mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-14 13:21:13 +00:00
More CRLF -> LF conversion.
This commit is contained in:
parent
c7cba8984d
commit
15d3160cf5
@ -1,53 +1,53 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/sdk/IOutput.h>
|
||||
#include "AlsaOut.h"
|
||||
|
||||
class AlsaOutPlugin : public musik::core::IPlugin {
|
||||
virtual void Destroy() { delete this; };
|
||||
virtual const char* Name() { return "AlsaOut IOutput plugin"; }
|
||||
virtual const char* Version() { return "0.2"; }
|
||||
virtual const char* Author() { return "Julian Cromarty, clangen"; }
|
||||
};
|
||||
|
||||
extern "C" musik::core::IPlugin* GetPlugin() {
|
||||
return new AlsaOutPlugin();
|
||||
}
|
||||
|
||||
extern "C" musik::core::audio::IOutput* GetAudioOutput() {
|
||||
return new AlsaOut();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/sdk/IOutput.h>
|
||||
#include "AlsaOut.h"
|
||||
|
||||
class AlsaOutPlugin : public musik::core::IPlugin {
|
||||
virtual void Destroy() { delete this; };
|
||||
virtual const char* Name() { return "AlsaOut IOutput plugin"; }
|
||||
virtual const char* Version() { return "0.2"; }
|
||||
virtual const char* Author() { return "Julian Cromarty, clangen"; }
|
||||
};
|
||||
|
||||
extern "C" musik::core::IPlugin* GetPlugin() {
|
||||
return new AlsaOutPlugin();
|
||||
}
|
||||
|
||||
extern "C" musik::core::audio::IOutput* GetAudioOutput() {
|
||||
return new AlsaOut();
|
||||
}
|
||||
|
@ -1,151 +1,151 @@
|
||||
#include "StdAfx.h"
|
||||
#include "APEdecoder.h"
|
||||
|
||||
|
||||
APEDecoder::APEDecoder(void)
|
||||
{
|
||||
}
|
||||
|
||||
APEDecoder::~APEDecoder(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool APEDecoder::Open(const utfchar* source)
|
||||
{
|
||||
int nRetVal;
|
||||
MACDecompressor = CreateIAPEDecompress(source, &nRetVal);
|
||||
if (nRetVal != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->sourcePath = source;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::Close()
|
||||
{
|
||||
delete MACDecompressor;
|
||||
return true;
|
||||
}
|
||||
|
||||
void APEDecoder::Destroy(void)
|
||||
{
|
||||
Close();
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool APEDecoder::GetFormat(unsigned long * SampleRate, unsigned long * Channels)
|
||||
{
|
||||
*SampleRate = (unsigned long)MACDecompressor->GetInfo(APE_INFO_SAMPLE_RATE);
|
||||
*Channels = (unsigned long)MACDecompressor->GetInfo(APE_INFO_CHANNELS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::GetLength(unsigned long * MS)
|
||||
{
|
||||
*MS = (unsigned long)MACDecompressor->GetInfo(APE_INFO_LENGTH_MS);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::SetPosition(unsigned long * MS)
|
||||
{
|
||||
int seekBlocks, totalBlocks, result;
|
||||
|
||||
seekBlocks = ((unsigned long)*MS/1000) * MACDecompressor->GetInfo(APE_INFO_SAMPLE_RATE);
|
||||
totalBlocks = MACDecompressor->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) - 1024;
|
||||
if (seekBlocks > totalBlocks) {
|
||||
seekBlocks = totalBlocks;
|
||||
}
|
||||
else if (seekBlocks <= 0) {
|
||||
seekBlocks = 0;
|
||||
}
|
||||
result = MACDecompressor->Seek(seekBlocks);
|
||||
if( result != ERROR_SUCCESS ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::SetState(unsigned long State)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::GetBuffer(float ** ppBuffer, unsigned long * NumSamples)
|
||||
{
|
||||
int result;
|
||||
int nChannels = MACDecompressor->GetInfo(APE_INFO_CHANNELS);
|
||||
int nBlockAlign = MACDecompressor->GetInfo(APE_INFO_BLOCK_ALIGN);
|
||||
int nBitsPerSample = MACDecompressor->GetInfo(APE_INFO_BITS_PER_SAMPLE);
|
||||
int nBytesPerSample = MACDecompressor->GetInfo(APE_INFO_BYTES_PER_SAMPLE);
|
||||
|
||||
static char * pRawData = NULL;
|
||||
|
||||
if(!pRawData)
|
||||
{
|
||||
pRawData = new char [1024 * nBlockAlign];
|
||||
}
|
||||
|
||||
int nBlocksRetrieved;
|
||||
result = MACDecompressor->GetData (pRawData, 1024, &nBlocksRetrieved);
|
||||
if((nBlocksRetrieved == 0) || (result != ERROR_SUCCESS))
|
||||
return false;
|
||||
|
||||
if(nBytesPerSample == 2) //16bit
|
||||
{
|
||||
short * pData = (short *)pRawData;
|
||||
float * pBuffer = m_Buffer;
|
||||
|
||||
for(int x=0; x<nBlocksRetrieved*nChannels; x++)
|
||||
{
|
||||
*pBuffer = (*pData) / 32767.0f;
|
||||
pData ++;
|
||||
pBuffer++;
|
||||
}
|
||||
}
|
||||
else if (nBytesPerSample == 3) //24bit
|
||||
{
|
||||
char * pData = (char *)pRawData;
|
||||
float *pBuffer = m_Buffer;
|
||||
int sourceIndex = 0;
|
||||
int padding = nBlockAlign - nChannels * (nBitsPerSample >> 3);
|
||||
int32 workingValue;
|
||||
|
||||
for ( unsigned int i = 0; i < 1024; i++ ) {
|
||||
for ( unsigned int j = 0; j < nChannels; j++ ) {
|
||||
workingValue = 0;
|
||||
workingValue |= ((int32)pRawData[sourceIndex++]) << 8 & 0xFF00;
|
||||
workingValue |= ((int32)pRawData[sourceIndex++]) << 16 & 0xFF0000;
|
||||
workingValue |= ((int32)pRawData[sourceIndex++]) << 24 & 0xFF000000;
|
||||
|
||||
*pBuffer = ((float)workingValue) * QUANTFACTOR;
|
||||
|
||||
pBuffer ++;
|
||||
pData += 3;
|
||||
}
|
||||
sourceIndex += padding;
|
||||
}
|
||||
}
|
||||
else if(nBytesPerSample == 4) //32bit?
|
||||
{
|
||||
int * pData = (int *)pRawData;
|
||||
float * pBuffer = m_Buffer;
|
||||
|
||||
float divider = (float)((1<<nBitsPerSample)-1);
|
||||
|
||||
for(int x=0; x<nBlocksRetrieved*nChannels;x++)
|
||||
{
|
||||
*pBuffer = (*pData) / divider;
|
||||
pData ++;
|
||||
pBuffer++;
|
||||
}
|
||||
}
|
||||
|
||||
*ppBuffer = m_Buffer;
|
||||
*NumSamples = nBlocksRetrieved * nChannels;
|
||||
return true;
|
||||
}
|
||||
#include "StdAfx.h"
|
||||
#include "APEdecoder.h"
|
||||
|
||||
|
||||
APEDecoder::APEDecoder(void)
|
||||
{
|
||||
}
|
||||
|
||||
APEDecoder::~APEDecoder(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool APEDecoder::Open(const utfchar* source)
|
||||
{
|
||||
int nRetVal;
|
||||
MACDecompressor = CreateIAPEDecompress(source, &nRetVal);
|
||||
if (nRetVal != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->sourcePath = source;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::Close()
|
||||
{
|
||||
delete MACDecompressor;
|
||||
return true;
|
||||
}
|
||||
|
||||
void APEDecoder::Destroy(void)
|
||||
{
|
||||
Close();
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool APEDecoder::GetFormat(unsigned long * SampleRate, unsigned long * Channels)
|
||||
{
|
||||
*SampleRate = (unsigned long)MACDecompressor->GetInfo(APE_INFO_SAMPLE_RATE);
|
||||
*Channels = (unsigned long)MACDecompressor->GetInfo(APE_INFO_CHANNELS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::GetLength(unsigned long * MS)
|
||||
{
|
||||
*MS = (unsigned long)MACDecompressor->GetInfo(APE_INFO_LENGTH_MS);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::SetPosition(unsigned long * MS)
|
||||
{
|
||||
int seekBlocks, totalBlocks, result;
|
||||
|
||||
seekBlocks = ((unsigned long)*MS/1000) * MACDecompressor->GetInfo(APE_INFO_SAMPLE_RATE);
|
||||
totalBlocks = MACDecompressor->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) - 1024;
|
||||
if (seekBlocks > totalBlocks) {
|
||||
seekBlocks = totalBlocks;
|
||||
}
|
||||
else if (seekBlocks <= 0) {
|
||||
seekBlocks = 0;
|
||||
}
|
||||
result = MACDecompressor->Seek(seekBlocks);
|
||||
if( result != ERROR_SUCCESS ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::SetState(unsigned long State)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APEDecoder::GetBuffer(float ** ppBuffer, unsigned long * NumSamples)
|
||||
{
|
||||
int result;
|
||||
int nChannels = MACDecompressor->GetInfo(APE_INFO_CHANNELS);
|
||||
int nBlockAlign = MACDecompressor->GetInfo(APE_INFO_BLOCK_ALIGN);
|
||||
int nBitsPerSample = MACDecompressor->GetInfo(APE_INFO_BITS_PER_SAMPLE);
|
||||
int nBytesPerSample = MACDecompressor->GetInfo(APE_INFO_BYTES_PER_SAMPLE);
|
||||
|
||||
static char * pRawData = NULL;
|
||||
|
||||
if(!pRawData)
|
||||
{
|
||||
pRawData = new char [1024 * nBlockAlign];
|
||||
}
|
||||
|
||||
int nBlocksRetrieved;
|
||||
result = MACDecompressor->GetData (pRawData, 1024, &nBlocksRetrieved);
|
||||
if((nBlocksRetrieved == 0) || (result != ERROR_SUCCESS))
|
||||
return false;
|
||||
|
||||
if(nBytesPerSample == 2) //16bit
|
||||
{
|
||||
short * pData = (short *)pRawData;
|
||||
float * pBuffer = m_Buffer;
|
||||
|
||||
for(int x=0; x<nBlocksRetrieved*nChannels; x++)
|
||||
{
|
||||
*pBuffer = (*pData) / 32767.0f;
|
||||
pData ++;
|
||||
pBuffer++;
|
||||
}
|
||||
}
|
||||
else if (nBytesPerSample == 3) //24bit
|
||||
{
|
||||
char * pData = (char *)pRawData;
|
||||
float *pBuffer = m_Buffer;
|
||||
int sourceIndex = 0;
|
||||
int padding = nBlockAlign - nChannels * (nBitsPerSample >> 3);
|
||||
int32 workingValue;
|
||||
|
||||
for ( unsigned int i = 0; i < 1024; i++ ) {
|
||||
for ( unsigned int j = 0; j < nChannels; j++ ) {
|
||||
workingValue = 0;
|
||||
workingValue |= ((int32)pRawData[sourceIndex++]) << 8 & 0xFF00;
|
||||
workingValue |= ((int32)pRawData[sourceIndex++]) << 16 & 0xFF0000;
|
||||
workingValue |= ((int32)pRawData[sourceIndex++]) << 24 & 0xFF000000;
|
||||
|
||||
*pBuffer = ((float)workingValue) * QUANTFACTOR;
|
||||
|
||||
pBuffer ++;
|
||||
pData += 3;
|
||||
}
|
||||
sourceIndex += padding;
|
||||
}
|
||||
}
|
||||
else if(nBytesPerSample == 4) //32bit?
|
||||
{
|
||||
int * pData = (int *)pRawData;
|
||||
float * pBuffer = m_Buffer;
|
||||
|
||||
float divider = (float)((1<<nBitsPerSample)-1);
|
||||
|
||||
for(int x=0; x<nBlocksRetrieved*nChannels;x++)
|
||||
{
|
||||
*pBuffer = (*pData) / divider;
|
||||
pData ++;
|
||||
pBuffer++;
|
||||
}
|
||||
}
|
||||
|
||||
*ppBuffer = m_Buffer;
|
||||
*NumSamples = nBlocksRetrieved * nChannels;
|
||||
return true;
|
||||
}
|
||||
|
@ -1,40 +1,40 @@
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "boost/filesystem.hpp"
|
||||
|
||||
#include "APESourceSupplier.h"
|
||||
|
||||
#include "APEDecoder.h"
|
||||
|
||||
APESourceSupplier::APESourceSupplier(void)
|
||||
{
|
||||
}
|
||||
|
||||
APESourceSupplier::~APESourceSupplier(void)
|
||||
{
|
||||
}
|
||||
|
||||
void APESourceSupplier::Destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
IAudioSource* APESourceSupplier::CreateAudioSource()
|
||||
{
|
||||
return new APEDecoder();
|
||||
}
|
||||
|
||||
bool APESourceSupplier::CanHandle(const utfchar* 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(".ape"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "boost/filesystem.hpp"
|
||||
|
||||
#include "APESourceSupplier.h"
|
||||
|
||||
#include "APEDecoder.h"
|
||||
|
||||
APESourceSupplier::APESourceSupplier(void)
|
||||
{
|
||||
}
|
||||
|
||||
APESourceSupplier::~APESourceSupplier(void)
|
||||
{
|
||||
}
|
||||
|
||||
void APESourceSupplier::Destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
IAudioSource* APESourceSupplier::CreateAudioSource()
|
||||
{
|
||||
return new APEDecoder();
|
||||
}
|
||||
|
||||
bool APESourceSupplier::CanHandle(const utfchar* 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(".ape"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1,64 +1,64 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Björn Olievier
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "APESourceSupplier.h"
|
||||
#include <core/IPlugin.h>
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
class APEDecoderPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; };
|
||||
|
||||
const utfchar* Name() { return TEXT("APE decoder"); };
|
||||
const utfchar* Version() { return TEXT("1"); };
|
||||
const utfchar* Author() { return TEXT("Björn Olievier"); };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin()
|
||||
{
|
||||
return new APEDecoderPlugin();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) IAudioSourceSupplier* CreateAudioSourceSupplier()
|
||||
{
|
||||
return new APESourceSupplier();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Björn Olievier
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "APESourceSupplier.h"
|
||||
#include <core/IPlugin.h>
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
class APEDecoderPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; };
|
||||
|
||||
const utfchar* Name() { return TEXT("APE decoder"); };
|
||||
const utfchar* Version() { return TEXT("1"); };
|
||||
const utfchar* Author() { return TEXT("Björn Olievier"); };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin()
|
||||
{
|
||||
return new APEDecoderPlugin();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) IAudioSourceSupplier* CreateAudioSourceSupplier()
|
||||
{
|
||||
return new APESourceSupplier();
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// ID3InfoManager.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// ID3InfoManager.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
|
@ -1,107 +1,107 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2007, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "pch.h"
|
||||
#include "BPMAnalyzer.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <core/config_format.h>
|
||||
|
||||
BPMAnalyzer::BPMAnalyzer()
|
||||
:detector(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BPMAnalyzer::~BPMAnalyzer(){
|
||||
delete this->detector;
|
||||
}
|
||||
|
||||
|
||||
void BPMAnalyzer::Destroy(){
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool BPMAnalyzer::Start(musik::core::ITrack *track){
|
||||
const utfchar *nobpmchar = track->GetValue("nobpm");
|
||||
if(nobpmchar){
|
||||
utfstring nobpmstring(nobpmchar);
|
||||
if(nobpmstring==UTF("true")){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const utfchar *bpmchar = track->GetValue("bpm");
|
||||
if(bpmchar){
|
||||
utfstring bpmstring(bpmchar);
|
||||
if(!bpmstring.empty()){
|
||||
try{
|
||||
double bpm = boost::lexical_cast<double>(bpmstring);
|
||||
return false;
|
||||
}
|
||||
catch(...){
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPMAnalyzer::Analyze(musik::core::ITrack *track,IBuffer *buffer){
|
||||
if(buffer->Channels()>2){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!this->detector){
|
||||
this->detector = new BPMDetect(buffer->Channels(),buffer->SampleRate());
|
||||
}
|
||||
|
||||
this->detector->inputSamples(buffer->BufferPointer(),buffer->Samples());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPMAnalyzer::End(musik::core::ITrack *track){
|
||||
if(this->detector){
|
||||
float bpm = this->detector->getBpm();
|
||||
try{
|
||||
if(bpm==0.0f){
|
||||
track->SetValue("nobpm",UTF("true"));
|
||||
}else{
|
||||
utfstring bpmString = boost::str( boost::utfformat(UTF("%.2f"))%bpm);
|
||||
track->SetValue("bpm",bpmString.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch(...){
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2007, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "pch.h"
|
||||
#include "BPMAnalyzer.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <core/config_format.h>
|
||||
|
||||
BPMAnalyzer::BPMAnalyzer()
|
||||
:detector(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BPMAnalyzer::~BPMAnalyzer(){
|
||||
delete this->detector;
|
||||
}
|
||||
|
||||
|
||||
void BPMAnalyzer::Destroy(){
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool BPMAnalyzer::Start(musik::core::ITrack *track){
|
||||
const utfchar *nobpmchar = track->GetValue("nobpm");
|
||||
if(nobpmchar){
|
||||
utfstring nobpmstring(nobpmchar);
|
||||
if(nobpmstring==UTF("true")){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const utfchar *bpmchar = track->GetValue("bpm");
|
||||
if(bpmchar){
|
||||
utfstring bpmstring(bpmchar);
|
||||
if(!bpmstring.empty()){
|
||||
try{
|
||||
double bpm = boost::lexical_cast<double>(bpmstring);
|
||||
return false;
|
||||
}
|
||||
catch(...){
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPMAnalyzer::Analyze(musik::core::ITrack *track,IBuffer *buffer){
|
||||
if(buffer->Channels()>2){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!this->detector){
|
||||
this->detector = new BPMDetect(buffer->Channels(),buffer->SampleRate());
|
||||
}
|
||||
|
||||
this->detector->inputSamples(buffer->BufferPointer(),buffer->Samples());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPMAnalyzer::End(musik::core::ITrack *track){
|
||||
if(this->detector){
|
||||
float bpm = this->detector->getBpm();
|
||||
try{
|
||||
if(bpm==0.0f){
|
||||
track->SetValue("nobpm",UTF("true"));
|
||||
}else{
|
||||
utfstring bpmString = boost::str( boost::utfformat(UTF("%.2f"))%bpm);
|
||||
track->SetValue("bpm",bpmString.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch(...){
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,67 +1,67 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <core/IPlugin.h>
|
||||
#include <core/audio/IAnalyzer.h>
|
||||
#include "BPMAnalyzer.h"
|
||||
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
class BPMAnalyzerPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; };
|
||||
|
||||
const utfchar* Name() { return TEXT("BPM Analyzer"); };
|
||||
const utfchar* Version() { return TEXT("1"); };
|
||||
const utfchar* Author() { return TEXT("Daniel Önnerby"); };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin()
|
||||
{
|
||||
return new BPMAnalyzerPlugin();
|
||||
}
|
||||
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::audio::IAnalyzer* GetAudioAnalyzer()
|
||||
{
|
||||
return new BPMAnalyzer();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <core/IPlugin.h>
|
||||
#include <core/audio/IAnalyzer.h>
|
||||
#include "BPMAnalyzer.h"
|
||||
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
class BPMAnalyzerPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; };
|
||||
|
||||
const utfchar* Name() { return TEXT("BPM Analyzer"); };
|
||||
const utfchar* Version() { return TEXT("1"); };
|
||||
const utfchar* Author() { return TEXT("Daniel Önnerby"); };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin()
|
||||
{
|
||||
return new BPMAnalyzerPlugin();
|
||||
}
|
||||
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::audio::IAnalyzer* GetAudioAnalyzer()
|
||||
{
|
||||
return new BPMAnalyzer();
|
||||
}
|
||||
|
@ -1,37 +1,37 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
@ -1,350 +1,350 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the AMD 3DNow! optimized routines for AMD K6-2/Athlon
|
||||
/// processors. All 3DNow! optimized functions have been gathered into this
|
||||
/// single source code file, regardless to their class or original source code
|
||||
/// file, in order to ease porting the library to other compiler and processor
|
||||
/// platforms.
|
||||
///
|
||||
/// By the way; the performance gain depends heavily on the CPU generation: On
|
||||
/// K6-2 these routines provided speed-up of even 2.4 times, while on Athlon the
|
||||
/// difference to the original routines stayed at unremarkable 8%! Such a small
|
||||
/// improvement on Athlon is due to 3DNow can perform only two operations in
|
||||
/// parallel, and obviously also the Athlon FPU is doing a very good job with
|
||||
/// the standard C floating point routines! Here these routines are anyway,
|
||||
/// although it might not be worth the effort to convert these to GCC platform,
|
||||
/// for Athlon CPU at least. The situation is different regarding the SSE
|
||||
/// optimizations though, thanks to the four parallel operations of SSE that
|
||||
/// already make a difference.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see '3dnow_gcc.cpp' for the gcc compiler version for all
|
||||
/// GNU platforms (if file supplied).
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support 3DNow! instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
//
|
||||
// $Id: 3dnow_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error "wrong platform - this source code file is exclusively for Win32 platform"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
// 3DNow! routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of 3DNow! optimized functions of class 'TDStretch3DNow'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <limits.h>
|
||||
|
||||
// these are declared in 'TDStretch.cpp'
|
||||
extern int scanOffsets[4][24];
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
/*
|
||||
c-pseudocode:
|
||||
|
||||
corr = 0;
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0];
|
||||
pV1[1] * pV2[1];
|
||||
pV1[2] * pV2[2];
|
||||
pV1[3] * pV2[3];
|
||||
pV1[4] * pV2[4];
|
||||
pV1[5] * pV2[5];
|
||||
pV1[6] * pV2[6];
|
||||
pV1[7] * pV2[7];
|
||||
|
||||
pV1 += 8;
|
||||
pV2 += 8;
|
||||
}
|
||||
*/
|
||||
|
||||
_asm
|
||||
{
|
||||
// give prefetch hints to CPU of what data are to be needed soonish.
|
||||
// give more aggressive hints on pV1 as that changes more between different calls
|
||||
// while pV2 stays the same.
|
||||
prefetch [pV1]
|
||||
prefetch [pV2]
|
||||
prefetch [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV2
|
||||
mov ebx, dword ptr pV1
|
||||
|
||||
pxor mm0, mm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 2 // div by four
|
||||
|
||||
loop1:
|
||||
movq mm1, [eax]
|
||||
prefetch [eax + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm1, [ebx]
|
||||
prefetch [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movq mm2, [eax + 8]
|
||||
pfadd mm0, mm1
|
||||
pfmul mm2, [ebx + 8]
|
||||
|
||||
movq mm3, [eax + 16]
|
||||
pfadd mm0, mm2
|
||||
pfmul mm3, [ebx + 16]
|
||||
|
||||
movq mm4, [eax + 24]
|
||||
pfadd mm0, mm3
|
||||
pfmul mm4, [ebx + 24]
|
||||
|
||||
add eax, 32
|
||||
pfadd mm0, mm4
|
||||
add ebx, 32
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add halfs of mm0 together and return the result.
|
||||
// note: mm1 is used as a dummy parameter only, we actually don't care about it's value
|
||||
pfacc mm0, mm1
|
||||
movd corr, mm0
|
||||
femms
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of 3DNow! optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilter3DNow::FIRFilter3DNow() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter3DNow::~FIRFilter3DNow()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for 3DNow! routine
|
||||
void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
float fDivider;
|
||||
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3DNow!-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const
|
||||
{
|
||||
float *filterCoeffsLocal = filterCoeffsAlign;
|
||||
uint count = (numSamples - length) & -2;
|
||||
uint lengthLocal = length / 4;
|
||||
|
||||
assert(length != 0);
|
||||
assert(count % 2 == 0);
|
||||
|
||||
/* original code:
|
||||
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
uint i, j;
|
||||
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *ptr;
|
||||
|
||||
suml1 = sumr1 = 0.0;
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
filterCoeffsLocal = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * filterCoeffsLocal[0] +
|
||||
ptr[2] * filterCoeffsLocal[2] +
|
||||
ptr[4] * filterCoeffsLocal[4] +
|
||||
ptr[6] * filterCoeffsLocal[6];
|
||||
|
||||
sumr1 += ptr[1] * filterCoeffsLocal[1] +
|
||||
ptr[3] * filterCoeffsLocal[3] +
|
||||
ptr[5] * filterCoeffsLocal[5] +
|
||||
ptr[7] * filterCoeffsLocal[7];
|
||||
|
||||
suml2 += ptr[8] * filterCoeffsLocal[0] +
|
||||
ptr[10] * filterCoeffsLocal[2] +
|
||||
ptr[12] * filterCoeffsLocal[4] +
|
||||
ptr[14] * filterCoeffsLocal[6];
|
||||
|
||||
sumr2 += ptr[9] * filterCoeffsLocal[1] +
|
||||
ptr[11] * filterCoeffsLocal[3] +
|
||||
ptr[13] * filterCoeffsLocal[5] +
|
||||
ptr[15] * filterCoeffsLocal[7];
|
||||
|
||||
ptr += 16;
|
||||
filterCoeffsLocal += 8;
|
||||
}
|
||||
dest[0] = (float)suml1;
|
||||
dest[1] = (float)sumr1;
|
||||
dest[2] = (float)suml2;
|
||||
dest[3] = (float)sumr2;
|
||||
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
|
||||
*/
|
||||
_asm
|
||||
{
|
||||
mov eax, dword ptr dest
|
||||
mov ebx, dword ptr src
|
||||
mov edx, count
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
prefetch [ebx] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetch [filterCoeffsLocal] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
pxor mm0, mm0
|
||||
pxor mm1, mm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round four FIR filter taps are evaluated for 2*2 output samples
|
||||
movq mm2, [edi]
|
||||
movq mm3, mm2
|
||||
prefetch [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm2, [esi]
|
||||
prefetch [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm3, [esi + 8]
|
||||
|
||||
movq mm4, [edi + 8]
|
||||
movq mm5, mm4
|
||||
pfadd mm0, mm2
|
||||
pfmul mm4, [esi + 8]
|
||||
pfadd mm1, mm3
|
||||
pfmul mm5, [esi + 16]
|
||||
|
||||
movq mm2, [edi + 16]
|
||||
movq mm6, mm2
|
||||
pfadd mm0, mm4
|
||||
pfmul mm2, [esi + 16]
|
||||
pfadd mm1, mm5
|
||||
pfmul mm6, [esi + 24]
|
||||
|
||||
movq mm3, [edi + 24]
|
||||
movq mm7, mm3
|
||||
pfadd mm0, mm2
|
||||
pfmul mm3, [esi + 24]
|
||||
pfadd mm1, mm6
|
||||
pfmul mm7, [esi + 32]
|
||||
add esi, 32
|
||||
pfadd mm0, mm3
|
||||
add edi, 32
|
||||
pfadd mm1, mm7
|
||||
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
movq [eax], mm0
|
||||
add ebx, 16
|
||||
movq [eax + 8], mm1
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
|
||||
femms
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#endif // ALLOW_3DNOW
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the AMD 3DNow! optimized routines for AMD K6-2/Athlon
|
||||
/// processors. All 3DNow! optimized functions have been gathered into this
|
||||
/// single source code file, regardless to their class or original source code
|
||||
/// file, in order to ease porting the library to other compiler and processor
|
||||
/// platforms.
|
||||
///
|
||||
/// By the way; the performance gain depends heavily on the CPU generation: On
|
||||
/// K6-2 these routines provided speed-up of even 2.4 times, while on Athlon the
|
||||
/// difference to the original routines stayed at unremarkable 8%! Such a small
|
||||
/// improvement on Athlon is due to 3DNow can perform only two operations in
|
||||
/// parallel, and obviously also the Athlon FPU is doing a very good job with
|
||||
/// the standard C floating point routines! Here these routines are anyway,
|
||||
/// although it might not be worth the effort to convert these to GCC platform,
|
||||
/// for Athlon CPU at least. The situation is different regarding the SSE
|
||||
/// optimizations though, thanks to the four parallel operations of SSE that
|
||||
/// already make a difference.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see '3dnow_gcc.cpp' for the gcc compiler version for all
|
||||
/// GNU platforms (if file supplied).
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support 3DNow! instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
//
|
||||
// $Id: 3dnow_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error "wrong platform - this source code file is exclusively for Win32 platform"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
// 3DNow! routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of 3DNow! optimized functions of class 'TDStretch3DNow'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <limits.h>
|
||||
|
||||
// these are declared in 'TDStretch.cpp'
|
||||
extern int scanOffsets[4][24];
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
/*
|
||||
c-pseudocode:
|
||||
|
||||
corr = 0;
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0];
|
||||
pV1[1] * pV2[1];
|
||||
pV1[2] * pV2[2];
|
||||
pV1[3] * pV2[3];
|
||||
pV1[4] * pV2[4];
|
||||
pV1[5] * pV2[5];
|
||||
pV1[6] * pV2[6];
|
||||
pV1[7] * pV2[7];
|
||||
|
||||
pV1 += 8;
|
||||
pV2 += 8;
|
||||
}
|
||||
*/
|
||||
|
||||
_asm
|
||||
{
|
||||
// give prefetch hints to CPU of what data are to be needed soonish.
|
||||
// give more aggressive hints on pV1 as that changes more between different calls
|
||||
// while pV2 stays the same.
|
||||
prefetch [pV1]
|
||||
prefetch [pV2]
|
||||
prefetch [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV2
|
||||
mov ebx, dword ptr pV1
|
||||
|
||||
pxor mm0, mm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 2 // div by four
|
||||
|
||||
loop1:
|
||||
movq mm1, [eax]
|
||||
prefetch [eax + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm1, [ebx]
|
||||
prefetch [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movq mm2, [eax + 8]
|
||||
pfadd mm0, mm1
|
||||
pfmul mm2, [ebx + 8]
|
||||
|
||||
movq mm3, [eax + 16]
|
||||
pfadd mm0, mm2
|
||||
pfmul mm3, [ebx + 16]
|
||||
|
||||
movq mm4, [eax + 24]
|
||||
pfadd mm0, mm3
|
||||
pfmul mm4, [ebx + 24]
|
||||
|
||||
add eax, 32
|
||||
pfadd mm0, mm4
|
||||
add ebx, 32
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add halfs of mm0 together and return the result.
|
||||
// note: mm1 is used as a dummy parameter only, we actually don't care about it's value
|
||||
pfacc mm0, mm1
|
||||
movd corr, mm0
|
||||
femms
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of 3DNow! optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilter3DNow::FIRFilter3DNow() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter3DNow::~FIRFilter3DNow()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for 3DNow! routine
|
||||
void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
float fDivider;
|
||||
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3DNow!-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const
|
||||
{
|
||||
float *filterCoeffsLocal = filterCoeffsAlign;
|
||||
uint count = (numSamples - length) & -2;
|
||||
uint lengthLocal = length / 4;
|
||||
|
||||
assert(length != 0);
|
||||
assert(count % 2 == 0);
|
||||
|
||||
/* original code:
|
||||
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
uint i, j;
|
||||
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *ptr;
|
||||
|
||||
suml1 = sumr1 = 0.0;
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
filterCoeffsLocal = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * filterCoeffsLocal[0] +
|
||||
ptr[2] * filterCoeffsLocal[2] +
|
||||
ptr[4] * filterCoeffsLocal[4] +
|
||||
ptr[6] * filterCoeffsLocal[6];
|
||||
|
||||
sumr1 += ptr[1] * filterCoeffsLocal[1] +
|
||||
ptr[3] * filterCoeffsLocal[3] +
|
||||
ptr[5] * filterCoeffsLocal[5] +
|
||||
ptr[7] * filterCoeffsLocal[7];
|
||||
|
||||
suml2 += ptr[8] * filterCoeffsLocal[0] +
|
||||
ptr[10] * filterCoeffsLocal[2] +
|
||||
ptr[12] * filterCoeffsLocal[4] +
|
||||
ptr[14] * filterCoeffsLocal[6];
|
||||
|
||||
sumr2 += ptr[9] * filterCoeffsLocal[1] +
|
||||
ptr[11] * filterCoeffsLocal[3] +
|
||||
ptr[13] * filterCoeffsLocal[5] +
|
||||
ptr[15] * filterCoeffsLocal[7];
|
||||
|
||||
ptr += 16;
|
||||
filterCoeffsLocal += 8;
|
||||
}
|
||||
dest[0] = (float)suml1;
|
||||
dest[1] = (float)sumr1;
|
||||
dest[2] = (float)suml2;
|
||||
dest[3] = (float)sumr2;
|
||||
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
|
||||
*/
|
||||
_asm
|
||||
{
|
||||
mov eax, dword ptr dest
|
||||
mov ebx, dword ptr src
|
||||
mov edx, count
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
prefetch [ebx] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetch [filterCoeffsLocal] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
pxor mm0, mm0
|
||||
pxor mm1, mm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round four FIR filter taps are evaluated for 2*2 output samples
|
||||
movq mm2, [edi]
|
||||
movq mm3, mm2
|
||||
prefetch [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm2, [esi]
|
||||
prefetch [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm3, [esi + 8]
|
||||
|
||||
movq mm4, [edi + 8]
|
||||
movq mm5, mm4
|
||||
pfadd mm0, mm2
|
||||
pfmul mm4, [esi + 8]
|
||||
pfadd mm1, mm3
|
||||
pfmul mm5, [esi + 16]
|
||||
|
||||
movq mm2, [edi + 16]
|
||||
movq mm6, mm2
|
||||
pfadd mm0, mm4
|
||||
pfmul mm2, [esi + 16]
|
||||
pfadd mm1, mm5
|
||||
pfmul mm6, [esi + 24]
|
||||
|
||||
movq mm3, [edi + 24]
|
||||
movq mm7, mm3
|
||||
pfadd mm0, mm2
|
||||
pfmul mm3, [esi + 24]
|
||||
pfadd mm1, mm6
|
||||
pfmul mm7, [esi + 32]
|
||||
add esi, 32
|
||||
pfadd mm0, mm3
|
||||
add edi, 32
|
||||
pfadd mm1, mm7
|
||||
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
movq [eax], mm0
|
||||
add ebx, 16
|
||||
movq [eax + 8], mm1
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
|
||||
femms
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#endif // ALLOW_3DNOW
|
||||
|
@ -1,184 +1,184 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
|
||||
/// MMX optimization.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.9 $
|
||||
//
|
||||
// $Id: AAFilter.cpp,v 1.9 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "AAFilter.h"
|
||||
#include "FIRFilter.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define PI 3.141592655357989
|
||||
#define TWOPI (2 * PI)
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'AAFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
AAFilter::AAFilter(const uint length)
|
||||
{
|
||||
pFIR = FIRFilter::newInstance();
|
||||
cutoffFreq = 0.5;
|
||||
setLength(length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AAFilter::~AAFilter()
|
||||
{
|
||||
delete pFIR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||
// sampling frequency (nyquist frequency = 0.5).
|
||||
// The filter will cut frequencies higher than the given frequency.
|
||||
void AAFilter::setCutoffFreq(const double newCutoffFreq)
|
||||
{
|
||||
cutoffFreq = newCutoffFreq;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets number of FIR filter taps
|
||||
void AAFilter::setLength(const uint newLength)
|
||||
{
|
||||
length = newLength;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
||||
void AAFilter::calculateCoeffs()
|
||||
{
|
||||
uint i;
|
||||
double cntTemp, temp, tempCoeff,h, w;
|
||||
double fc2, wc;
|
||||
double scaleCoeff, sum;
|
||||
double *work;
|
||||
SAMPLETYPE *coeffs;
|
||||
|
||||
assert(length > 0);
|
||||
assert(length % 4 == 0);
|
||||
assert(cutoffFreq >= 0);
|
||||
assert(cutoffFreq <= 0.5);
|
||||
|
||||
work = new double[length];
|
||||
coeffs = new SAMPLETYPE[length];
|
||||
|
||||
fc2 = 2.0 * cutoffFreq;
|
||||
wc = PI * fc2;
|
||||
tempCoeff = TWOPI / (double)length;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
cntTemp = (double)i - (double)(length / 2);
|
||||
|
||||
temp = cntTemp * wc;
|
||||
if (temp != 0)
|
||||
{
|
||||
h = fc2 * sin(temp) / temp; // sinc function
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 1.0;
|
||||
}
|
||||
w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
|
||||
|
||||
temp = w * h;
|
||||
work[i] = temp;
|
||||
|
||||
// calc net sum of coefficients
|
||||
sum += temp;
|
||||
}
|
||||
|
||||
// ensure the sum of coefficients is larger than zero
|
||||
assert(sum > 0);
|
||||
|
||||
// ensure we've really designed a lowpass filter...
|
||||
assert(work[length/2] > 0);
|
||||
assert(work[length/2 + 1] > -1e-6);
|
||||
assert(work[length/2 - 1] > -1e-6);
|
||||
|
||||
// Calculate a scaling coefficient in such a way that the result can be
|
||||
// divided by 16384
|
||||
scaleCoeff = 16384.0f / sum;
|
||||
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
// scale & round to nearest integer
|
||||
temp = work[i] * scaleCoeff;
|
||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||
// ensure no overfloods
|
||||
assert(temp >= -32768 && temp <= 32767);
|
||||
coeffs[i] = (SAMPLETYPE)temp;
|
||||
}
|
||||
|
||||
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
|
||||
pFIR->setCoefficients(coeffs, length, 14);
|
||||
|
||||
delete[] work;
|
||||
delete[] coeffs;
|
||||
}
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
// Note : The amount of outputted samples is by value of 'filter length'
|
||||
// smaller than the amount of input samples.
|
||||
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
return pFIR->evaluate(dest, src, numSamples, numChannels);
|
||||
}
|
||||
|
||||
|
||||
uint AAFilter::getLength() const
|
||||
{
|
||||
return pFIR->getLength();
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
|
||||
/// MMX optimization.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.9 $
|
||||
//
|
||||
// $Id: AAFilter.cpp,v 1.9 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "AAFilter.h"
|
||||
#include "FIRFilter.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define PI 3.141592655357989
|
||||
#define TWOPI (2 * PI)
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'AAFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
AAFilter::AAFilter(const uint length)
|
||||
{
|
||||
pFIR = FIRFilter::newInstance();
|
||||
cutoffFreq = 0.5;
|
||||
setLength(length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AAFilter::~AAFilter()
|
||||
{
|
||||
delete pFIR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||
// sampling frequency (nyquist frequency = 0.5).
|
||||
// The filter will cut frequencies higher than the given frequency.
|
||||
void AAFilter::setCutoffFreq(const double newCutoffFreq)
|
||||
{
|
||||
cutoffFreq = newCutoffFreq;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets number of FIR filter taps
|
||||
void AAFilter::setLength(const uint newLength)
|
||||
{
|
||||
length = newLength;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
||||
void AAFilter::calculateCoeffs()
|
||||
{
|
||||
uint i;
|
||||
double cntTemp, temp, tempCoeff,h, w;
|
||||
double fc2, wc;
|
||||
double scaleCoeff, sum;
|
||||
double *work;
|
||||
SAMPLETYPE *coeffs;
|
||||
|
||||
assert(length > 0);
|
||||
assert(length % 4 == 0);
|
||||
assert(cutoffFreq >= 0);
|
||||
assert(cutoffFreq <= 0.5);
|
||||
|
||||
work = new double[length];
|
||||
coeffs = new SAMPLETYPE[length];
|
||||
|
||||
fc2 = 2.0 * cutoffFreq;
|
||||
wc = PI * fc2;
|
||||
tempCoeff = TWOPI / (double)length;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
cntTemp = (double)i - (double)(length / 2);
|
||||
|
||||
temp = cntTemp * wc;
|
||||
if (temp != 0)
|
||||
{
|
||||
h = fc2 * sin(temp) / temp; // sinc function
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 1.0;
|
||||
}
|
||||
w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
|
||||
|
||||
temp = w * h;
|
||||
work[i] = temp;
|
||||
|
||||
// calc net sum of coefficients
|
||||
sum += temp;
|
||||
}
|
||||
|
||||
// ensure the sum of coefficients is larger than zero
|
||||
assert(sum > 0);
|
||||
|
||||
// ensure we've really designed a lowpass filter...
|
||||
assert(work[length/2] > 0);
|
||||
assert(work[length/2 + 1] > -1e-6);
|
||||
assert(work[length/2 - 1] > -1e-6);
|
||||
|
||||
// Calculate a scaling coefficient in such a way that the result can be
|
||||
// divided by 16384
|
||||
scaleCoeff = 16384.0f / sum;
|
||||
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
// scale & round to nearest integer
|
||||
temp = work[i] * scaleCoeff;
|
||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||
// ensure no overfloods
|
||||
assert(temp >= -32768 && temp <= 32767);
|
||||
coeffs[i] = (SAMPLETYPE)temp;
|
||||
}
|
||||
|
||||
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
|
||||
pFIR->setCoefficients(coeffs, length, 14);
|
||||
|
||||
delete[] work;
|
||||
delete[] coeffs;
|
||||
}
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
// Note : The amount of outputted samples is by value of 'filter length'
|
||||
// smaller than the amount of input samples.
|
||||
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
return pFIR->evaluate(dest, src, numSamples, numChannels);
|
||||
}
|
||||
|
||||
|
||||
uint AAFilter::getLength() const
|
||||
{
|
||||
return pFIR->getLength();
|
||||
}
|
||||
|
@ -1,252 +1,252 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// first-in-first-out pipe.
|
||||
///
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// function, and are received from the beginning of the buffer by calling
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// outputted samples from the buffer, as well as grows the buffer size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.11 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.cpp,v 1.11 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
// Constructor
|
||||
FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels)
|
||||
{
|
||||
sizeInBytes = 0; // reasonable initial value
|
||||
buffer = NULL; //new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE)];
|
||||
bufferUnaligned = NULL;
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
channels = numChannels;
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||
{
|
||||
delete[] bufferUnaligned;
|
||||
}
|
||||
|
||||
|
||||
// Sets number of channels, 1 = mono, 2 = stereo
|
||||
void FIFOSampleBuffer::setChannels(const uint numChannels)
|
||||
{
|
||||
uint usedBytes;
|
||||
|
||||
usedBytes = channels * samplesInBuffer;
|
||||
channels = numChannels;
|
||||
samplesInBuffer = usedBytes / channels;
|
||||
}
|
||||
|
||||
|
||||
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
||||
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
||||
// location on to the beginning of the buffer.
|
||||
void FIFOSampleBuffer::rewind()
|
||||
{
|
||||
if (bufferPos)
|
||||
{
|
||||
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
||||
bufferPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
// the sample buffer.
|
||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
memcpy(ptrEnd(numSamples), samples, sizeof(SAMPLETYPE) * numSamples * channels);
|
||||
samplesInBuffer += numSamples;
|
||||
}
|
||||
|
||||
|
||||
// Increases the number of samples in the buffer without copying any actual
|
||||
// samples.
|
||||
//
|
||||
// This function is used to update the number of samples in the sample buffer
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// careful though!
|
||||
void FIFOSampleBuffer::putSamples(uint numSamples)
|
||||
{
|
||||
uint req;
|
||||
|
||||
req = samplesInBuffer + numSamples;
|
||||
ensureCapacity(req);
|
||||
samplesInBuffer += numSamples;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
// where the new samples are to be inserted). This function may be used for
|
||||
// inserting new samples into the sample buffer directly. Please be careful!
|
||||
//
|
||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||
// terms of samples) there _at least_ should be, in order to the caller to
|
||||
// succesfully insert all the required samples to the buffer. When necessary,
|
||||
// the function grows the buffer size to comply with this requirement.
|
||||
//
|
||||
// When using this function as means for inserting new samples, also remember
|
||||
// to increase the sample count afterwards, by calling the
|
||||
// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||
{
|
||||
ensureCapacity(samplesInBuffer + slackCapacity);
|
||||
return buffer + samplesInBuffer * channels;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||
// This function is provided for accessing the output samples directly.
|
||||
// Please be careful!
|
||||
//
|
||||
// When using this function to output samples, also remember to 'remove' the
|
||||
// outputted samples from the buffer by calling the
|
||||
// 'receiveSamples(numSamples)' function
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin() const
|
||||
{
|
||||
return buffer + bufferPos * channels;
|
||||
}
|
||||
|
||||
|
||||
// Ensures that the buffer has enought capacity, i.e. space for _at least_
|
||||
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
||||
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
||||
// as well as to round the buffer size up to the virtual memory page size.
|
||||
void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||
{
|
||||
SAMPLETYPE *tempUnaligned, *temp;
|
||||
|
||||
if (capacityRequirement > getCapacity())
|
||||
{
|
||||
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & -4096;
|
||||
assert(sizeInBytes % 2 == 0);
|
||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||
if (tempUnaligned == NULL)
|
||||
{
|
||||
throw std::runtime_error("Couldn't allocate memory!\n");
|
||||
}
|
||||
temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & -16);
|
||||
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
||||
delete[] bufferUnaligned;
|
||||
buffer = temp;
|
||||
bufferUnaligned = tempUnaligned;
|
||||
bufferPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply rewind the buffer (if necessary)
|
||||
rewind();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the current buffer capacity in terms of samples
|
||||
uint FIFOSampleBuffer::getCapacity() const
|
||||
{
|
||||
return sizeInBytes / (channels * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of samples currently in the buffer
|
||||
uint FIFOSampleBuffer::numSamples() const
|
||||
{
|
||||
return samplesInBuffer;
|
||||
}
|
||||
|
||||
|
||||
// Output samples from beginning of the sample buffer. Copies demanded number
|
||||
// of samples to output and removes them from the sample buffer. If there
|
||||
// are less than 'numsample' samples in the buffer, returns all available.
|
||||
//
|
||||
// Returns number of samples copied.
|
||||
uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||
{
|
||||
uint num;
|
||||
|
||||
num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples;
|
||||
|
||||
memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num);
|
||||
return receiveSamples(num);
|
||||
}
|
||||
|
||||
|
||||
// Removes samples from the beginning of the sample buffer without copying them
|
||||
// anywhere. Used to reduce the number of samples in the buffer, when accessing
|
||||
// the sample buffer with the 'ptrBegin' function.
|
||||
uint FIFOSampleBuffer::receiveSamples(uint maxSamples)
|
||||
{
|
||||
if (maxSamples >= samplesInBuffer)
|
||||
{
|
||||
uint temp;
|
||||
|
||||
temp = samplesInBuffer;
|
||||
samplesInBuffer = 0;
|
||||
return temp;
|
||||
}
|
||||
|
||||
samplesInBuffer -= maxSamples;
|
||||
bufferPos += maxSamples;
|
||||
|
||||
return maxSamples;
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if the sample buffer is empty
|
||||
int FIFOSampleBuffer::isEmpty() const
|
||||
{
|
||||
return (samplesInBuffer == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
// Clears the sample buffer
|
||||
void FIFOSampleBuffer::clear()
|
||||
{
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// first-in-first-out pipe.
|
||||
///
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// function, and are received from the beginning of the buffer by calling
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// outputted samples from the buffer, as well as grows the buffer size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.11 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.cpp,v 1.11 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
// Constructor
|
||||
FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels)
|
||||
{
|
||||
sizeInBytes = 0; // reasonable initial value
|
||||
buffer = NULL; //new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE)];
|
||||
bufferUnaligned = NULL;
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
channels = numChannels;
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||
{
|
||||
delete[] bufferUnaligned;
|
||||
}
|
||||
|
||||
|
||||
// Sets number of channels, 1 = mono, 2 = stereo
|
||||
void FIFOSampleBuffer::setChannels(const uint numChannels)
|
||||
{
|
||||
uint usedBytes;
|
||||
|
||||
usedBytes = channels * samplesInBuffer;
|
||||
channels = numChannels;
|
||||
samplesInBuffer = usedBytes / channels;
|
||||
}
|
||||
|
||||
|
||||
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
||||
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
||||
// location on to the beginning of the buffer.
|
||||
void FIFOSampleBuffer::rewind()
|
||||
{
|
||||
if (bufferPos)
|
||||
{
|
||||
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
||||
bufferPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
// the sample buffer.
|
||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
memcpy(ptrEnd(numSamples), samples, sizeof(SAMPLETYPE) * numSamples * channels);
|
||||
samplesInBuffer += numSamples;
|
||||
}
|
||||
|
||||
|
||||
// Increases the number of samples in the buffer without copying any actual
|
||||
// samples.
|
||||
//
|
||||
// This function is used to update the number of samples in the sample buffer
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// careful though!
|
||||
void FIFOSampleBuffer::putSamples(uint numSamples)
|
||||
{
|
||||
uint req;
|
||||
|
||||
req = samplesInBuffer + numSamples;
|
||||
ensureCapacity(req);
|
||||
samplesInBuffer += numSamples;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
// where the new samples are to be inserted). This function may be used for
|
||||
// inserting new samples into the sample buffer directly. Please be careful!
|
||||
//
|
||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||
// terms of samples) there _at least_ should be, in order to the caller to
|
||||
// succesfully insert all the required samples to the buffer. When necessary,
|
||||
// the function grows the buffer size to comply with this requirement.
|
||||
//
|
||||
// When using this function as means for inserting new samples, also remember
|
||||
// to increase the sample count afterwards, by calling the
|
||||
// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||
{
|
||||
ensureCapacity(samplesInBuffer + slackCapacity);
|
||||
return buffer + samplesInBuffer * channels;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||
// This function is provided for accessing the output samples directly.
|
||||
// Please be careful!
|
||||
//
|
||||
// When using this function to output samples, also remember to 'remove' the
|
||||
// outputted samples from the buffer by calling the
|
||||
// 'receiveSamples(numSamples)' function
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin() const
|
||||
{
|
||||
return buffer + bufferPos * channels;
|
||||
}
|
||||
|
||||
|
||||
// Ensures that the buffer has enought capacity, i.e. space for _at least_
|
||||
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
||||
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
||||
// as well as to round the buffer size up to the virtual memory page size.
|
||||
void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||
{
|
||||
SAMPLETYPE *tempUnaligned, *temp;
|
||||
|
||||
if (capacityRequirement > getCapacity())
|
||||
{
|
||||
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & -4096;
|
||||
assert(sizeInBytes % 2 == 0);
|
||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||
if (tempUnaligned == NULL)
|
||||
{
|
||||
throw std::runtime_error("Couldn't allocate memory!\n");
|
||||
}
|
||||
temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & -16);
|
||||
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
||||
delete[] bufferUnaligned;
|
||||
buffer = temp;
|
||||
bufferUnaligned = tempUnaligned;
|
||||
bufferPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply rewind the buffer (if necessary)
|
||||
rewind();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the current buffer capacity in terms of samples
|
||||
uint FIFOSampleBuffer::getCapacity() const
|
||||
{
|
||||
return sizeInBytes / (channels * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of samples currently in the buffer
|
||||
uint FIFOSampleBuffer::numSamples() const
|
||||
{
|
||||
return samplesInBuffer;
|
||||
}
|
||||
|
||||
|
||||
// Output samples from beginning of the sample buffer. Copies demanded number
|
||||
// of samples to output and removes them from the sample buffer. If there
|
||||
// are less than 'numsample' samples in the buffer, returns all available.
|
||||
//
|
||||
// Returns number of samples copied.
|
||||
uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||
{
|
||||
uint num;
|
||||
|
||||
num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples;
|
||||
|
||||
memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num);
|
||||
return receiveSamples(num);
|
||||
}
|
||||
|
||||
|
||||
// Removes samples from the beginning of the sample buffer without copying them
|
||||
// anywhere. Used to reduce the number of samples in the buffer, when accessing
|
||||
// the sample buffer with the 'ptrBegin' function.
|
||||
uint FIFOSampleBuffer::receiveSamples(uint maxSamples)
|
||||
{
|
||||
if (maxSamples >= samplesInBuffer)
|
||||
{
|
||||
uint temp;
|
||||
|
||||
temp = samplesInBuffer;
|
||||
samplesInBuffer = 0;
|
||||
return temp;
|
||||
}
|
||||
|
||||
samplesInBuffer -= maxSamples;
|
||||
bufferPos += maxSamples;
|
||||
|
||||
return maxSamples;
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if the sample buffer is empty
|
||||
int FIFOSampleBuffer::isEmpty() const
|
||||
{
|
||||
return (samplesInBuffer == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
// Clears the sample buffer
|
||||
void FIFOSampleBuffer::clear()
|
||||
{
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
@ -1,266 +1,266 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.16 $
|
||||
//
|
||||
// $Id: FIRFilter.cpp,v 1.16 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
#include "FIRFilter.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'FIRFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
FIRFilter::FIRFilter()
|
||||
{
|
||||
resultDivFactor = 0;
|
||||
length = 0;
|
||||
lengthDiv8 = 0;
|
||||
filterCoeffs = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter::~FIRFilter()
|
||||
{
|
||||
delete[] filterCoeffs;
|
||||
}
|
||||
|
||||
// Usual C-version of the filter routine for stereo sound
|
||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE suml, sumr;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = 2 * (numSamples - length);
|
||||
|
||||
for (j = 0; j < end; j += 2)
|
||||
{
|
||||
const SAMPLETYPE *ptr;
|
||||
|
||||
suml = sumr = 0;
|
||||
ptr = src + j;
|
||||
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 2] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 4] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 6] * filterCoeffs[i + 3];
|
||||
sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 3] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 5] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 7] * filterCoeffs[i + 3];
|
||||
}
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
suml >>= resultDivFactor;
|
||||
sumr >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
||||
// saturate to 16 bit integer limits
|
||||
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
||||
#else
|
||||
suml *= dScaler;
|
||||
sumr *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)suml;
|
||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||
}
|
||||
return numSamples - length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Usual C-version of the filter routine for mono sound
|
||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE sum;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = numSamples - length;
|
||||
for (j = 0; j < end; j ++)
|
||||
{
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
sum += src[i + 0] * filterCoeffs[i + 0] +
|
||||
src[i + 1] * filterCoeffs[i + 1] +
|
||||
src[i + 2] * filterCoeffs[i + 2] +
|
||||
src[i + 3] * filterCoeffs[i + 3];
|
||||
}
|
||||
#ifdef INTEGER_SAMPLES
|
||||
sum >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||
#else
|
||||
sum *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)sum;
|
||||
src ++;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
// Set filter coeffiecients and length.
|
||||
//
|
||||
// Throws an exception if filter length isn't divisible by 8
|
||||
void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
assert(newLength > 0);
|
||||
if (newLength % 8) throw std::runtime_error("FIR filter length not divisible by 8");
|
||||
|
||||
lengthDiv8 = newLength / 8;
|
||||
length = lengthDiv8 * 8;
|
||||
assert(length == newLength);
|
||||
|
||||
resultDivFactor = uResultDivFactor;
|
||||
resultDivider = (SAMPLETYPE)pow(2.0f, (int)resultDivFactor);
|
||||
|
||||
delete[] filterCoeffs;
|
||||
filterCoeffs = new SAMPLETYPE[length];
|
||||
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
uint FIRFilter::getLength() const
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
//
|
||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
// smaller than the amount of input samples.
|
||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
assert(numChannels == 1 || numChannels == 2);
|
||||
|
||||
assert(length > 0);
|
||||
assert(lengthDiv8 * 8 == length);
|
||||
if (numSamples < length) return 0;
|
||||
assert(resultDivFactor >= 0);
|
||||
if (numChannels == 2)
|
||||
{
|
||||
return evaluateFilterStereo(dest, src, numSamples);
|
||||
} else {
|
||||
return evaluateFilterMono(dest, src, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX-capable CPU available or not.
|
||||
void * FIRFilter::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
||||
throw std::runtime_error("Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter * FIRFilter::newInstance()
|
||||
{
|
||||
uint uExtensions;
|
||||
|
||||
uExtensions = detectCPUextensions();
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample types
|
||||
if (uExtensions & SUPPORT_MMX)
|
||||
{
|
||||
return ::new FIRFilterMMX;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_MMX
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
if (uExtensions & SUPPORT_SSE)
|
||||
{
|
||||
// SSE support
|
||||
return ::new FIRFilterSSE;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
if (uExtensions & SUPPORT_3DNOW)
|
||||
{
|
||||
// 3DNow! support
|
||||
return ::new FIRFilter3DNow;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
{
|
||||
// ISA optimizations not supported, use plain C version
|
||||
return ::new FIRFilter;
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.16 $
|
||||
//
|
||||
// $Id: FIRFilter.cpp,v 1.16 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
#include "FIRFilter.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'FIRFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
FIRFilter::FIRFilter()
|
||||
{
|
||||
resultDivFactor = 0;
|
||||
length = 0;
|
||||
lengthDiv8 = 0;
|
||||
filterCoeffs = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter::~FIRFilter()
|
||||
{
|
||||
delete[] filterCoeffs;
|
||||
}
|
||||
|
||||
// Usual C-version of the filter routine for stereo sound
|
||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE suml, sumr;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = 2 * (numSamples - length);
|
||||
|
||||
for (j = 0; j < end; j += 2)
|
||||
{
|
||||
const SAMPLETYPE *ptr;
|
||||
|
||||
suml = sumr = 0;
|
||||
ptr = src + j;
|
||||
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 2] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 4] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 6] * filterCoeffs[i + 3];
|
||||
sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 3] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 5] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 7] * filterCoeffs[i + 3];
|
||||
}
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
suml >>= resultDivFactor;
|
||||
sumr >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
||||
// saturate to 16 bit integer limits
|
||||
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
||||
#else
|
||||
suml *= dScaler;
|
||||
sumr *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)suml;
|
||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||
}
|
||||
return numSamples - length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Usual C-version of the filter routine for mono sound
|
||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE sum;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = numSamples - length;
|
||||
for (j = 0; j < end; j ++)
|
||||
{
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
sum += src[i + 0] * filterCoeffs[i + 0] +
|
||||
src[i + 1] * filterCoeffs[i + 1] +
|
||||
src[i + 2] * filterCoeffs[i + 2] +
|
||||
src[i + 3] * filterCoeffs[i + 3];
|
||||
}
|
||||
#ifdef INTEGER_SAMPLES
|
||||
sum >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||
#else
|
||||
sum *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)sum;
|
||||
src ++;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
// Set filter coeffiecients and length.
|
||||
//
|
||||
// Throws an exception if filter length isn't divisible by 8
|
||||
void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
assert(newLength > 0);
|
||||
if (newLength % 8) throw std::runtime_error("FIR filter length not divisible by 8");
|
||||
|
||||
lengthDiv8 = newLength / 8;
|
||||
length = lengthDiv8 * 8;
|
||||
assert(length == newLength);
|
||||
|
||||
resultDivFactor = uResultDivFactor;
|
||||
resultDivider = (SAMPLETYPE)pow(2.0f, (int)resultDivFactor);
|
||||
|
||||
delete[] filterCoeffs;
|
||||
filterCoeffs = new SAMPLETYPE[length];
|
||||
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
uint FIRFilter::getLength() const
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
//
|
||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
// smaller than the amount of input samples.
|
||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
assert(numChannels == 1 || numChannels == 2);
|
||||
|
||||
assert(length > 0);
|
||||
assert(lengthDiv8 * 8 == length);
|
||||
if (numSamples < length) return 0;
|
||||
assert(resultDivFactor >= 0);
|
||||
if (numChannels == 2)
|
||||
{
|
||||
return evaluateFilterStereo(dest, src, numSamples);
|
||||
} else {
|
||||
return evaluateFilterMono(dest, src, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX-capable CPU available or not.
|
||||
void * FIRFilter::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
||||
throw std::runtime_error("Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter * FIRFilter::newInstance()
|
||||
{
|
||||
uint uExtensions;
|
||||
|
||||
uExtensions = detectCPUextensions();
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample types
|
||||
if (uExtensions & SUPPORT_MMX)
|
||||
{
|
||||
return ::new FIRFilterMMX;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_MMX
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
if (uExtensions & SUPPORT_SSE)
|
||||
{
|
||||
// SSE support
|
||||
return ::new FIRFilterSSE;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
if (uExtensions & SUPPORT_3DNOW)
|
||||
{
|
||||
// 3DNow! support
|
||||
return ::new FIRFilter3DNow;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
{
|
||||
// ISA optimizations not supported, use plain C version
|
||||
return ::new FIRFilter;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,474 +1,474 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
/// Please notice though that they aren't currently protected by semaphores,
|
||||
/// so in multi-thread application external semaphore protection may be
|
||||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.13 $
|
||||
//
|
||||
// $Id: SoundTouch.cpp,v 1.13 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SoundTouch.h"
|
||||
#include "TDStretch.h"
|
||||
#include "RateTransposer.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/// Print library version string
|
||||
extern "C" void soundtouch_ac_test()
|
||||
{
|
||||
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
||||
}
|
||||
|
||||
|
||||
SoundTouch::SoundTouch()
|
||||
{
|
||||
// Initialize rate transposer and tempo changer instances
|
||||
|
||||
pRateTransposer = RateTransposer::newInstance();
|
||||
pTDStretch = TDStretch::newInstance();
|
||||
|
||||
setOutPipe(pTDStretch);
|
||||
|
||||
rate = tempo = 0;
|
||||
|
||||
virtualPitch =
|
||||
virtualRate =
|
||||
virtualTempo = 1.0;
|
||||
|
||||
calcEffectiveRateAndTempo();
|
||||
|
||||
channels = 0;
|
||||
bSrateSet = FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SoundTouch::~SoundTouch()
|
||||
{
|
||||
delete pRateTransposer;
|
||||
delete pTDStretch;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get SoundTouch library version string
|
||||
const char *SoundTouch::getVersionString()
|
||||
{
|
||||
static const char *_version = SOUNDTOUCH_VERSION;
|
||||
|
||||
return _version;
|
||||
}
|
||||
|
||||
|
||||
/// Get SoundTouch library version Id
|
||||
uint SoundTouch::getVersionId()
|
||||
{
|
||||
return SOUNDTOUCH_VERSION_ID;
|
||||
}
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void SoundTouch::setChannels(uint numChannels)
|
||||
{
|
||||
if (numChannels != 1 && numChannels != 2)
|
||||
{
|
||||
throw std::runtime_error("Illegal number of channels");
|
||||
}
|
||||
channels = numChannels;
|
||||
pRateTransposer->setChannels(numChannels);
|
||||
pTDStretch->setChannels(numChannels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||
// represent slower rate, larger faster rates.
|
||||
void SoundTouch::setRate(float newRate)
|
||||
{
|
||||
virtualRate = newRate;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new rate control value as a difference in percents compared
|
||||
// to the original rate (-50 .. +100 %)
|
||||
void SoundTouch::setRateChange(float newRate)
|
||||
{
|
||||
virtualRate = 1.0f + 0.01f * newRate;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||
// represent slower tempo, larger faster tempo.
|
||||
void SoundTouch::setTempo(float newTempo)
|
||||
{
|
||||
virtualTempo = newTempo;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new tempo control value as a difference in percents compared
|
||||
// to the original tempo (-50 .. +100 %)
|
||||
void SoundTouch::setTempoChange(float newTempo)
|
||||
{
|
||||
virtualTempo = 1.0f + 0.01f * newTempo;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||
// represent lower pitches, larger values higher pitch.
|
||||
void SoundTouch::setPitch(float newPitch)
|
||||
{
|
||||
virtualPitch = newPitch;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets pitch change in octaves compared to the original pitch
|
||||
// (-1.00 .. +1.00)
|
||||
void SoundTouch::setPitchOctaves(float newPitch)
|
||||
{
|
||||
virtualPitch = (float)exp(0.69314718056f * newPitch);
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets pitch change in semi-tones compared to the original pitch
|
||||
// (-12 .. +12)
|
||||
void SoundTouch::setPitchSemiTones(int newPitch)
|
||||
{
|
||||
setPitchOctaves((float)newPitch / 12.0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SoundTouch::setPitchSemiTones(float newPitch)
|
||||
{
|
||||
setPitchOctaves(newPitch / 12.0f);
|
||||
}
|
||||
|
||||
|
||||
// Calculates 'effective' rate and tempo values from the
|
||||
// nominal control values.
|
||||
void SoundTouch::calcEffectiveRateAndTempo()
|
||||
{
|
||||
float oldTempo = tempo;
|
||||
float oldRate = rate;
|
||||
|
||||
tempo = virtualTempo / virtualPitch;
|
||||
rate = virtualPitch * virtualRate;
|
||||
|
||||
if (rate != oldRate) pRateTransposer->setRate(rate);
|
||||
if (tempo != oldTempo) pTDStretch->setTempo(tempo);
|
||||
|
||||
if (rate > 1.0f)
|
||||
{
|
||||
if (output != pRateTransposer)
|
||||
{
|
||||
FIFOSamplePipe *transOut;
|
||||
|
||||
assert(output == pTDStretch);
|
||||
// move samples in the current output buffer to the output of pRateTransposer
|
||||
transOut = pRateTransposer->getOutput();
|
||||
transOut->moveSamples(*output);
|
||||
// move samples in tempo changer's input to pitch transposer's input
|
||||
pRateTransposer->moveSamples(*pTDStretch->getInput());
|
||||
|
||||
output = pRateTransposer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (output != pTDStretch)
|
||||
{
|
||||
FIFOSamplePipe *tempoOut;
|
||||
|
||||
assert(output == pRateTransposer);
|
||||
// move samples in the current output buffer to the output of pTDStretch
|
||||
tempoOut = pTDStretch->getOutput();
|
||||
tempoOut->moveSamples(*output);
|
||||
// move samples in pitch transposer's store buffer to tempo changer's input
|
||||
pTDStretch->moveSamples(*pRateTransposer->getStore());
|
||||
|
||||
output = pTDStretch;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sets sample rate.
|
||||
void SoundTouch::setSampleRate(uint srate)
|
||||
{
|
||||
bSrateSet = TRUE;
|
||||
// set sample rate, leave other tempo changer parameters as they are.
|
||||
pTDStretch->setParameters(srate);
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
if (bSrateSet == FALSE)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Sample rate not defined");
|
||||
}
|
||||
else if (channels == 0)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Number of channels not defined");
|
||||
}
|
||||
|
||||
// Transpose the rate of the new samples if necessary
|
||||
/* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
|
||||
if (rate == 1.0f)
|
||||
{
|
||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
||||
assert(output == pTDStretch);
|
||||
if (pRateTransposer->isEmpty() == 0)
|
||||
{
|
||||
// yet flush the last samples in the pitch transposer buffer
|
||||
// (may happen if 'rate' changes from a non-zero value to zero)
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
}
|
||||
*/
|
||||
else if (rate <= 1.0f)
|
||||
{
|
||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||
assert(output == pTDStretch);
|
||||
pRateTransposer->putSamples(samples, numSamples);
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(rate > 1.0f);
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
assert(output == pRateTransposer);
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
pRateTransposer->moveSamples(*pTDStretch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Flushes the last samples from the processing pipeline to the output.
|
||||
// Clears also the internal processing buffers.
|
||||
//
|
||||
// Note: This function is meant for extracting the last samples of a sound
|
||||
// stream. This function may introduce additional blank samples in the end
|
||||
// of the sound stream, and thus it's not recommended to call this function
|
||||
// in the middle of a sound stream.
|
||||
void SoundTouch::flush()
|
||||
{
|
||||
int i;
|
||||
uint nOut;
|
||||
SAMPLETYPE buff[128];
|
||||
|
||||
nOut = numSamples();
|
||||
|
||||
memset(buff, 0, 128 * sizeof(SAMPLETYPE));
|
||||
// "Push" the last active samples out from the processing pipeline by
|
||||
// feeding blank samples into the processing pipeline until new,
|
||||
// processed samples appear in the output (not however, more than
|
||||
// 8ksamples in any case)
|
||||
for (i = 0; i < 128; i ++)
|
||||
{
|
||||
putSamples(buff, 64);
|
||||
if (numSamples() != nOut) break; // new samples have appeared in the output!
|
||||
}
|
||||
|
||||
// Clear working buffers
|
||||
pRateTransposer->clear();
|
||||
pTDStretch->clearInput();
|
||||
// yet leave the 'tempoChanger' output intouched as that's where the
|
||||
// flushed samples are!
|
||||
}
|
||||
|
||||
|
||||
// Changes a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
BOOL SoundTouch::setSetting(uint settingId, uint value)
|
||||
{
|
||||
uint sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
||||
|
||||
// read current tdstretch routine parameters
|
||||
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
// enables / disabless anti-alias filter
|
||||
pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
// sets anti-alias filter length
|
||||
pRateTransposer->getAAFilter()->setLength(value);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
// enables / disables tempo routine quick seeking algorithm
|
||||
pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
// change time-stretch sequence duration parameter
|
||||
pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
// change time-stretch seek window length parameter
|
||||
pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
// change time-stretch overlap length parameter
|
||||
pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
|
||||
return TRUE;
|
||||
|
||||
default :
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
//
|
||||
// Returns the setting value.
|
||||
uint SoundTouch::getSetting(uint settingId) const
|
||||
{
|
||||
uint temp;
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
return pRateTransposer->isAAFilterEnabled();
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
return pRateTransposer->getAAFilter()->getLength();
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
return pTDStretch->isQuickSeekEnabled();
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
||||
return temp;
|
||||
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clears all the samples in the object's output and internal processing
|
||||
// buffers.
|
||||
void SoundTouch::clear()
|
||||
{
|
||||
pRateTransposer->clear();
|
||||
pTDStretch->clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Returns number of samples currently unprocessed.
|
||||
uint SoundTouch::numUnprocessedSamples() const
|
||||
{
|
||||
FIFOSamplePipe * psp;
|
||||
if (pTDStretch)
|
||||
{
|
||||
psp = pTDStretch->getInput();
|
||||
if (psp)
|
||||
{
|
||||
return psp->numSamples();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
/// Please notice though that they aren't currently protected by semaphores,
|
||||
/// so in multi-thread application external semaphore protection may be
|
||||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.13 $
|
||||
//
|
||||
// $Id: SoundTouch.cpp,v 1.13 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SoundTouch.h"
|
||||
#include "TDStretch.h"
|
||||
#include "RateTransposer.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/// Print library version string
|
||||
extern "C" void soundtouch_ac_test()
|
||||
{
|
||||
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
||||
}
|
||||
|
||||
|
||||
SoundTouch::SoundTouch()
|
||||
{
|
||||
// Initialize rate transposer and tempo changer instances
|
||||
|
||||
pRateTransposer = RateTransposer::newInstance();
|
||||
pTDStretch = TDStretch::newInstance();
|
||||
|
||||
setOutPipe(pTDStretch);
|
||||
|
||||
rate = tempo = 0;
|
||||
|
||||
virtualPitch =
|
||||
virtualRate =
|
||||
virtualTempo = 1.0;
|
||||
|
||||
calcEffectiveRateAndTempo();
|
||||
|
||||
channels = 0;
|
||||
bSrateSet = FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SoundTouch::~SoundTouch()
|
||||
{
|
||||
delete pRateTransposer;
|
||||
delete pTDStretch;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get SoundTouch library version string
|
||||
const char *SoundTouch::getVersionString()
|
||||
{
|
||||
static const char *_version = SOUNDTOUCH_VERSION;
|
||||
|
||||
return _version;
|
||||
}
|
||||
|
||||
|
||||
/// Get SoundTouch library version Id
|
||||
uint SoundTouch::getVersionId()
|
||||
{
|
||||
return SOUNDTOUCH_VERSION_ID;
|
||||
}
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void SoundTouch::setChannels(uint numChannels)
|
||||
{
|
||||
if (numChannels != 1 && numChannels != 2)
|
||||
{
|
||||
throw std::runtime_error("Illegal number of channels");
|
||||
}
|
||||
channels = numChannels;
|
||||
pRateTransposer->setChannels(numChannels);
|
||||
pTDStretch->setChannels(numChannels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||
// represent slower rate, larger faster rates.
|
||||
void SoundTouch::setRate(float newRate)
|
||||
{
|
||||
virtualRate = newRate;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new rate control value as a difference in percents compared
|
||||
// to the original rate (-50 .. +100 %)
|
||||
void SoundTouch::setRateChange(float newRate)
|
||||
{
|
||||
virtualRate = 1.0f + 0.01f * newRate;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||
// represent slower tempo, larger faster tempo.
|
||||
void SoundTouch::setTempo(float newTempo)
|
||||
{
|
||||
virtualTempo = newTempo;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new tempo control value as a difference in percents compared
|
||||
// to the original tempo (-50 .. +100 %)
|
||||
void SoundTouch::setTempoChange(float newTempo)
|
||||
{
|
||||
virtualTempo = 1.0f + 0.01f * newTempo;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||
// represent lower pitches, larger values higher pitch.
|
||||
void SoundTouch::setPitch(float newPitch)
|
||||
{
|
||||
virtualPitch = newPitch;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets pitch change in octaves compared to the original pitch
|
||||
// (-1.00 .. +1.00)
|
||||
void SoundTouch::setPitchOctaves(float newPitch)
|
||||
{
|
||||
virtualPitch = (float)exp(0.69314718056f * newPitch);
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets pitch change in semi-tones compared to the original pitch
|
||||
// (-12 .. +12)
|
||||
void SoundTouch::setPitchSemiTones(int newPitch)
|
||||
{
|
||||
setPitchOctaves((float)newPitch / 12.0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SoundTouch::setPitchSemiTones(float newPitch)
|
||||
{
|
||||
setPitchOctaves(newPitch / 12.0f);
|
||||
}
|
||||
|
||||
|
||||
// Calculates 'effective' rate and tempo values from the
|
||||
// nominal control values.
|
||||
void SoundTouch::calcEffectiveRateAndTempo()
|
||||
{
|
||||
float oldTempo = tempo;
|
||||
float oldRate = rate;
|
||||
|
||||
tempo = virtualTempo / virtualPitch;
|
||||
rate = virtualPitch * virtualRate;
|
||||
|
||||
if (rate != oldRate) pRateTransposer->setRate(rate);
|
||||
if (tempo != oldTempo) pTDStretch->setTempo(tempo);
|
||||
|
||||
if (rate > 1.0f)
|
||||
{
|
||||
if (output != pRateTransposer)
|
||||
{
|
||||
FIFOSamplePipe *transOut;
|
||||
|
||||
assert(output == pTDStretch);
|
||||
// move samples in the current output buffer to the output of pRateTransposer
|
||||
transOut = pRateTransposer->getOutput();
|
||||
transOut->moveSamples(*output);
|
||||
// move samples in tempo changer's input to pitch transposer's input
|
||||
pRateTransposer->moveSamples(*pTDStretch->getInput());
|
||||
|
||||
output = pRateTransposer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (output != pTDStretch)
|
||||
{
|
||||
FIFOSamplePipe *tempoOut;
|
||||
|
||||
assert(output == pRateTransposer);
|
||||
// move samples in the current output buffer to the output of pTDStretch
|
||||
tempoOut = pTDStretch->getOutput();
|
||||
tempoOut->moveSamples(*output);
|
||||
// move samples in pitch transposer's store buffer to tempo changer's input
|
||||
pTDStretch->moveSamples(*pRateTransposer->getStore());
|
||||
|
||||
output = pTDStretch;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sets sample rate.
|
||||
void SoundTouch::setSampleRate(uint srate)
|
||||
{
|
||||
bSrateSet = TRUE;
|
||||
// set sample rate, leave other tempo changer parameters as they are.
|
||||
pTDStretch->setParameters(srate);
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
if (bSrateSet == FALSE)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Sample rate not defined");
|
||||
}
|
||||
else if (channels == 0)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Number of channels not defined");
|
||||
}
|
||||
|
||||
// Transpose the rate of the new samples if necessary
|
||||
/* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
|
||||
if (rate == 1.0f)
|
||||
{
|
||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
||||
assert(output == pTDStretch);
|
||||
if (pRateTransposer->isEmpty() == 0)
|
||||
{
|
||||
// yet flush the last samples in the pitch transposer buffer
|
||||
// (may happen if 'rate' changes from a non-zero value to zero)
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
}
|
||||
*/
|
||||
else if (rate <= 1.0f)
|
||||
{
|
||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||
assert(output == pTDStretch);
|
||||
pRateTransposer->putSamples(samples, numSamples);
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(rate > 1.0f);
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
assert(output == pRateTransposer);
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
pRateTransposer->moveSamples(*pTDStretch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Flushes the last samples from the processing pipeline to the output.
|
||||
// Clears also the internal processing buffers.
|
||||
//
|
||||
// Note: This function is meant for extracting the last samples of a sound
|
||||
// stream. This function may introduce additional blank samples in the end
|
||||
// of the sound stream, and thus it's not recommended to call this function
|
||||
// in the middle of a sound stream.
|
||||
void SoundTouch::flush()
|
||||
{
|
||||
int i;
|
||||
uint nOut;
|
||||
SAMPLETYPE buff[128];
|
||||
|
||||
nOut = numSamples();
|
||||
|
||||
memset(buff, 0, 128 * sizeof(SAMPLETYPE));
|
||||
// "Push" the last active samples out from the processing pipeline by
|
||||
// feeding blank samples into the processing pipeline until new,
|
||||
// processed samples appear in the output (not however, more than
|
||||
// 8ksamples in any case)
|
||||
for (i = 0; i < 128; i ++)
|
||||
{
|
||||
putSamples(buff, 64);
|
||||
if (numSamples() != nOut) break; // new samples have appeared in the output!
|
||||
}
|
||||
|
||||
// Clear working buffers
|
||||
pRateTransposer->clear();
|
||||
pTDStretch->clearInput();
|
||||
// yet leave the 'tempoChanger' output intouched as that's where the
|
||||
// flushed samples are!
|
||||
}
|
||||
|
||||
|
||||
// Changes a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
BOOL SoundTouch::setSetting(uint settingId, uint value)
|
||||
{
|
||||
uint sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
||||
|
||||
// read current tdstretch routine parameters
|
||||
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
// enables / disabless anti-alias filter
|
||||
pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
// sets anti-alias filter length
|
||||
pRateTransposer->getAAFilter()->setLength(value);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
// enables / disables tempo routine quick seeking algorithm
|
||||
pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
// change time-stretch sequence duration parameter
|
||||
pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
// change time-stretch seek window length parameter
|
||||
pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
// change time-stretch overlap length parameter
|
||||
pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
|
||||
return TRUE;
|
||||
|
||||
default :
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
//
|
||||
// Returns the setting value.
|
||||
uint SoundTouch::getSetting(uint settingId) const
|
||||
{
|
||||
uint temp;
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
return pRateTransposer->isAAFilterEnabled();
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
return pRateTransposer->getAAFilter()->getLength();
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
return pTDStretch->isQuickSeekEnabled();
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
||||
return temp;
|
||||
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clears all the samples in the object's output and internal processing
|
||||
// buffers.
|
||||
void SoundTouch::clear()
|
||||
{
|
||||
pRateTransposer->clear();
|
||||
pTDStretch->clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Returns number of samples currently unprocessed.
|
||||
uint SoundTouch::numUnprocessedSamples() const
|
||||
{
|
||||
FIFOSamplePipe * psp;
|
||||
if (pTDStretch)
|
||||
{
|
||||
psp = pTDStretch->getInput();
|
||||
if (psp)
|
||||
{
|
||||
return psp->numSamples();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,138 +1,138 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// gcc version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled on any platform with the GNU C compiler.
|
||||
/// Compiler. Please see 'cpu_detect_x86_win.cpp' for the x86 Windows version
|
||||
/// of this file.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.6 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_gcc.cpp,v 1.6 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#error wrong platform - this source code file is for the GNU C compiler.
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <stdio.h>
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
#ifndef __i386__
|
||||
return 0; // always disable extensions on non-x86 platforms.
|
||||
#else
|
||||
uint res = 0;
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
asm volatile(
|
||||
"\n\txor %%esi, %%esi" // clear %%esi = result register
|
||||
// check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
|
||||
"\n\tpushf" // save eflags to stack
|
||||
"\n\tpop %%eax" // load eax from stack (with eflags)
|
||||
"\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx
|
||||
"\n\txor $0x00200000, %%eax" // toggle bit 21
|
||||
"\n\tpush %%eax" // store toggled eflags to stack
|
||||
"\n\tpopf" // load eflags from stack
|
||||
"\n\tpushf" // save updated eflags to stack
|
||||
"\n\tpop %%eax" // load from stack
|
||||
"\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
|
||||
"\n\tcmp %%ecx, %%eax" // compare to original eflags values
|
||||
"\n\tjz end" // jumps to 'end' if cpuid not present
|
||||
|
||||
// cpuid instruction available, test for presence of mmx instructions
|
||||
|
||||
"\n\tmovl $1, %%eax"
|
||||
"\n\tcpuid"
|
||||
// movl $0x00800000, %edx // force enable MMX
|
||||
"\n\ttest $0x00800000, %%edx"
|
||||
"\n\tjz end" // branch if MMX not available
|
||||
|
||||
"\n\tor $0x01, %%esi" // otherwise add MMX support bit
|
||||
|
||||
"\n\ttest $0x02000000, %%edx"
|
||||
"\n\tjz test3DNow" // branch if SSE not available
|
||||
|
||||
"\n\tor $0x08, %%esi" // otherwise add SSE support bit
|
||||
|
||||
"\n\ttest3DNow:"
|
||||
// test for precense of AMD extensions
|
||||
"\n\tmov $0x80000000, %%eax"
|
||||
"\n\tcpuid"
|
||||
"\n\tcmp $0x80000000, %%eax"
|
||||
"\n\tjbe end" // branch if no AMD extensions detected
|
||||
|
||||
// test for precense of 3DNow! extension
|
||||
"\n\tmov $0x80000001, %%eax"
|
||||
"\n\tcpuid"
|
||||
"\n\ttest $0x80000000, %%edx"
|
||||
"\n\tjz end" // branch if 3DNow! not detected
|
||||
|
||||
"\n\tor $0x02, %%esi" // otherwise add 3DNow support bit
|
||||
|
||||
"\n\tend:"
|
||||
|
||||
"\n\tmov %%esi, %0"
|
||||
|
||||
: "=r" (res)
|
||||
: /* no inputs */
|
||||
: "%edx", "%eax", "%ecx", "%esi" );
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
#endif
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// gcc version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled on any platform with the GNU C compiler.
|
||||
/// Compiler. Please see 'cpu_detect_x86_win.cpp' for the x86 Windows version
|
||||
/// of this file.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.6 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_gcc.cpp,v 1.6 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#error wrong platform - this source code file is for the GNU C compiler.
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <stdio.h>
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
#ifndef __i386__
|
||||
return 0; // always disable extensions on non-x86 platforms.
|
||||
#else
|
||||
uint res = 0;
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
asm volatile(
|
||||
"\n\txor %%esi, %%esi" // clear %%esi = result register
|
||||
// check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
|
||||
"\n\tpushf" // save eflags to stack
|
||||
"\n\tpop %%eax" // load eax from stack (with eflags)
|
||||
"\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx
|
||||
"\n\txor $0x00200000, %%eax" // toggle bit 21
|
||||
"\n\tpush %%eax" // store toggled eflags to stack
|
||||
"\n\tpopf" // load eflags from stack
|
||||
"\n\tpushf" // save updated eflags to stack
|
||||
"\n\tpop %%eax" // load from stack
|
||||
"\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
|
||||
"\n\tcmp %%ecx, %%eax" // compare to original eflags values
|
||||
"\n\tjz end" // jumps to 'end' if cpuid not present
|
||||
|
||||
// cpuid instruction available, test for presence of mmx instructions
|
||||
|
||||
"\n\tmovl $1, %%eax"
|
||||
"\n\tcpuid"
|
||||
// movl $0x00800000, %edx // force enable MMX
|
||||
"\n\ttest $0x00800000, %%edx"
|
||||
"\n\tjz end" // branch if MMX not available
|
||||
|
||||
"\n\tor $0x01, %%esi" // otherwise add MMX support bit
|
||||
|
||||
"\n\ttest $0x02000000, %%edx"
|
||||
"\n\tjz test3DNow" // branch if SSE not available
|
||||
|
||||
"\n\tor $0x08, %%esi" // otherwise add SSE support bit
|
||||
|
||||
"\n\ttest3DNow:"
|
||||
// test for precense of AMD extensions
|
||||
"\n\tmov $0x80000000, %%eax"
|
||||
"\n\tcpuid"
|
||||
"\n\tcmp $0x80000000, %%eax"
|
||||
"\n\tjbe end" // branch if no AMD extensions detected
|
||||
|
||||
// test for precense of 3DNow! extension
|
||||
"\n\tmov $0x80000001, %%eax"
|
||||
"\n\tcpuid"
|
||||
"\n\ttest $0x80000000, %%edx"
|
||||
"\n\tjz end" // branch if 3DNow! not detected
|
||||
|
||||
"\n\tor $0x02, %%esi" // otherwise add 3DNow support bit
|
||||
|
||||
"\n\tend:"
|
||||
|
||||
"\n\tmov %%esi, %0"
|
||||
|
||||
: "=r" (res)
|
||||
: /* no inputs */
|
||||
: "%edx", "%eax", "%ecx", "%esi" );
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
#endif
|
||||
}
|
||||
|
@ -1,126 +1,126 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version
|
||||
/// for all GNU platforms.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error wrong platform - this source code file is exclusively for Win32 platform
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
uint res = 0;
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
_asm
|
||||
{
|
||||
; check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
;
|
||||
xor esi, esi ; clear esi = result register
|
||||
|
||||
pushfd ; save eflags to stack
|
||||
pop eax ; load eax from stack (with eflags)
|
||||
mov ecx, eax ; save the original eflags values to ecx
|
||||
xor eax, 0x00200000 ; toggle bit 21
|
||||
push eax ; store toggled eflags to stack
|
||||
popfd ; load eflags from stack
|
||||
pushfd ; save updated eflags to stack
|
||||
pop eax ; load from stack
|
||||
xor edx, edx ; clear edx for defaulting no mmx
|
||||
cmp eax, ecx ; compare to original eflags values
|
||||
jz end ; jumps to 'end' if cpuid not present
|
||||
|
||||
; cpuid instruction available, test for presence of mmx instructions
|
||||
mov eax, 1
|
||||
cpuid
|
||||
test edx, 0x00800000
|
||||
jz end ; branch if MMX not available
|
||||
|
||||
or esi, SUPPORT_MMX ; otherwise add MMX support bit
|
||||
|
||||
test edx, 0x02000000
|
||||
jz test3DNow ; branch if SSE not available
|
||||
|
||||
or esi, SUPPORT_SSE ; otherwise add SSE support bit
|
||||
|
||||
test3DNow:
|
||||
; test for precense of AMD extensions
|
||||
mov eax, 0x80000000
|
||||
cpuid
|
||||
cmp eax, 0x80000000
|
||||
jbe end ; branch if no AMD extensions detected
|
||||
|
||||
; test for precense of 3DNow! extension
|
||||
mov eax, 0x80000001
|
||||
cpuid
|
||||
test edx, 0x80000000
|
||||
jz end ; branch if 3DNow! not detected
|
||||
|
||||
or esi, SUPPORT_3DNOW ; otherwise add 3DNow support bit
|
||||
|
||||
end:
|
||||
|
||||
mov res, esi
|
||||
}
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version
|
||||
/// for all GNU platforms.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error wrong platform - this source code file is exclusively for Win32 platform
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
uint res = 0;
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
_asm
|
||||
{
|
||||
; check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
;
|
||||
xor esi, esi ; clear esi = result register
|
||||
|
||||
pushfd ; save eflags to stack
|
||||
pop eax ; load eax from stack (with eflags)
|
||||
mov ecx, eax ; save the original eflags values to ecx
|
||||
xor eax, 0x00200000 ; toggle bit 21
|
||||
push eax ; store toggled eflags to stack
|
||||
popfd ; load eflags from stack
|
||||
pushfd ; save updated eflags to stack
|
||||
pop eax ; load from stack
|
||||
xor edx, edx ; clear edx for defaulting no mmx
|
||||
cmp eax, ecx ; compare to original eflags values
|
||||
jz end ; jumps to 'end' if cpuid not present
|
||||
|
||||
; cpuid instruction available, test for presence of mmx instructions
|
||||
mov eax, 1
|
||||
cpuid
|
||||
test edx, 0x00800000
|
||||
jz end ; branch if MMX not available
|
||||
|
||||
or esi, SUPPORT_MMX ; otherwise add MMX support bit
|
||||
|
||||
test edx, 0x02000000
|
||||
jz test3DNow ; branch if SSE not available
|
||||
|
||||
or esi, SUPPORT_SSE ; otherwise add SSE support bit
|
||||
|
||||
test3DNow:
|
||||
; test for precense of AMD extensions
|
||||
mov eax, 0x80000000
|
||||
cpuid
|
||||
cmp eax, 0x80000000
|
||||
jbe end ; branch if no AMD extensions detected
|
||||
|
||||
; test for precense of 3DNow! extension
|
||||
mov eax, 0x80000001
|
||||
cpuid
|
||||
test edx, 0x80000000
|
||||
jz end ; branch if 3DNow! not detected
|
||||
|
||||
or esi, SUPPORT_3DNOW ; otherwise add 3DNow support bit
|
||||
|
||||
end:
|
||||
|
||||
mov res, esi
|
||||
}
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
}
|
||||
|
@ -1,305 +1,305 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
||||
/// this single source code file, regardless to their class or original source
|
||||
/// code file, in order to ease porting the library to other compiler and
|
||||
/// processor platforms.
|
||||
///
|
||||
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
|
||||
/// is available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/06 18:52:43 $
|
||||
// File revision : $Revision: 1.1 $
|
||||
//
|
||||
// $Id: mmx_optimized.cpp,v 1.1 2006/02/06 18:52:43 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample type
|
||||
|
||||
#if !(WIN32 || __i386__ || __x86_64__)
|
||||
#error "wrong platform - this source code file is exclusively for x86 platforms"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'TDStretchMMX'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <mmintrin.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
||||
{
|
||||
const __m64 *pVec1, *pVec2;
|
||||
__m64 shifter;
|
||||
__m64 accu;
|
||||
long corr;
|
||||
uint i;
|
||||
|
||||
pVec1 = (__m64*)pV1;
|
||||
pVec2 = (__m64*)pV2;
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
accu = _mm_setzero_si64();
|
||||
|
||||
// Process 4 parallel sets of 2 * stereo samples each during each
|
||||
// round to improve CPU-level parallellization.
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
__m64 temp;
|
||||
|
||||
// dictionary of instructions:
|
||||
// _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
|
||||
// _mm_add_pi32 : 2*32bit add
|
||||
// _m_psrad : 32bit right-shift
|
||||
|
||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
|
||||
_mm_madd_pi16(pVec1[1], pVec2[1]));
|
||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
||||
|
||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
|
||||
_mm_madd_pi16(pVec1[3], pVec2[3]));
|
||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
||||
|
||||
pVec1 += 4;
|
||||
pVec2 += 4;
|
||||
}
|
||||
|
||||
// copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
|
||||
// and finally store the result into the variable "corr"
|
||||
|
||||
accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
|
||||
corr = _m_to_int(accu);
|
||||
|
||||
// Clear MMS state
|
||||
_m_empty();
|
||||
|
||||
return corr;
|
||||
// Note: Warning about the missing EMMS instruction is harmless
|
||||
// as it'll be called elsewhere.
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TDStretchMMX::clearCrossCorrState()
|
||||
{
|
||||
// Clear MMS state
|
||||
_m_empty();
|
||||
//_asm EMMS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MMX-optimized version of the function overlapStereo
|
||||
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||
{
|
||||
const __m64 *pVinput, *pVMidBuf;
|
||||
__m64 *pVdest;
|
||||
__m64 mix1, mix2, adder, shifter;
|
||||
uint i;
|
||||
|
||||
pVinput = (const __m64*)input;
|
||||
pVMidBuf = (const __m64*)pMidBuffer;
|
||||
pVdest = (__m64*)output;
|
||||
|
||||
// mix1 = mixer values for 1st stereo sample
|
||||
// mix1 = mixer values for 2nd stereo sample
|
||||
// adder = adder for updating mixer values after each round
|
||||
|
||||
mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
|
||||
adder = _mm_set_pi16(1, -1, 1, -1);
|
||||
mix2 = _mm_add_pi16(mix1, adder);
|
||||
adder = _mm_add_pi16(adder, adder);
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
__m64 temp1, temp2;
|
||||
|
||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
|
||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
|
||||
|
||||
// temp = (temp .* mix) >> shifter
|
||||
temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
|
||||
temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
|
||||
pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
|
||||
|
||||
// update mix += adder
|
||||
mix1 = _mm_add_pi16(mix1, adder);
|
||||
mix2 = _mm_add_pi16(mix2, adder);
|
||||
|
||||
// --- second round begins here ---
|
||||
|
||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r
|
||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r
|
||||
|
||||
// temp = (temp .* mix) >> shifter
|
||||
temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
|
||||
temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
|
||||
pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
|
||||
|
||||
// update mix += adder
|
||||
mix1 = _mm_add_pi16(mix1, adder);
|
||||
mix2 = _mm_add_pi16(mix2, adder);
|
||||
|
||||
pVinput += 2;
|
||||
pVMidBuf += 2;
|
||||
pVdest += 2;
|
||||
}
|
||||
|
||||
_m_empty(); // clear MMS state
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
|
||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterMMX::~FIRFilterMMX()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for MMX routine
|
||||
void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||
filterCoeffsAlign = (short *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
||||
filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
|
||||
|
||||
filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
|
||||
filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// mmx-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
|
||||
{
|
||||
// Create stack copies of the needed member variables for asm routines :
|
||||
uint i, j;
|
||||
__m64 *pVdest = (__m64*)dest;
|
||||
|
||||
if (length < 2) return 0;
|
||||
|
||||
for (i = 0; i < numSamples / 2; i ++)
|
||||
{
|
||||
__m64 accu1;
|
||||
__m64 accu2;
|
||||
const __m64 *pVsrc = (const __m64*)src;
|
||||
const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;
|
||||
|
||||
accu1 = accu2 = _mm_setzero_si64();
|
||||
for (j = 0; j < lengthDiv8 * 2; j ++)
|
||||
{
|
||||
__m64 temp1, temp2;
|
||||
|
||||
temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0
|
||||
temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1
|
||||
|
||||
accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0
|
||||
accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1
|
||||
|
||||
temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2
|
||||
|
||||
accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0
|
||||
accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1
|
||||
|
||||
// accu1 += l2*f2+l0*f0 r2*f2+r0*f0
|
||||
// += l3*f3+l1*f1 r3*f3+r1*f1
|
||||
|
||||
// accu2 += l3*f2+l1*f0 r3*f2+r1*f0
|
||||
// l4*f3+l2*f1 r4*f3+r2*f1
|
||||
|
||||
pVfilter += 2;
|
||||
pVsrc += 2;
|
||||
}
|
||||
// accu >>= resultDivFactor
|
||||
accu1 = _mm_srai_pi32(accu1, resultDivFactor);
|
||||
accu2 = _mm_srai_pi32(accu2, resultDivFactor);
|
||||
|
||||
// pack 2*2*32bits => 4*16 bits
|
||||
pVdest[0] = _mm_packs_pi32(accu1, accu2);
|
||||
src += 4;
|
||||
pVdest ++;
|
||||
}
|
||||
|
||||
_m_empty(); // clear emms state
|
||||
|
||||
return (numSamples & 0xfffffffe) - length;
|
||||
}
|
||||
|
||||
#endif // ALLOW_MMX
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
||||
/// this single source code file, regardless to their class or original source
|
||||
/// code file, in order to ease porting the library to other compiler and
|
||||
/// processor platforms.
|
||||
///
|
||||
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
|
||||
/// is available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/06 18:52:43 $
|
||||
// File revision : $Revision: 1.1 $
|
||||
//
|
||||
// $Id: mmx_optimized.cpp,v 1.1 2006/02/06 18:52:43 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample type
|
||||
|
||||
#if !(WIN32 || __i386__ || __x86_64__)
|
||||
#error "wrong platform - this source code file is exclusively for x86 platforms"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'TDStretchMMX'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <mmintrin.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
||||
{
|
||||
const __m64 *pVec1, *pVec2;
|
||||
__m64 shifter;
|
||||
__m64 accu;
|
||||
long corr;
|
||||
uint i;
|
||||
|
||||
pVec1 = (__m64*)pV1;
|
||||
pVec2 = (__m64*)pV2;
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
accu = _mm_setzero_si64();
|
||||
|
||||
// Process 4 parallel sets of 2 * stereo samples each during each
|
||||
// round to improve CPU-level parallellization.
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
__m64 temp;
|
||||
|
||||
// dictionary of instructions:
|
||||
// _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
|
||||
// _mm_add_pi32 : 2*32bit add
|
||||
// _m_psrad : 32bit right-shift
|
||||
|
||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
|
||||
_mm_madd_pi16(pVec1[1], pVec2[1]));
|
||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
||||
|
||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
|
||||
_mm_madd_pi16(pVec1[3], pVec2[3]));
|
||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
||||
|
||||
pVec1 += 4;
|
||||
pVec2 += 4;
|
||||
}
|
||||
|
||||
// copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
|
||||
// and finally store the result into the variable "corr"
|
||||
|
||||
accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
|
||||
corr = _m_to_int(accu);
|
||||
|
||||
// Clear MMS state
|
||||
_m_empty();
|
||||
|
||||
return corr;
|
||||
// Note: Warning about the missing EMMS instruction is harmless
|
||||
// as it'll be called elsewhere.
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TDStretchMMX::clearCrossCorrState()
|
||||
{
|
||||
// Clear MMS state
|
||||
_m_empty();
|
||||
//_asm EMMS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MMX-optimized version of the function overlapStereo
|
||||
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||
{
|
||||
const __m64 *pVinput, *pVMidBuf;
|
||||
__m64 *pVdest;
|
||||
__m64 mix1, mix2, adder, shifter;
|
||||
uint i;
|
||||
|
||||
pVinput = (const __m64*)input;
|
||||
pVMidBuf = (const __m64*)pMidBuffer;
|
||||
pVdest = (__m64*)output;
|
||||
|
||||
// mix1 = mixer values for 1st stereo sample
|
||||
// mix1 = mixer values for 2nd stereo sample
|
||||
// adder = adder for updating mixer values after each round
|
||||
|
||||
mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
|
||||
adder = _mm_set_pi16(1, -1, 1, -1);
|
||||
mix2 = _mm_add_pi16(mix1, adder);
|
||||
adder = _mm_add_pi16(adder, adder);
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
__m64 temp1, temp2;
|
||||
|
||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
|
||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
|
||||
|
||||
// temp = (temp .* mix) >> shifter
|
||||
temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
|
||||
temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
|
||||
pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
|
||||
|
||||
// update mix += adder
|
||||
mix1 = _mm_add_pi16(mix1, adder);
|
||||
mix2 = _mm_add_pi16(mix2, adder);
|
||||
|
||||
// --- second round begins here ---
|
||||
|
||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r
|
||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r
|
||||
|
||||
// temp = (temp .* mix) >> shifter
|
||||
temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
|
||||
temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
|
||||
pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
|
||||
|
||||
// update mix += adder
|
||||
mix1 = _mm_add_pi16(mix1, adder);
|
||||
mix2 = _mm_add_pi16(mix2, adder);
|
||||
|
||||
pVinput += 2;
|
||||
pVMidBuf += 2;
|
||||
pVdest += 2;
|
||||
}
|
||||
|
||||
_m_empty(); // clear MMS state
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
|
||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterMMX::~FIRFilterMMX()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for MMX routine
|
||||
void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||
filterCoeffsAlign = (short *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
||||
filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
|
||||
|
||||
filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
|
||||
filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// mmx-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
|
||||
{
|
||||
// Create stack copies of the needed member variables for asm routines :
|
||||
uint i, j;
|
||||
__m64 *pVdest = (__m64*)dest;
|
||||
|
||||
if (length < 2) return 0;
|
||||
|
||||
for (i = 0; i < numSamples / 2; i ++)
|
||||
{
|
||||
__m64 accu1;
|
||||
__m64 accu2;
|
||||
const __m64 *pVsrc = (const __m64*)src;
|
||||
const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;
|
||||
|
||||
accu1 = accu2 = _mm_setzero_si64();
|
||||
for (j = 0; j < lengthDiv8 * 2; j ++)
|
||||
{
|
||||
__m64 temp1, temp2;
|
||||
|
||||
temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0
|
||||
temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1
|
||||
|
||||
accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0
|
||||
accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1
|
||||
|
||||
temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2
|
||||
|
||||
accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0
|
||||
accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1
|
||||
|
||||
// accu1 += l2*f2+l0*f0 r2*f2+r0*f0
|
||||
// += l3*f3+l1*f1 r3*f3+r1*f1
|
||||
|
||||
// accu2 += l3*f2+l1*f0 r3*f2+r1*f0
|
||||
// l4*f3+l2*f1 r4*f3+r2*f1
|
||||
|
||||
pVfilter += 2;
|
||||
pVsrc += 2;
|
||||
}
|
||||
// accu >>= resultDivFactor
|
||||
accu1 = _mm_srai_pi32(accu1, resultDivFactor);
|
||||
accu2 = _mm_srai_pi32(accu2, resultDivFactor);
|
||||
|
||||
// pack 2*2*32bits => 4*16 bits
|
||||
pVdest[0] = _mm_packs_pi32(accu1, accu2);
|
||||
src += 4;
|
||||
pVdest ++;
|
||||
}
|
||||
|
||||
_m_empty(); // clear emms state
|
||||
|
||||
return (numSamples & 0xfffffffe) - length;
|
||||
}
|
||||
|
||||
#endif // ALLOW_MMX
|
||||
|
@ -1,484 +1,484 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
||||
/// optimized functions have been gathered into this single source
|
||||
/// code file, regardless to their class or original source code file, in order
|
||||
/// to ease porting the library to other compiler and processor platforms.
|
||||
///
|
||||
/// The SSE-optimizations are programmed using SSE compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.2 $
|
||||
//
|
||||
// $Id: sse_optimized.cpp,v 1.2 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
|
||||
// SSE routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of SSE optimized functions of class 'TDStretchSSE'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <xmmintrin.h>
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint i;
|
||||
__m128 vSum, *pVec2;
|
||||
|
||||
// Note. It means a major slow-down if the routine needs to tolerate
|
||||
// unaligned __m128 memory accesses. It's way faster if we can skip
|
||||
// unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
|
||||
// This can mean up to ~ 10-fold difference (incl. part of which is
|
||||
// due to skipping every second round for stereo sound though).
|
||||
//
|
||||
// Compile-time define ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
||||
// for choosing if this little cheating is allowed.
|
||||
|
||||
#ifdef ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||
// Little cheating allowed, return valid correlation only for
|
||||
// aligned locations, meaning every second round for stereo sound.
|
||||
|
||||
#define _MM_LOAD _mm_load_ps
|
||||
|
||||
if (((ulong)pV1) & 15) return -1e50; // skip unaligned locations
|
||||
|
||||
#else
|
||||
// No cheating allowed, use unaligned load & take the resulting
|
||||
// performance hit.
|
||||
#define _MM_LOAD _mm_loadu_ps
|
||||
#endif
|
||||
|
||||
// ensure overlapLength is divisible by 8
|
||||
assert((overlapLength % 8) == 0);
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
// Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
|
||||
pVec2 = (__m128*)pV2;
|
||||
vSum = _mm_setzero_ps();
|
||||
|
||||
// Unroll the loop by factor of 4 * 4 operations
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
// vSum += pV1[0..3] * pV2[0..3]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1),pVec2[0]));
|
||||
|
||||
// vSum += pV1[4..7] * pV2[4..7]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 4), pVec2[1]));
|
||||
|
||||
// vSum += pV1[8..11] * pV2[8..11]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 8), pVec2[2]));
|
||||
|
||||
// vSum += pV1[12..15] * pV2[12..15]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 12), pVec2[3]));
|
||||
|
||||
pV1 += 16;
|
||||
pVec2 += 4;
|
||||
}
|
||||
|
||||
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
|
||||
float *pvSum = (float*)&vSum;
|
||||
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]);
|
||||
|
||||
/* This is approximately corresponding routine in C-language:
|
||||
double corr;
|
||||
uint i;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
corr = 0.0;
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0] +
|
||||
pV1[1] * pV2[1] +
|
||||
pV1[2] * pV2[2] +
|
||||
pV1[3] * pV2[3] +
|
||||
pV1[4] * pV2[4] +
|
||||
pV1[5] * pV2[5] +
|
||||
pV1[6] * pV2[6] +
|
||||
pV1[7] * pV2[7] +
|
||||
pV1[8] * pV2[8] +
|
||||
pV1[9] * pV2[9] +
|
||||
pV1[10] * pV2[10] +
|
||||
pV1[11] * pV2[11] +
|
||||
pV1[12] * pV2[12] +
|
||||
pV1[13] * pV2[13] +
|
||||
pV1[14] * pV2[14] +
|
||||
pV1[15] * pV2[15];
|
||||
|
||||
pV1 += 16;
|
||||
pV2 += 16;
|
||||
}
|
||||
*/
|
||||
|
||||
/* This is corresponding routine in assembler. This may be teeny-weeny bit faster
|
||||
than intrinsic version, but more difficult to maintain & get compiled on multiple
|
||||
platforms.
|
||||
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'pV2' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
// give more aggressive hints on pV1 as that changes while pV2 stays
|
||||
// same between runs
|
||||
prefetcht0 [pV1]
|
||||
prefetcht0 [pV2]
|
||||
prefetcht0 [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV1
|
||||
mov ebx, dword ptr pV2
|
||||
|
||||
xorps xmm0, xmm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 3 // div by eight
|
||||
|
||||
loop1:
|
||||
prefetcht0 [eax + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
movups xmm1, [eax]
|
||||
mulps xmm1, [ebx]
|
||||
addps xmm0, xmm1
|
||||
|
||||
movups xmm2, [eax + 16]
|
||||
mulps xmm2, [ebx + 16]
|
||||
addps xmm0, xmm2
|
||||
|
||||
prefetcht0 [eax + 96] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm3, [eax + 32]
|
||||
mulps xmm3, [ebx + 32]
|
||||
addps xmm0, xmm3
|
||||
|
||||
movups xmm4, [eax + 48]
|
||||
mulps xmm4, [ebx + 48]
|
||||
addps xmm0, xmm4
|
||||
|
||||
add eax, 64
|
||||
add ebx, 64
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add the four floats of xmm0 together and return the result.
|
||||
|
||||
movhlps xmm1, xmm0 // move 3 & 4 of xmm0 to 1 & 2 of xmm1
|
||||
addps xmm1, xmm0
|
||||
movaps xmm2, xmm1
|
||||
shufps xmm2, xmm2, 0x01 // move 2 of xmm2 as 1 of xmm2
|
||||
addss xmm2, xmm1
|
||||
movss corr, xmm2
|
||||
}
|
||||
|
||||
return (double)corr;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of SSE optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterSSE::~FIRFilterSSE()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for SSE routine
|
||||
void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
float fDivider;
|
||||
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SSE-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
|
||||
{
|
||||
int count = (numSamples - length) & -2;
|
||||
int j;
|
||||
|
||||
assert(count % 2 == 0);
|
||||
|
||||
if (count < 2) return 0;
|
||||
|
||||
assert((length % 8) == 0);
|
||||
assert(((unsigned long)filterCoeffsAlign) % 16 == 0);
|
||||
|
||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *pSrc;
|
||||
const __m128 *pFil;
|
||||
__m128 sum1, sum2;
|
||||
uint i;
|
||||
|
||||
pSrc = source; // source audio data
|
||||
pFil = (__m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||
// are aligned to 16-byte boundary
|
||||
sum1 = sum2 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < length / 8; i ++)
|
||||
{
|
||||
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples
|
||||
// at each pass
|
||||
|
||||
// sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
|
||||
// sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset.
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3]));
|
||||
|
||||
pSrc += 16;
|
||||
pFil += 4;
|
||||
}
|
||||
|
||||
// Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
|
||||
// to sum the two hi- and lo-floats of these registers together.
|
||||
|
||||
// post-shuffle & add the filtered values and store to dest.
|
||||
_mm_storeu_ps(dest, _mm_add_ps(
|
||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
|
||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
|
||||
));
|
||||
source += 4;
|
||||
dest += 4;
|
||||
}
|
||||
|
||||
// Ideas for further improvement:
|
||||
// 1. If it could be guaranteed that 'source' were always aligned to 16-byte
|
||||
// boundary, a faster aligned '_mm_load_ps' instruction could be used.
|
||||
// 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
|
||||
// boundary, a faster '_mm_store_ps' instruction could be used.
|
||||
|
||||
return (uint)count;
|
||||
|
||||
/* original routine in C-language. please notice the C-version has differently
|
||||
organized coefficients though.
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
uint i, j;
|
||||
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *ptr;
|
||||
const float *pFil;
|
||||
|
||||
suml1 = sumr1 = 0.0;
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
pFil = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * pFil[0] +
|
||||
ptr[2] * pFil[2] +
|
||||
ptr[4] * pFil[4] +
|
||||
ptr[6] * pFil[6];
|
||||
|
||||
sumr1 += ptr[1] * pFil[1] +
|
||||
ptr[3] * pFil[3] +
|
||||
ptr[5] * pFil[5] +
|
||||
ptr[7] * pFil[7];
|
||||
|
||||
suml2 += ptr[8] * pFil[0] +
|
||||
ptr[10] * pFil[2] +
|
||||
ptr[12] * pFil[4] +
|
||||
ptr[14] * pFil[6];
|
||||
|
||||
sumr2 += ptr[9] * pFil[1] +
|
||||
ptr[11] * pFil[3] +
|
||||
ptr[13] * pFil[5] +
|
||||
ptr[15] * pFil[7];
|
||||
|
||||
ptr += 16;
|
||||
pFil += 8;
|
||||
}
|
||||
dest[0] = (float)suml1;
|
||||
dest[1] = (float)sumr1;
|
||||
dest[2] = (float)suml2;
|
||||
dest[3] = (float)sumr2;
|
||||
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* Similar routine in assembly, again obsoleted due to maintainability
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'src' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
mov edx, count
|
||||
mov ebx, dword ptr src
|
||||
mov eax, dword ptr dest
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
prefetcht0 [ebx]
|
||||
prefetcht0 [filterCoeffsLocal]
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
xorps xmm0, xmm0
|
||||
xorps xmm1, xmm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round eight FIR filter taps are evaluated for 2*2 samples
|
||||
prefetcht0 [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm2, [esi] // possibly unaligned load
|
||||
movups xmm3, [esi + 8] // possibly unaligned load
|
||||
mulps xmm2, [edi]
|
||||
mulps xmm3, [edi]
|
||||
addps xmm0, xmm2
|
||||
addps xmm1, xmm3
|
||||
|
||||
movups xmm4, [esi + 16] // possibly unaligned load
|
||||
movups xmm5, [esi + 24] // possibly unaligned load
|
||||
mulps xmm4, [edi + 16]
|
||||
mulps xmm5, [edi + 16]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
prefetcht0 [esi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm6, [esi + 32] // possibly unaligned load
|
||||
movups xmm7, [esi + 40] // possibly unaligned load
|
||||
mulps xmm6, [edi + 32]
|
||||
mulps xmm7, [edi + 32]
|
||||
addps xmm0, xmm6
|
||||
addps xmm1, xmm7
|
||||
|
||||
movups xmm4, [esi + 48] // possibly unaligned load
|
||||
movups xmm5, [esi + 56] // possibly unaligned load
|
||||
mulps xmm4, [edi + 48]
|
||||
mulps xmm5, [edi + 48]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
add esi, 64
|
||||
add edi, 64
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
// Now xmm0 and xmm1 both have a filtered 2-channel sample each, but we still need
|
||||
// to sum the two hi- and lo-floats of these registers together.
|
||||
|
||||
movhlps xmm2, xmm0 // xmm2 = xmm2_3 xmm2_2 xmm0_3 xmm0_2
|
||||
movlhps xmm2, xmm1 // xmm2 = xmm1_1 xmm1_0 xmm0_3 xmm0_2
|
||||
shufps xmm0, xmm1, 0xe4 // xmm0 = xmm1_3 xmm1_2 xmm0_1 xmm0_0
|
||||
addps xmm0, xmm2
|
||||
|
||||
movaps [eax], xmm0
|
||||
add ebx, 16
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // ALLOW_SSE
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
||||
/// optimized functions have been gathered into this single source
|
||||
/// code file, regardless to their class or original source code file, in order
|
||||
/// to ease porting the library to other compiler and processor platforms.
|
||||
///
|
||||
/// The SSE-optimizations are programmed using SSE compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.2 $
|
||||
//
|
||||
// $Id: sse_optimized.cpp,v 1.2 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
|
||||
// SSE routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of SSE optimized functions of class 'TDStretchSSE'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <xmmintrin.h>
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint i;
|
||||
__m128 vSum, *pVec2;
|
||||
|
||||
// Note. It means a major slow-down if the routine needs to tolerate
|
||||
// unaligned __m128 memory accesses. It's way faster if we can skip
|
||||
// unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
|
||||
// This can mean up to ~ 10-fold difference (incl. part of which is
|
||||
// due to skipping every second round for stereo sound though).
|
||||
//
|
||||
// Compile-time define ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
||||
// for choosing if this little cheating is allowed.
|
||||
|
||||
#ifdef ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||
// Little cheating allowed, return valid correlation only for
|
||||
// aligned locations, meaning every second round for stereo sound.
|
||||
|
||||
#define _MM_LOAD _mm_load_ps
|
||||
|
||||
if (((ulong)pV1) & 15) return -1e50; // skip unaligned locations
|
||||
|
||||
#else
|
||||
// No cheating allowed, use unaligned load & take the resulting
|
||||
// performance hit.
|
||||
#define _MM_LOAD _mm_loadu_ps
|
||||
#endif
|
||||
|
||||
// ensure overlapLength is divisible by 8
|
||||
assert((overlapLength % 8) == 0);
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
// Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
|
||||
pVec2 = (__m128*)pV2;
|
||||
vSum = _mm_setzero_ps();
|
||||
|
||||
// Unroll the loop by factor of 4 * 4 operations
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
// vSum += pV1[0..3] * pV2[0..3]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1),pVec2[0]));
|
||||
|
||||
// vSum += pV1[4..7] * pV2[4..7]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 4), pVec2[1]));
|
||||
|
||||
// vSum += pV1[8..11] * pV2[8..11]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 8), pVec2[2]));
|
||||
|
||||
// vSum += pV1[12..15] * pV2[12..15]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 12), pVec2[3]));
|
||||
|
||||
pV1 += 16;
|
||||
pVec2 += 4;
|
||||
}
|
||||
|
||||
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
|
||||
float *pvSum = (float*)&vSum;
|
||||
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]);
|
||||
|
||||
/* This is approximately corresponding routine in C-language:
|
||||
double corr;
|
||||
uint i;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
corr = 0.0;
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0] +
|
||||
pV1[1] * pV2[1] +
|
||||
pV1[2] * pV2[2] +
|
||||
pV1[3] * pV2[3] +
|
||||
pV1[4] * pV2[4] +
|
||||
pV1[5] * pV2[5] +
|
||||
pV1[6] * pV2[6] +
|
||||
pV1[7] * pV2[7] +
|
||||
pV1[8] * pV2[8] +
|
||||
pV1[9] * pV2[9] +
|
||||
pV1[10] * pV2[10] +
|
||||
pV1[11] * pV2[11] +
|
||||
pV1[12] * pV2[12] +
|
||||
pV1[13] * pV2[13] +
|
||||
pV1[14] * pV2[14] +
|
||||
pV1[15] * pV2[15];
|
||||
|
||||
pV1 += 16;
|
||||
pV2 += 16;
|
||||
}
|
||||
*/
|
||||
|
||||
/* This is corresponding routine in assembler. This may be teeny-weeny bit faster
|
||||
than intrinsic version, but more difficult to maintain & get compiled on multiple
|
||||
platforms.
|
||||
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'pV2' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
// give more aggressive hints on pV1 as that changes while pV2 stays
|
||||
// same between runs
|
||||
prefetcht0 [pV1]
|
||||
prefetcht0 [pV2]
|
||||
prefetcht0 [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV1
|
||||
mov ebx, dword ptr pV2
|
||||
|
||||
xorps xmm0, xmm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 3 // div by eight
|
||||
|
||||
loop1:
|
||||
prefetcht0 [eax + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
movups xmm1, [eax]
|
||||
mulps xmm1, [ebx]
|
||||
addps xmm0, xmm1
|
||||
|
||||
movups xmm2, [eax + 16]
|
||||
mulps xmm2, [ebx + 16]
|
||||
addps xmm0, xmm2
|
||||
|
||||
prefetcht0 [eax + 96] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm3, [eax + 32]
|
||||
mulps xmm3, [ebx + 32]
|
||||
addps xmm0, xmm3
|
||||
|
||||
movups xmm4, [eax + 48]
|
||||
mulps xmm4, [ebx + 48]
|
||||
addps xmm0, xmm4
|
||||
|
||||
add eax, 64
|
||||
add ebx, 64
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add the four floats of xmm0 together and return the result.
|
||||
|
||||
movhlps xmm1, xmm0 // move 3 & 4 of xmm0 to 1 & 2 of xmm1
|
||||
addps xmm1, xmm0
|
||||
movaps xmm2, xmm1
|
||||
shufps xmm2, xmm2, 0x01 // move 2 of xmm2 as 1 of xmm2
|
||||
addss xmm2, xmm1
|
||||
movss corr, xmm2
|
||||
}
|
||||
|
||||
return (double)corr;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of SSE optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterSSE::~FIRFilterSSE()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for SSE routine
|
||||
void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
float fDivider;
|
||||
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SSE-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
|
||||
{
|
||||
int count = (numSamples - length) & -2;
|
||||
int j;
|
||||
|
||||
assert(count % 2 == 0);
|
||||
|
||||
if (count < 2) return 0;
|
||||
|
||||
assert((length % 8) == 0);
|
||||
assert(((unsigned long)filterCoeffsAlign) % 16 == 0);
|
||||
|
||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *pSrc;
|
||||
const __m128 *pFil;
|
||||
__m128 sum1, sum2;
|
||||
uint i;
|
||||
|
||||
pSrc = source; // source audio data
|
||||
pFil = (__m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||
// are aligned to 16-byte boundary
|
||||
sum1 = sum2 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < length / 8; i ++)
|
||||
{
|
||||
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples
|
||||
// at each pass
|
||||
|
||||
// sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
|
||||
// sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset.
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3]));
|
||||
|
||||
pSrc += 16;
|
||||
pFil += 4;
|
||||
}
|
||||
|
||||
// Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
|
||||
// to sum the two hi- and lo-floats of these registers together.
|
||||
|
||||
// post-shuffle & add the filtered values and store to dest.
|
||||
_mm_storeu_ps(dest, _mm_add_ps(
|
||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
|
||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
|
||||
));
|
||||
source += 4;
|
||||
dest += 4;
|
||||
}
|
||||
|
||||
// Ideas for further improvement:
|
||||
// 1. If it could be guaranteed that 'source' were always aligned to 16-byte
|
||||
// boundary, a faster aligned '_mm_load_ps' instruction could be used.
|
||||
// 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
|
||||
// boundary, a faster '_mm_store_ps' instruction could be used.
|
||||
|
||||
return (uint)count;
|
||||
|
||||
/* original routine in C-language. please notice the C-version has differently
|
||||
organized coefficients though.
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
uint i, j;
|
||||
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *ptr;
|
||||
const float *pFil;
|
||||
|
||||
suml1 = sumr1 = 0.0;
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
pFil = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * pFil[0] +
|
||||
ptr[2] * pFil[2] +
|
||||
ptr[4] * pFil[4] +
|
||||
ptr[6] * pFil[6];
|
||||
|
||||
sumr1 += ptr[1] * pFil[1] +
|
||||
ptr[3] * pFil[3] +
|
||||
ptr[5] * pFil[5] +
|
||||
ptr[7] * pFil[7];
|
||||
|
||||
suml2 += ptr[8] * pFil[0] +
|
||||
ptr[10] * pFil[2] +
|
||||
ptr[12] * pFil[4] +
|
||||
ptr[14] * pFil[6];
|
||||
|
||||
sumr2 += ptr[9] * pFil[1] +
|
||||
ptr[11] * pFil[3] +
|
||||
ptr[13] * pFil[5] +
|
||||
ptr[15] * pFil[7];
|
||||
|
||||
ptr += 16;
|
||||
pFil += 8;
|
||||
}
|
||||
dest[0] = (float)suml1;
|
||||
dest[1] = (float)sumr1;
|
||||
dest[2] = (float)suml2;
|
||||
dest[3] = (float)sumr2;
|
||||
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* Similar routine in assembly, again obsoleted due to maintainability
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'src' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
mov edx, count
|
||||
mov ebx, dword ptr src
|
||||
mov eax, dword ptr dest
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
prefetcht0 [ebx]
|
||||
prefetcht0 [filterCoeffsLocal]
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
xorps xmm0, xmm0
|
||||
xorps xmm1, xmm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round eight FIR filter taps are evaluated for 2*2 samples
|
||||
prefetcht0 [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm2, [esi] // possibly unaligned load
|
||||
movups xmm3, [esi + 8] // possibly unaligned load
|
||||
mulps xmm2, [edi]
|
||||
mulps xmm3, [edi]
|
||||
addps xmm0, xmm2
|
||||
addps xmm1, xmm3
|
||||
|
||||
movups xmm4, [esi + 16] // possibly unaligned load
|
||||
movups xmm5, [esi + 24] // possibly unaligned load
|
||||
mulps xmm4, [edi + 16]
|
||||
mulps xmm5, [edi + 16]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
prefetcht0 [esi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm6, [esi + 32] // possibly unaligned load
|
||||
movups xmm7, [esi + 40] // possibly unaligned load
|
||||
mulps xmm6, [edi + 32]
|
||||
mulps xmm7, [edi + 32]
|
||||
addps xmm0, xmm6
|
||||
addps xmm1, xmm7
|
||||
|
||||
movups xmm4, [esi + 48] // possibly unaligned load
|
||||
movups xmm5, [esi + 56] // possibly unaligned load
|
||||
mulps xmm4, [edi + 48]
|
||||
mulps xmm5, [edi + 48]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
add esi, 64
|
||||
add edi, 64
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
// Now xmm0 and xmm1 both have a filtered 2-channel sample each, but we still need
|
||||
// to sum the two hi- and lo-floats of these registers together.
|
||||
|
||||
movhlps xmm2, xmm0 // xmm2 = xmm2_3 xmm2_2 xmm0_3 xmm0_2
|
||||
movlhps xmm2, xmm1 // xmm2 = xmm1_1 xmm1_0 xmm0_3 xmm0_2
|
||||
shufps xmm0, xmm1, 0xe4 // xmm0 = xmm1_3 xmm1_2 xmm0_1 xmm0_0
|
||||
addps xmm0, xmm2
|
||||
|
||||
movaps [eax], xmm0
|
||||
add ebx, 16
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // ALLOW_SSE
|
||||
|
@ -1,293 +1,293 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A class for parsing the 'soundstretch' application command line parameters
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.8 $
|
||||
//
|
||||
// $Id: RunParameters.cpp,v 1.8 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "RunParameters.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Program usage instructions
|
||||
|
||||
static const char licenseText[] =
|
||||
" LICENSE:\n"
|
||||
" ========\n"
|
||||
" \n"
|
||||
" SoundTouch sound processing library\n"
|
||||
" Copyright (c) Olli Parviainen\n"
|
||||
" \n"
|
||||
" This library is free software; you can redistribute it and/or\n"
|
||||
" modify it under the terms of the GNU Lesser General Public\n"
|
||||
" License as published by the Free Software Foundation; either\n"
|
||||
" version 2.1 of the License, or (at your option) any later version.\n"
|
||||
" \n"
|
||||
" This library is distributed in the hope that it will be useful,\n"
|
||||
" but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
|
||||
" Lesser General Public License for more details.\n"
|
||||
" \n"
|
||||
" You should have received a copy of the GNU Lesser General Public\n"
|
||||
" License along with this library; if not, write to the Free Software\n"
|
||||
" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
|
||||
" \n"
|
||||
"This application is distributed with full source codes; however, if you\n"
|
||||
"didn't receive them, please visit the author's homepage (see the link above).";
|
||||
|
||||
static const char whatText[] =
|
||||
"This application processes WAV audio files by modifying the sound tempo,\n"
|
||||
"pitch and playback rate properties independently from each other.\n"
|
||||
"\n";
|
||||
|
||||
static const char usage[] =
|
||||
"Usage :\n"
|
||||
" soundstretch infile.wav outfile.wav [switches]\n\n"
|
||||
"Available switches are:\n"
|
||||
" -tempo=n : Change sound tempo by n percents (n=-95..+5000 %)\n"
|
||||
" -pitch=n : Change sound pitch by n semitones (n=-60..+60 semitones)\n"
|
||||
" -rate=n : Change sound rate by n percents (n=-95..+5000 %)\n"
|
||||
" -bpm=n : Detect the BPM rate of sound and adjust tempo to meet 'n' BPMs.\n"
|
||||
" If '=n' is omitted, just detects the BPM rate.\n"
|
||||
" -quick : Use quicker tempo change algorithm (gain speed, lose quality)\n"
|
||||
" -naa : Don't use anti-alias filtering (gain speed, lose quality)\n"
|
||||
" -license : Display the program license text (LGPL)\n";
|
||||
|
||||
|
||||
// Converts a char into lower case
|
||||
static int _toLowerCase(int c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
c += 'a' - 'A';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
// Constructor
|
||||
RunParameters::RunParameters(const int nParams, const char *paramStr[])
|
||||
{
|
||||
int i;
|
||||
int nFirstParam;
|
||||
|
||||
if (nParams < 3)
|
||||
{
|
||||
// Too few parameters
|
||||
if (nParams > 1 && paramStr[1][0] == '-' &&
|
||||
_toLowerCase(paramStr[1][1]) == 'l')
|
||||
{
|
||||
// '-license' switch
|
||||
throwLicense();
|
||||
}
|
||||
string msg = whatText;
|
||||
msg += usage;
|
||||
throw runtime_error(msg.c_str());
|
||||
}
|
||||
|
||||
inFileName = NULL;
|
||||
outFileName = NULL;
|
||||
tempoDelta = 0;
|
||||
pitchDelta = 0;
|
||||
rateDelta = 0;
|
||||
quick = 0;
|
||||
noAntiAlias = 0;
|
||||
goalBPM = 0;
|
||||
detectBPM = FALSE;
|
||||
|
||||
// Get input & output file names
|
||||
inFileName = (char*)paramStr[1];
|
||||
outFileName = (char*)paramStr[2];
|
||||
|
||||
if (outFileName[0] == '-')
|
||||
{
|
||||
// no outputfile name was given but parameters
|
||||
outFileName = NULL;
|
||||
nFirstParam = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
nFirstParam = 3;
|
||||
}
|
||||
|
||||
// parse switch parameters
|
||||
for (i = nFirstParam; i < nParams; i ++)
|
||||
{
|
||||
parseSwitchParam(paramStr[i]);
|
||||
}
|
||||
|
||||
checkLimits();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Checks parameter limits
|
||||
void RunParameters::checkLimits()
|
||||
{
|
||||
if (tempoDelta < -95.0f)
|
||||
{
|
||||
tempoDelta = -95.0f;
|
||||
}
|
||||
else if (tempoDelta > 5000.0f)
|
||||
{
|
||||
tempoDelta = 5000.0f;
|
||||
}
|
||||
|
||||
if (pitchDelta < -60.0f)
|
||||
{
|
||||
pitchDelta = -60.0f;
|
||||
}
|
||||
else if (pitchDelta > 60.0f)
|
||||
{
|
||||
pitchDelta = 60.0f;
|
||||
}
|
||||
|
||||
if (rateDelta < -95.0f)
|
||||
{
|
||||
rateDelta = -95.0f;
|
||||
}
|
||||
else if (rateDelta > 5000.0f)
|
||||
{
|
||||
rateDelta = 5000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Unknown switch parameter -- throws an exception with an error message
|
||||
void RunParameters::throwIllegalParamExp(const string &str) const
|
||||
{
|
||||
string msg = "ERROR : Illegal parameter \"";
|
||||
msg += str;
|
||||
msg += "\".\n\n";
|
||||
msg += usage;
|
||||
throw runtime_error(msg.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RunParameters::throwLicense() const
|
||||
{
|
||||
throw runtime_error(licenseText);
|
||||
}
|
||||
|
||||
|
||||
float RunParameters::parseSwitchValue(const string &str) const
|
||||
{
|
||||
int pos;
|
||||
|
||||
pos = str.find_first_of('=');
|
||||
if (pos < 0)
|
||||
{
|
||||
// '=' missing
|
||||
throwIllegalParamExp(str);
|
||||
}
|
||||
|
||||
// Read numerical parameter value after '='
|
||||
return (float)atof(str.substr(pos + 1).c_str());
|
||||
}
|
||||
|
||||
|
||||
// Interprets a single switch parameter string of format "-switch=xx"
|
||||
// Valid switches are "-tempo=xx", "-pitch=xx" and "-rate=xx". Stores
|
||||
// switch values into 'params' structure.
|
||||
void RunParameters::parseSwitchParam(const string &str)
|
||||
{
|
||||
int upS;
|
||||
|
||||
if (str[0] != '-')
|
||||
{
|
||||
// leading hyphen missing => not a valid parameter
|
||||
throwIllegalParamExp(str);
|
||||
}
|
||||
|
||||
// Take the first character of switch name & change to lower case
|
||||
upS = _toLowerCase(str[1]);
|
||||
|
||||
// interpret the switch name & operate accordingly
|
||||
switch (upS)
|
||||
{
|
||||
case 't' :
|
||||
// switch '-tempo=xx'
|
||||
tempoDelta = parseSwitchValue(str);
|
||||
break;
|
||||
|
||||
case 'p' :
|
||||
// switch '-pitch=xx'
|
||||
pitchDelta = parseSwitchValue(str);
|
||||
break;
|
||||
|
||||
case 'r' :
|
||||
// switch '-rate=xx'
|
||||
rateDelta = parseSwitchValue(str);
|
||||
break;
|
||||
|
||||
case 'b' :
|
||||
// switch '-bpm=xx'
|
||||
detectBPM = TRUE;
|
||||
try
|
||||
{
|
||||
goalBPM = parseSwitchValue(str);
|
||||
}
|
||||
catch (runtime_error)
|
||||
{
|
||||
// illegal or missing bpm value => just calculate bpm
|
||||
goalBPM = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'q' :
|
||||
// switch '-quick'
|
||||
quick = 1;
|
||||
break;
|
||||
|
||||
case 'n' :
|
||||
// switch '-naa'
|
||||
noAntiAlias = 1;
|
||||
break;
|
||||
|
||||
case 'l' :
|
||||
// switch '-license'
|
||||
throwLicense();
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown switch
|
||||
throwIllegalParamExp(str);
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A class for parsing the 'soundstretch' application command line parameters
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.8 $
|
||||
//
|
||||
// $Id: RunParameters.cpp,v 1.8 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "RunParameters.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Program usage instructions
|
||||
|
||||
static const char licenseText[] =
|
||||
" LICENSE:\n"
|
||||
" ========\n"
|
||||
" \n"
|
||||
" SoundTouch sound processing library\n"
|
||||
" Copyright (c) Olli Parviainen\n"
|
||||
" \n"
|
||||
" This library is free software; you can redistribute it and/or\n"
|
||||
" modify it under the terms of the GNU Lesser General Public\n"
|
||||
" License as published by the Free Software Foundation; either\n"
|
||||
" version 2.1 of the License, or (at your option) any later version.\n"
|
||||
" \n"
|
||||
" This library is distributed in the hope that it will be useful,\n"
|
||||
" but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
|
||||
" Lesser General Public License for more details.\n"
|
||||
" \n"
|
||||
" You should have received a copy of the GNU Lesser General Public\n"
|
||||
" License along with this library; if not, write to the Free Software\n"
|
||||
" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
|
||||
" \n"
|
||||
"This application is distributed with full source codes; however, if you\n"
|
||||
"didn't receive them, please visit the author's homepage (see the link above).";
|
||||
|
||||
static const char whatText[] =
|
||||
"This application processes WAV audio files by modifying the sound tempo,\n"
|
||||
"pitch and playback rate properties independently from each other.\n"
|
||||
"\n";
|
||||
|
||||
static const char usage[] =
|
||||
"Usage :\n"
|
||||
" soundstretch infile.wav outfile.wav [switches]\n\n"
|
||||
"Available switches are:\n"
|
||||
" -tempo=n : Change sound tempo by n percents (n=-95..+5000 %)\n"
|
||||
" -pitch=n : Change sound pitch by n semitones (n=-60..+60 semitones)\n"
|
||||
" -rate=n : Change sound rate by n percents (n=-95..+5000 %)\n"
|
||||
" -bpm=n : Detect the BPM rate of sound and adjust tempo to meet 'n' BPMs.\n"
|
||||
" If '=n' is omitted, just detects the BPM rate.\n"
|
||||
" -quick : Use quicker tempo change algorithm (gain speed, lose quality)\n"
|
||||
" -naa : Don't use anti-alias filtering (gain speed, lose quality)\n"
|
||||
" -license : Display the program license text (LGPL)\n";
|
||||
|
||||
|
||||
// Converts a char into lower case
|
||||
static int _toLowerCase(int c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
c += 'a' - 'A';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
// Constructor
|
||||
RunParameters::RunParameters(const int nParams, const char *paramStr[])
|
||||
{
|
||||
int i;
|
||||
int nFirstParam;
|
||||
|
||||
if (nParams < 3)
|
||||
{
|
||||
// Too few parameters
|
||||
if (nParams > 1 && paramStr[1][0] == '-' &&
|
||||
_toLowerCase(paramStr[1][1]) == 'l')
|
||||
{
|
||||
// '-license' switch
|
||||
throwLicense();
|
||||
}
|
||||
string msg = whatText;
|
||||
msg += usage;
|
||||
throw runtime_error(msg.c_str());
|
||||
}
|
||||
|
||||
inFileName = NULL;
|
||||
outFileName = NULL;
|
||||
tempoDelta = 0;
|
||||
pitchDelta = 0;
|
||||
rateDelta = 0;
|
||||
quick = 0;
|
||||
noAntiAlias = 0;
|
||||
goalBPM = 0;
|
||||
detectBPM = FALSE;
|
||||
|
||||
// Get input & output file names
|
||||
inFileName = (char*)paramStr[1];
|
||||
outFileName = (char*)paramStr[2];
|
||||
|
||||
if (outFileName[0] == '-')
|
||||
{
|
||||
// no outputfile name was given but parameters
|
||||
outFileName = NULL;
|
||||
nFirstParam = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
nFirstParam = 3;
|
||||
}
|
||||
|
||||
// parse switch parameters
|
||||
for (i = nFirstParam; i < nParams; i ++)
|
||||
{
|
||||
parseSwitchParam(paramStr[i]);
|
||||
}
|
||||
|
||||
checkLimits();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Checks parameter limits
|
||||
void RunParameters::checkLimits()
|
||||
{
|
||||
if (tempoDelta < -95.0f)
|
||||
{
|
||||
tempoDelta = -95.0f;
|
||||
}
|
||||
else if (tempoDelta > 5000.0f)
|
||||
{
|
||||
tempoDelta = 5000.0f;
|
||||
}
|
||||
|
||||
if (pitchDelta < -60.0f)
|
||||
{
|
||||
pitchDelta = -60.0f;
|
||||
}
|
||||
else if (pitchDelta > 60.0f)
|
||||
{
|
||||
pitchDelta = 60.0f;
|
||||
}
|
||||
|
||||
if (rateDelta < -95.0f)
|
||||
{
|
||||
rateDelta = -95.0f;
|
||||
}
|
||||
else if (rateDelta > 5000.0f)
|
||||
{
|
||||
rateDelta = 5000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Unknown switch parameter -- throws an exception with an error message
|
||||
void RunParameters::throwIllegalParamExp(const string &str) const
|
||||
{
|
||||
string msg = "ERROR : Illegal parameter \"";
|
||||
msg += str;
|
||||
msg += "\".\n\n";
|
||||
msg += usage;
|
||||
throw runtime_error(msg.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RunParameters::throwLicense() const
|
||||
{
|
||||
throw runtime_error(licenseText);
|
||||
}
|
||||
|
||||
|
||||
float RunParameters::parseSwitchValue(const string &str) const
|
||||
{
|
||||
int pos;
|
||||
|
||||
pos = str.find_first_of('=');
|
||||
if (pos < 0)
|
||||
{
|
||||
// '=' missing
|
||||
throwIllegalParamExp(str);
|
||||
}
|
||||
|
||||
// Read numerical parameter value after '='
|
||||
return (float)atof(str.substr(pos + 1).c_str());
|
||||
}
|
||||
|
||||
|
||||
// Interprets a single switch parameter string of format "-switch=xx"
|
||||
// Valid switches are "-tempo=xx", "-pitch=xx" and "-rate=xx". Stores
|
||||
// switch values into 'params' structure.
|
||||
void RunParameters::parseSwitchParam(const string &str)
|
||||
{
|
||||
int upS;
|
||||
|
||||
if (str[0] != '-')
|
||||
{
|
||||
// leading hyphen missing => not a valid parameter
|
||||
throwIllegalParamExp(str);
|
||||
}
|
||||
|
||||
// Take the first character of switch name & change to lower case
|
||||
upS = _toLowerCase(str[1]);
|
||||
|
||||
// interpret the switch name & operate accordingly
|
||||
switch (upS)
|
||||
{
|
||||
case 't' :
|
||||
// switch '-tempo=xx'
|
||||
tempoDelta = parseSwitchValue(str);
|
||||
break;
|
||||
|
||||
case 'p' :
|
||||
// switch '-pitch=xx'
|
||||
pitchDelta = parseSwitchValue(str);
|
||||
break;
|
||||
|
||||
case 'r' :
|
||||
// switch '-rate=xx'
|
||||
rateDelta = parseSwitchValue(str);
|
||||
break;
|
||||
|
||||
case 'b' :
|
||||
// switch '-bpm=xx'
|
||||
detectBPM = TRUE;
|
||||
try
|
||||
{
|
||||
goalBPM = parseSwitchValue(str);
|
||||
}
|
||||
catch (runtime_error)
|
||||
{
|
||||
// illegal or missing bpm value => just calculate bpm
|
||||
goalBPM = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'q' :
|
||||
// switch '-quick'
|
||||
quick = 1;
|
||||
break;
|
||||
|
||||
case 'n' :
|
||||
// switch '-naa'
|
||||
noAntiAlias = 1;
|
||||
break;
|
||||
|
||||
case 'l' :
|
||||
// switch '-license'
|
||||
throwLicense();
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown switch
|
||||
throwIllegalParamExp(str);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,288 +1,288 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundStretch main routine.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.20 $
|
||||
//
|
||||
// $Id: main.cpp,v 1.20 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include "RunParameters.h"
|
||||
#include "WavFile.h"
|
||||
#include "SoundTouch.h"
|
||||
#include "BPMDetect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
using namespace std;
|
||||
|
||||
// Processing chunk size
|
||||
#define BUFF_SIZE 2048
|
||||
|
||||
|
||||
static const char _helloText[] =
|
||||
"\n"
|
||||
" SoundStretch v%s - Written by Olli Parviainen 2001 - 2006\n"
|
||||
"==================================================================\n"
|
||||
"author e-mail: <oparviai@iki.fi> - WWW: http://www.surina.net/soundtouch\n"
|
||||
"\n"
|
||||
"This program is subject to (L)GPL license. Run \"soundstretch -license\" for\n"
|
||||
"more information.\n"
|
||||
"\n";
|
||||
|
||||
static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParameters *params)
|
||||
{
|
||||
int bits, samplerate, channels;
|
||||
|
||||
// open input file...
|
||||
*inFile = new WavInFile(params->inFileName);
|
||||
|
||||
// ... open output file with same sound parameters
|
||||
bits = (*inFile)->getNumBits();
|
||||
samplerate = (*inFile)->getSampleRate();
|
||||
channels = (*inFile)->getNumChannels();
|
||||
|
||||
if (params->outFileName)
|
||||
{
|
||||
*outFile = new WavOutFile(params->outFileName, samplerate, bits, channels);
|
||||
}
|
||||
else
|
||||
{
|
||||
*outFile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets the 'SoundTouch' object up according to input file sound format &
|
||||
// command line parameters
|
||||
static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunParameters *params)
|
||||
{
|
||||
int sampleRate;
|
||||
int channels;
|
||||
|
||||
sampleRate = inFile->getSampleRate();
|
||||
channels = inFile->getNumChannels();
|
||||
pSoundTouch->setSampleRate(sampleRate);
|
||||
pSoundTouch->setChannels(channels);
|
||||
|
||||
pSoundTouch->setTempoChange(params->tempoDelta);
|
||||
pSoundTouch->setPitchSemiTones(params->pitchDelta);
|
||||
pSoundTouch->setRateChange(params->rateDelta);
|
||||
|
||||
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, params->quick);
|
||||
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, !params->noAntiAlias);
|
||||
|
||||
// print processing information
|
||||
if (params->outFileName)
|
||||
{
|
||||
#ifdef INTEGER_SAMPLES
|
||||
printf("Uses 16bit integer sample type in processing.\n\n");
|
||||
#else
|
||||
#ifndef FLOAT_SAMPLES
|
||||
#error "Sampletype not defined"
|
||||
#endif
|
||||
printf("Uses 32bit floating point sample type in processing.\n\n");
|
||||
#endif
|
||||
// print processing information only if outFileName given i.e. some processing will happen
|
||||
printf("Processing the file with the following changes:\n");
|
||||
printf(" tempo change = %+g %%\n", params->tempoDelta);
|
||||
printf(" pitch change = %+g semitones\n", params->pitchDelta);
|
||||
printf(" rate change = %+g %%\n\n", params->rateDelta);
|
||||
printf("Working...");
|
||||
}
|
||||
else
|
||||
{
|
||||
// outFileName not given
|
||||
printf("Warning: output file name missing, won't output anything.\n\n");
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Processes the sound
|
||||
static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outFile)
|
||||
{
|
||||
int nSamples;
|
||||
int nChannels;
|
||||
int buffSizeSamples;
|
||||
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
||||
|
||||
if ((inFile == NULL) || (outFile == NULL)) return; // nothing to do.
|
||||
|
||||
nChannels = inFile->getNumChannels();
|
||||
buffSizeSamples = BUFF_SIZE / nChannels;
|
||||
|
||||
// Process samples read from the input file
|
||||
while (inFile->eof() == 0)
|
||||
{
|
||||
int num;
|
||||
|
||||
// Read a chunk of samples from the input file
|
||||
num = inFile->read(sampleBuffer, BUFF_SIZE);
|
||||
nSamples = num / inFile->getNumChannels();
|
||||
|
||||
// Feed the samples into SoundTouch processor
|
||||
pSoundTouch->putSamples(sampleBuffer, nSamples);
|
||||
|
||||
// Read ready samples from SoundTouch processor & write them output file.
|
||||
// NOTES:
|
||||
// - 'receiveSamples' doesn't necessarily return any samples at all
|
||||
// during some rounds!
|
||||
// - On the other hand, during some round 'receiveSamples' may have more
|
||||
// ready samples than would fit into 'sampleBuffer', and for this reason
|
||||
// the 'receiveSamples' call is iterated for as many times as it
|
||||
// outputs samples.
|
||||
do
|
||||
{
|
||||
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
||||
outFile->write(sampleBuffer, nSamples * nChannels);
|
||||
} while (nSamples != 0);
|
||||
}
|
||||
|
||||
// Now the input file is processed, yet 'flush' few last samples that are
|
||||
// hiding in the SoundTouch's internal processing pipeline.
|
||||
pSoundTouch->flush();
|
||||
do
|
||||
{
|
||||
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
||||
outFile->write(sampleBuffer, nSamples * nChannels);
|
||||
} while (nSamples != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
|
||||
static void detectBPM(WavInFile *inFile, RunParameters *params)
|
||||
{
|
||||
float bpmValue;
|
||||
int nChannels;
|
||||
BPMDetect bpm(inFile->getNumChannels(), inFile->getSampleRate());
|
||||
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
||||
|
||||
// detect bpm rate
|
||||
printf("Detecting BPM rate...");
|
||||
fflush(stdout);
|
||||
|
||||
nChannels = inFile->getNumChannels();
|
||||
|
||||
// Process the 'inFile' in small blocks, repeat until whole file has
|
||||
// been processed
|
||||
while (inFile->eof() == 0)
|
||||
{
|
||||
int num, samples;
|
||||
|
||||
// Read sample data from input file
|
||||
num = inFile->read(sampleBuffer, BUFF_SIZE);
|
||||
|
||||
// Enter the new samples to the bpm analyzer class
|
||||
samples = num / nChannels;
|
||||
bpm.inputSamples(sampleBuffer, samples);
|
||||
}
|
||||
|
||||
// Now the whole song data has been analyzed. Read the resulting bpm.
|
||||
bpmValue = bpm.getBpm();
|
||||
printf("Done!\n");
|
||||
|
||||
// rewind the file after bpm detection
|
||||
inFile->rewind();
|
||||
|
||||
if (bpmValue > 0)
|
||||
{
|
||||
printf("Detected BPM rate %.1f\n\n", bpmValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Couldn't detect BPM rate.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params->goalBPM > 0)
|
||||
{
|
||||
// adjust tempo to given bpm
|
||||
params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f;
|
||||
printf("The file will be converted to %.1f BPM\n\n", params->goalBPM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(const int nParams, const char *paramStr[])
|
||||
{
|
||||
WavInFile *inFile;
|
||||
WavOutFile *outFile;
|
||||
RunParameters *params;
|
||||
SoundTouch SoundTouch;
|
||||
|
||||
printf(_helloText, SoundTouch::getVersionString());
|
||||
|
||||
try
|
||||
{
|
||||
// Parse command line parameters
|
||||
params = new RunParameters(nParams, paramStr);
|
||||
|
||||
// Open input & output files
|
||||
openFiles(&inFile, &outFile, params);
|
||||
|
||||
if (params->detectBPM == TRUE)
|
||||
{
|
||||
// detect sound BPM (and adjust processing parameters
|
||||
// accordingly if necessary)
|
||||
detectBPM(inFile, params);
|
||||
}
|
||||
|
||||
// Setup the 'SoundTouch' object for processing the sound
|
||||
setup(&SoundTouch, inFile, params);
|
||||
|
||||
// Process the sound
|
||||
process(&SoundTouch, inFile, outFile);
|
||||
|
||||
// Close WAV file handles & dispose of the objects
|
||||
delete inFile;
|
||||
delete outFile;
|
||||
delete params;
|
||||
|
||||
printf("Done!\n");
|
||||
}
|
||||
catch (runtime_error &e)
|
||||
{
|
||||
// An exception occurred during processing, display an error message
|
||||
printf("%s\n", e.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundStretch main routine.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.20 $
|
||||
//
|
||||
// $Id: main.cpp,v 1.20 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include "RunParameters.h"
|
||||
#include "WavFile.h"
|
||||
#include "SoundTouch.h"
|
||||
#include "BPMDetect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
using namespace std;
|
||||
|
||||
// Processing chunk size
|
||||
#define BUFF_SIZE 2048
|
||||
|
||||
|
||||
static const char _helloText[] =
|
||||
"\n"
|
||||
" SoundStretch v%s - Written by Olli Parviainen 2001 - 2006\n"
|
||||
"==================================================================\n"
|
||||
"author e-mail: <oparviai@iki.fi> - WWW: http://www.surina.net/soundtouch\n"
|
||||
"\n"
|
||||
"This program is subject to (L)GPL license. Run \"soundstretch -license\" for\n"
|
||||
"more information.\n"
|
||||
"\n";
|
||||
|
||||
static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParameters *params)
|
||||
{
|
||||
int bits, samplerate, channels;
|
||||
|
||||
// open input file...
|
||||
*inFile = new WavInFile(params->inFileName);
|
||||
|
||||
// ... open output file with same sound parameters
|
||||
bits = (*inFile)->getNumBits();
|
||||
samplerate = (*inFile)->getSampleRate();
|
||||
channels = (*inFile)->getNumChannels();
|
||||
|
||||
if (params->outFileName)
|
||||
{
|
||||
*outFile = new WavOutFile(params->outFileName, samplerate, bits, channels);
|
||||
}
|
||||
else
|
||||
{
|
||||
*outFile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets the 'SoundTouch' object up according to input file sound format &
|
||||
// command line parameters
|
||||
static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunParameters *params)
|
||||
{
|
||||
int sampleRate;
|
||||
int channels;
|
||||
|
||||
sampleRate = inFile->getSampleRate();
|
||||
channels = inFile->getNumChannels();
|
||||
pSoundTouch->setSampleRate(sampleRate);
|
||||
pSoundTouch->setChannels(channels);
|
||||
|
||||
pSoundTouch->setTempoChange(params->tempoDelta);
|
||||
pSoundTouch->setPitchSemiTones(params->pitchDelta);
|
||||
pSoundTouch->setRateChange(params->rateDelta);
|
||||
|
||||
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, params->quick);
|
||||
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, !params->noAntiAlias);
|
||||
|
||||
// print processing information
|
||||
if (params->outFileName)
|
||||
{
|
||||
#ifdef INTEGER_SAMPLES
|
||||
printf("Uses 16bit integer sample type in processing.\n\n");
|
||||
#else
|
||||
#ifndef FLOAT_SAMPLES
|
||||
#error "Sampletype not defined"
|
||||
#endif
|
||||
printf("Uses 32bit floating point sample type in processing.\n\n");
|
||||
#endif
|
||||
// print processing information only if outFileName given i.e. some processing will happen
|
||||
printf("Processing the file with the following changes:\n");
|
||||
printf(" tempo change = %+g %%\n", params->tempoDelta);
|
||||
printf(" pitch change = %+g semitones\n", params->pitchDelta);
|
||||
printf(" rate change = %+g %%\n\n", params->rateDelta);
|
||||
printf("Working...");
|
||||
}
|
||||
else
|
||||
{
|
||||
// outFileName not given
|
||||
printf("Warning: output file name missing, won't output anything.\n\n");
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Processes the sound
|
||||
static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outFile)
|
||||
{
|
||||
int nSamples;
|
||||
int nChannels;
|
||||
int buffSizeSamples;
|
||||
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
||||
|
||||
if ((inFile == NULL) || (outFile == NULL)) return; // nothing to do.
|
||||
|
||||
nChannels = inFile->getNumChannels();
|
||||
buffSizeSamples = BUFF_SIZE / nChannels;
|
||||
|
||||
// Process samples read from the input file
|
||||
while (inFile->eof() == 0)
|
||||
{
|
||||
int num;
|
||||
|
||||
// Read a chunk of samples from the input file
|
||||
num = inFile->read(sampleBuffer, BUFF_SIZE);
|
||||
nSamples = num / inFile->getNumChannels();
|
||||
|
||||
// Feed the samples into SoundTouch processor
|
||||
pSoundTouch->putSamples(sampleBuffer, nSamples);
|
||||
|
||||
// Read ready samples from SoundTouch processor & write them output file.
|
||||
// NOTES:
|
||||
// - 'receiveSamples' doesn't necessarily return any samples at all
|
||||
// during some rounds!
|
||||
// - On the other hand, during some round 'receiveSamples' may have more
|
||||
// ready samples than would fit into 'sampleBuffer', and for this reason
|
||||
// the 'receiveSamples' call is iterated for as many times as it
|
||||
// outputs samples.
|
||||
do
|
||||
{
|
||||
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
||||
outFile->write(sampleBuffer, nSamples * nChannels);
|
||||
} while (nSamples != 0);
|
||||
}
|
||||
|
||||
// Now the input file is processed, yet 'flush' few last samples that are
|
||||
// hiding in the SoundTouch's internal processing pipeline.
|
||||
pSoundTouch->flush();
|
||||
do
|
||||
{
|
||||
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
||||
outFile->write(sampleBuffer, nSamples * nChannels);
|
||||
} while (nSamples != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
|
||||
static void detectBPM(WavInFile *inFile, RunParameters *params)
|
||||
{
|
||||
float bpmValue;
|
||||
int nChannels;
|
||||
BPMDetect bpm(inFile->getNumChannels(), inFile->getSampleRate());
|
||||
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
||||
|
||||
// detect bpm rate
|
||||
printf("Detecting BPM rate...");
|
||||
fflush(stdout);
|
||||
|
||||
nChannels = inFile->getNumChannels();
|
||||
|
||||
// Process the 'inFile' in small blocks, repeat until whole file has
|
||||
// been processed
|
||||
while (inFile->eof() == 0)
|
||||
{
|
||||
int num, samples;
|
||||
|
||||
// Read sample data from input file
|
||||
num = inFile->read(sampleBuffer, BUFF_SIZE);
|
||||
|
||||
// Enter the new samples to the bpm analyzer class
|
||||
samples = num / nChannels;
|
||||
bpm.inputSamples(sampleBuffer, samples);
|
||||
}
|
||||
|
||||
// Now the whole song data has been analyzed. Read the resulting bpm.
|
||||
bpmValue = bpm.getBpm();
|
||||
printf("Done!\n");
|
||||
|
||||
// rewind the file after bpm detection
|
||||
inFile->rewind();
|
||||
|
||||
if (bpmValue > 0)
|
||||
{
|
||||
printf("Detected BPM rate %.1f\n\n", bpmValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Couldn't detect BPM rate.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params->goalBPM > 0)
|
||||
{
|
||||
// adjust tempo to given bpm
|
||||
params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f;
|
||||
printf("The file will be converted to %.1f BPM\n\n", params->goalBPM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(const int nParams, const char *paramStr[])
|
||||
{
|
||||
WavInFile *inFile;
|
||||
WavOutFile *outFile;
|
||||
RunParameters *params;
|
||||
SoundTouch SoundTouch;
|
||||
|
||||
printf(_helloText, SoundTouch::getVersionString());
|
||||
|
||||
try
|
||||
{
|
||||
// Parse command line parameters
|
||||
params = new RunParameters(nParams, paramStr);
|
||||
|
||||
// Open input & output files
|
||||
openFiles(&inFile, &outFile, params);
|
||||
|
||||
if (params->detectBPM == TRUE)
|
||||
{
|
||||
// detect sound BPM (and adjust processing parameters
|
||||
// accordingly if necessary)
|
||||
detectBPM(inFile, params);
|
||||
}
|
||||
|
||||
// Setup the 'SoundTouch' object for processing the sound
|
||||
setup(&SoundTouch, inFile, params);
|
||||
|
||||
// Process the sound
|
||||
process(&SoundTouch, inFile, outFile);
|
||||
|
||||
// Close WAV file handles & dispose of the objects
|
||||
delete inFile;
|
||||
delete outFile;
|
||||
delete params;
|
||||
|
||||
printf("Done!\n");
|
||||
}
|
||||
catch (runtime_error &e)
|
||||
{
|
||||
// An exception occurred during processing, display an error message
|
||||
printf("%s\n", e.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,311 +1,311 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Beats-per-minute (BPM) detection routine.
|
||||
///
|
||||
/// The beat detection algorithm works as follows:
|
||||
/// - Use function 'inputSamples' to input a chunks of samples to the class for
|
||||
/// analysis. It's a good idea to enter a large sound file or stream in smallish
|
||||
/// chunks of around few kilosamples in order not to extinguish too much RAM memory.
|
||||
/// - Inputted sound data is decimated to approx 500 Hz to reduce calculation burden,
|
||||
/// which is basically ok as low (bass) frequencies mostly determine the beat rate.
|
||||
/// Simple averaging is used for anti-alias filtering because the resulting signal
|
||||
/// quality isn't of that high importance.
|
||||
/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
|
||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||
/// leave only notable peaks there.
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// autocorrelation function of the enveloped signal.
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.7 $
|
||||
//
|
||||
// $Id: BPMDetect.cpp,v 1.7 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "FIFOSampleBuffer.h"
|
||||
#include "PeakFinder.h"
|
||||
#include "BPMDetect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define INPUT_BLOCK_SAMPLES 2048
|
||||
#define DECIMATED_BLOCK_SAMPLES 256
|
||||
|
||||
typedef unsigned short ushort;
|
||||
|
||||
/// decay constant for calculating RMS volume sliding average approximation
|
||||
/// (time constant is about 10 sec)
|
||||
const float avgdecay = 0.99986f;
|
||||
|
||||
/// Normalization coefficient for calculating RMS sliding average approximation.
|
||||
const float avgnorm = (1 - avgdecay);
|
||||
|
||||
|
||||
|
||||
BPMDetect::BPMDetect(int numChannels, int sampleRate)
|
||||
{
|
||||
xcorr = NULL;
|
||||
|
||||
buffer = new FIFOSampleBuffer();
|
||||
|
||||
decimateSum = 0;
|
||||
decimateCount = 0;
|
||||
decimateBy = 0;
|
||||
|
||||
this->sampleRate = sampleRate;
|
||||
this->channels = numChannels;
|
||||
|
||||
envelopeAccu = 0;
|
||||
|
||||
// Initialize RMS volume accumulator to RMS level of 3000 (out of 32768) that's
|
||||
// a typical RMS signal level value for song data. This value is then adapted
|
||||
// to the actual level during processing.
|
||||
#ifdef INTEGER_SAMPLES
|
||||
// integer samples
|
||||
RMSVolumeAccu = (3000 * 3000) / avgnorm;
|
||||
#else
|
||||
// float samples, scaled to range [-1..+1[
|
||||
RMSVolumeAccu = (0.092f * 0.092f) / avgnorm;
|
||||
#endif
|
||||
|
||||
init(numChannels, sampleRate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
BPMDetect::~BPMDetect()
|
||||
{
|
||||
delete[] xcorr;
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
|
||||
/// low-pass filter & decimate to about 500 Hz. return number of outputted samples.
|
||||
///
|
||||
/// Decimation is used to remove the unnecessary frequencies and thus to reduce
|
||||
/// the amount of data needed to be processed as calculating autocorrelation
|
||||
/// function is a very-very heavy operation.
|
||||
///
|
||||
/// Anti-alias filtering is done simply by averaging the samples. This is really a
|
||||
/// poor-man's anti-alias filtering, but it's not so critical in this kind of application
|
||||
/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
|
||||
/// narrow band)
|
||||
int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||
{
|
||||
int count, outcount;
|
||||
LONG_SAMPLETYPE out;
|
||||
|
||||
assert(decimateBy != 0);
|
||||
outcount = 0;
|
||||
for (count = 0; count < numsamples; count ++)
|
||||
{
|
||||
decimateSum += src[count];
|
||||
|
||||
decimateCount ++;
|
||||
if (decimateCount >= decimateBy)
|
||||
{
|
||||
// Store every Nth sample only
|
||||
out = (LONG_SAMPLETYPE)(decimateSum / decimateBy);
|
||||
decimateSum = 0;
|
||||
decimateCount = 0;
|
||||
#ifdef INTEGER_SAMPLES
|
||||
// check ranges for sure (shouldn't actually be necessary)
|
||||
if (out > 32767)
|
||||
{
|
||||
out = 32767;
|
||||
}
|
||||
else if (out < -32768)
|
||||
{
|
||||
out = -32768;
|
||||
}
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[outcount] = (SAMPLETYPE)out;
|
||||
outcount ++;
|
||||
}
|
||||
}
|
||||
return outcount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates autocorrelation function of the sample history buffer
|
||||
void BPMDetect::updateXCorr(int process_samples)
|
||||
{
|
||||
int offs;
|
||||
SAMPLETYPE *pBuffer;
|
||||
|
||||
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||
|
||||
pBuffer = buffer->ptrBegin();
|
||||
for (offs = windowStart; offs < windowLen; offs ++)
|
||||
{
|
||||
LONG_SAMPLETYPE sum;
|
||||
int i;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < process_samples; i ++)
|
||||
{
|
||||
sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
||||
}
|
||||
// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
|
||||
// if it's desired that the system adapts automatically to
|
||||
// various bpms, e.g. in processing continouos music stream.
|
||||
// The 'xcorr_decay' should be a value that's smaller than but
|
||||
// close to one, and should also depend on 'process_samples' value.
|
||||
|
||||
xcorr[offs] += (float)sum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates envelope of the sample data
|
||||
void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
|
||||
{
|
||||
const float decay = 0.7f; // decay constant for smoothing the envelope
|
||||
const float norm = (1 - decay);
|
||||
|
||||
int i;
|
||||
LONG_SAMPLETYPE out;
|
||||
float val;
|
||||
|
||||
for (i = 0; i < numsamples; i ++)
|
||||
{
|
||||
// calc average RMS volume
|
||||
RMSVolumeAccu *= avgdecay;
|
||||
val = (float)fabs((float)samples[i]);
|
||||
RMSVolumeAccu += val * val;
|
||||
|
||||
// cut amplitudes that are below 2 times average RMS volume
|
||||
// (we're interested in peak values, not the silent moments)
|
||||
val -= 2 * (float)sqrt(RMSVolumeAccu * avgnorm);
|
||||
val = (val > 0) ? val : 0;
|
||||
|
||||
// smooth amplitude envelope
|
||||
envelopeAccu *= decay;
|
||||
envelopeAccu += val;
|
||||
out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
// cut peaks (shouldn't be necessary though)
|
||||
if (out > 32767) out = 32767;
|
||||
#endif // INTEGER_SAMPLES
|
||||
samples[i] = (SAMPLETYPE)out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BPMDetect::inputSamples(SAMPLETYPE *samples, int numSamples)
|
||||
{
|
||||
SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
|
||||
|
||||
// convert from stereo to mono if necessary
|
||||
if (channels == 2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numSamples; i ++)
|
||||
{
|
||||
samples[i] = (samples[i * 2] + samples[i * 2 + 1]) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// decimate
|
||||
numSamples = decimate(decimated, samples, numSamples);
|
||||
|
||||
// envelope new samples and add them to buffer
|
||||
calcEnvelope(decimated, numSamples);
|
||||
buffer->putSamples(decimated, numSamples);
|
||||
|
||||
// when the buffer has enought samples for processing...
|
||||
if ((int)buffer->numSamples() > windowLen)
|
||||
{
|
||||
int processLength;
|
||||
|
||||
// how many samples are processed
|
||||
processLength = buffer->numSamples() - windowLen;
|
||||
|
||||
// ... calculate autocorrelations for oldest samples...
|
||||
updateXCorr(processLength);
|
||||
// ... and remove them from the buffer
|
||||
buffer->receiveSamples(processLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BPMDetect::init(int numChannels, int sampleRate)
|
||||
{
|
||||
this->sampleRate = sampleRate;
|
||||
|
||||
// choose decimation factor so that result is approx. 500 Hz
|
||||
decimateBy = sampleRate / 500;
|
||||
assert(decimateBy > 0);
|
||||
assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
|
||||
|
||||
// Calculate window length & starting item according to desired min & max bpms
|
||||
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
|
||||
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
|
||||
|
||||
assert(windowLen > windowStart);
|
||||
|
||||
// allocate new working objects
|
||||
xcorr = new float[windowLen];
|
||||
memset(xcorr, 0, windowLen * sizeof(float));
|
||||
|
||||
// we do processing in mono mode
|
||||
buffer->setChannels(1);
|
||||
buffer->clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
float BPMDetect::getBpm()
|
||||
{
|
||||
float peakPos;
|
||||
PeakFinder peakFinder;
|
||||
|
||||
// find peak position
|
||||
peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
|
||||
|
||||
assert(decimateBy != 0);
|
||||
if (peakPos < 1e-6) return 0.0; // detection failed.
|
||||
|
||||
// calculate BPM
|
||||
return 60.0f * (((float)sampleRate / (float)decimateBy) / peakPos);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Beats-per-minute (BPM) detection routine.
|
||||
///
|
||||
/// The beat detection algorithm works as follows:
|
||||
/// - Use function 'inputSamples' to input a chunks of samples to the class for
|
||||
/// analysis. It's a good idea to enter a large sound file or stream in smallish
|
||||
/// chunks of around few kilosamples in order not to extinguish too much RAM memory.
|
||||
/// - Inputted sound data is decimated to approx 500 Hz to reduce calculation burden,
|
||||
/// which is basically ok as low (bass) frequencies mostly determine the beat rate.
|
||||
/// Simple averaging is used for anti-alias filtering because the resulting signal
|
||||
/// quality isn't of that high importance.
|
||||
/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
|
||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||
/// leave only notable peaks there.
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// autocorrelation function of the enveloped signal.
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.7 $
|
||||
//
|
||||
// $Id: BPMDetect.cpp,v 1.7 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "FIFOSampleBuffer.h"
|
||||
#include "PeakFinder.h"
|
||||
#include "BPMDetect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define INPUT_BLOCK_SAMPLES 2048
|
||||
#define DECIMATED_BLOCK_SAMPLES 256
|
||||
|
||||
typedef unsigned short ushort;
|
||||
|
||||
/// decay constant for calculating RMS volume sliding average approximation
|
||||
/// (time constant is about 10 sec)
|
||||
const float avgdecay = 0.99986f;
|
||||
|
||||
/// Normalization coefficient for calculating RMS sliding average approximation.
|
||||
const float avgnorm = (1 - avgdecay);
|
||||
|
||||
|
||||
|
||||
BPMDetect::BPMDetect(int numChannels, int sampleRate)
|
||||
{
|
||||
xcorr = NULL;
|
||||
|
||||
buffer = new FIFOSampleBuffer();
|
||||
|
||||
decimateSum = 0;
|
||||
decimateCount = 0;
|
||||
decimateBy = 0;
|
||||
|
||||
this->sampleRate = sampleRate;
|
||||
this->channels = numChannels;
|
||||
|
||||
envelopeAccu = 0;
|
||||
|
||||
// Initialize RMS volume accumulator to RMS level of 3000 (out of 32768) that's
|
||||
// a typical RMS signal level value for song data. This value is then adapted
|
||||
// to the actual level during processing.
|
||||
#ifdef INTEGER_SAMPLES
|
||||
// integer samples
|
||||
RMSVolumeAccu = (3000 * 3000) / avgnorm;
|
||||
#else
|
||||
// float samples, scaled to range [-1..+1[
|
||||
RMSVolumeAccu = (0.092f * 0.092f) / avgnorm;
|
||||
#endif
|
||||
|
||||
init(numChannels, sampleRate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
BPMDetect::~BPMDetect()
|
||||
{
|
||||
delete[] xcorr;
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
|
||||
/// low-pass filter & decimate to about 500 Hz. return number of outputted samples.
|
||||
///
|
||||
/// Decimation is used to remove the unnecessary frequencies and thus to reduce
|
||||
/// the amount of data needed to be processed as calculating autocorrelation
|
||||
/// function is a very-very heavy operation.
|
||||
///
|
||||
/// Anti-alias filtering is done simply by averaging the samples. This is really a
|
||||
/// poor-man's anti-alias filtering, but it's not so critical in this kind of application
|
||||
/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
|
||||
/// narrow band)
|
||||
int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||
{
|
||||
int count, outcount;
|
||||
LONG_SAMPLETYPE out;
|
||||
|
||||
assert(decimateBy != 0);
|
||||
outcount = 0;
|
||||
for (count = 0; count < numsamples; count ++)
|
||||
{
|
||||
decimateSum += src[count];
|
||||
|
||||
decimateCount ++;
|
||||
if (decimateCount >= decimateBy)
|
||||
{
|
||||
// Store every Nth sample only
|
||||
out = (LONG_SAMPLETYPE)(decimateSum / decimateBy);
|
||||
decimateSum = 0;
|
||||
decimateCount = 0;
|
||||
#ifdef INTEGER_SAMPLES
|
||||
// check ranges for sure (shouldn't actually be necessary)
|
||||
if (out > 32767)
|
||||
{
|
||||
out = 32767;
|
||||
}
|
||||
else if (out < -32768)
|
||||
{
|
||||
out = -32768;
|
||||
}
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[outcount] = (SAMPLETYPE)out;
|
||||
outcount ++;
|
||||
}
|
||||
}
|
||||
return outcount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates autocorrelation function of the sample history buffer
|
||||
void BPMDetect::updateXCorr(int process_samples)
|
||||
{
|
||||
int offs;
|
||||
SAMPLETYPE *pBuffer;
|
||||
|
||||
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||
|
||||
pBuffer = buffer->ptrBegin();
|
||||
for (offs = windowStart; offs < windowLen; offs ++)
|
||||
{
|
||||
LONG_SAMPLETYPE sum;
|
||||
int i;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < process_samples; i ++)
|
||||
{
|
||||
sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
||||
}
|
||||
// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
|
||||
// if it's desired that the system adapts automatically to
|
||||
// various bpms, e.g. in processing continouos music stream.
|
||||
// The 'xcorr_decay' should be a value that's smaller than but
|
||||
// close to one, and should also depend on 'process_samples' value.
|
||||
|
||||
xcorr[offs] += (float)sum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates envelope of the sample data
|
||||
void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
|
||||
{
|
||||
const float decay = 0.7f; // decay constant for smoothing the envelope
|
||||
const float norm = (1 - decay);
|
||||
|
||||
int i;
|
||||
LONG_SAMPLETYPE out;
|
||||
float val;
|
||||
|
||||
for (i = 0; i < numsamples; i ++)
|
||||
{
|
||||
// calc average RMS volume
|
||||
RMSVolumeAccu *= avgdecay;
|
||||
val = (float)fabs((float)samples[i]);
|
||||
RMSVolumeAccu += val * val;
|
||||
|
||||
// cut amplitudes that are below 2 times average RMS volume
|
||||
// (we're interested in peak values, not the silent moments)
|
||||
val -= 2 * (float)sqrt(RMSVolumeAccu * avgnorm);
|
||||
val = (val > 0) ? val : 0;
|
||||
|
||||
// smooth amplitude envelope
|
||||
envelopeAccu *= decay;
|
||||
envelopeAccu += val;
|
||||
out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
// cut peaks (shouldn't be necessary though)
|
||||
if (out > 32767) out = 32767;
|
||||
#endif // INTEGER_SAMPLES
|
||||
samples[i] = (SAMPLETYPE)out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BPMDetect::inputSamples(SAMPLETYPE *samples, int numSamples)
|
||||
{
|
||||
SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
|
||||
|
||||
// convert from stereo to mono if necessary
|
||||
if (channels == 2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numSamples; i ++)
|
||||
{
|
||||
samples[i] = (samples[i * 2] + samples[i * 2 + 1]) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// decimate
|
||||
numSamples = decimate(decimated, samples, numSamples);
|
||||
|
||||
// envelope new samples and add them to buffer
|
||||
calcEnvelope(decimated, numSamples);
|
||||
buffer->putSamples(decimated, numSamples);
|
||||
|
||||
// when the buffer has enought samples for processing...
|
||||
if ((int)buffer->numSamples() > windowLen)
|
||||
{
|
||||
int processLength;
|
||||
|
||||
// how many samples are processed
|
||||
processLength = buffer->numSamples() - windowLen;
|
||||
|
||||
// ... calculate autocorrelations for oldest samples...
|
||||
updateXCorr(processLength);
|
||||
// ... and remove them from the buffer
|
||||
buffer->receiveSamples(processLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BPMDetect::init(int numChannels, int sampleRate)
|
||||
{
|
||||
this->sampleRate = sampleRate;
|
||||
|
||||
// choose decimation factor so that result is approx. 500 Hz
|
||||
decimateBy = sampleRate / 500;
|
||||
assert(decimateBy > 0);
|
||||
assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
|
||||
|
||||
// Calculate window length & starting item according to desired min & max bpms
|
||||
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
|
||||
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
|
||||
|
||||
assert(windowLen > windowStart);
|
||||
|
||||
// allocate new working objects
|
||||
xcorr = new float[windowLen];
|
||||
memset(xcorr, 0, windowLen * sizeof(float));
|
||||
|
||||
// we do processing in mono mode
|
||||
buffer->setChannels(1);
|
||||
buffer->clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
float BPMDetect::getBpm()
|
||||
{
|
||||
float peakPos;
|
||||
PeakFinder peakFinder;
|
||||
|
||||
// find peak position
|
||||
peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
|
||||
|
||||
assert(decimateBy != 0);
|
||||
if (peakPos < 1e-6) return 0.0; // detection failed.
|
||||
|
||||
// calculate BPM
|
||||
return 60.0f * (((float)sampleRate / (float)decimateBy) / peakPos);
|
||||
}
|
||||
|
@ -1,191 +1,191 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Peak detection routine.
|
||||
///
|
||||
/// The routine detects highest value on an array of values and calculates the
|
||||
/// precise peak location as a mass-center of the 'hump' around the peak value.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:07 $
|
||||
// File revision : $Revision: 1.3 $
|
||||
//
|
||||
// $Id: PeakFinder.cpp,v 1.3 2006/02/05 16:44:07 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "PeakFinder.h"
|
||||
|
||||
|
||||
PeakFinder::PeakFinder()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
|
||||
// to direction defined by 'direction' until next 'hump' after minimum value will
|
||||
// begin
|
||||
int PeakFinder::findGround(const float *data, int peakpos, int direction) const
|
||||
{
|
||||
float refvalue;
|
||||
int lowpos;
|
||||
int pos;
|
||||
int climb_count;
|
||||
float delta;
|
||||
|
||||
climb_count = 0;
|
||||
refvalue = data[peakpos];
|
||||
lowpos = peakpos;
|
||||
|
||||
pos = peakpos;
|
||||
|
||||
while ((pos > minPos) && (pos < maxPos))
|
||||
{
|
||||
int prevpos;
|
||||
|
||||
prevpos = pos;
|
||||
pos += direction;
|
||||
|
||||
// calculate derivate
|
||||
delta = data[pos] - data[prevpos];
|
||||
if (delta <= 0)
|
||||
{
|
||||
// going downhill, ok
|
||||
if (climb_count)
|
||||
{
|
||||
climb_count --; // decrease climb count
|
||||
}
|
||||
|
||||
// check if new minimum found
|
||||
if (data[pos] < refvalue)
|
||||
{
|
||||
// new minimum found
|
||||
lowpos = pos;
|
||||
refvalue = data[pos];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// going uphill, increase climbing counter
|
||||
climb_count ++;
|
||||
if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
|
||||
}
|
||||
}
|
||||
return lowpos;
|
||||
}
|
||||
|
||||
|
||||
// Find offset where the value crosses the given level, when starting from 'peakpos' and
|
||||
// proceeds to direction defined in 'direction'
|
||||
int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
|
||||
{
|
||||
float peaklevel;
|
||||
int pos;
|
||||
|
||||
peaklevel = data[peakpos];
|
||||
assert(peaklevel >= level);
|
||||
pos = peakpos;
|
||||
while ((pos >= minPos) && (pos < maxPos))
|
||||
{
|
||||
if (data[pos + direction] < level) return pos; // crossing found
|
||||
pos += direction;
|
||||
}
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
|
||||
// Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos'
|
||||
float PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const
|
||||
{
|
||||
int i;
|
||||
float sum;
|
||||
float wsum;
|
||||
|
||||
sum = 0;
|
||||
wsum = 0;
|
||||
for (i = firstPos; i <= lastPos; i ++)
|
||||
{
|
||||
sum += (float)i * data[i];
|
||||
wsum += data[i];
|
||||
}
|
||||
return sum / wsum;
|
||||
}
|
||||
|
||||
|
||||
float PeakFinder::detectPeak(const float *data, int minPos, int maxPos)
|
||||
{
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
int i;
|
||||
int peakpos; // position of peak level
|
||||
float peakLevel; // peak level
|
||||
int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
|
||||
float cutLevel; // cutting value
|
||||
float groundLevel; // ground level of the peak
|
||||
int gp1, gp2; // bottom positions of the peak 'hump'
|
||||
|
||||
this->minPos = minPos;
|
||||
this->maxPos = maxPos;
|
||||
|
||||
// find absolute peak
|
||||
peakpos = minPos;
|
||||
peakLevel = data[minPos];
|
||||
for (i = minPos + 1; i < maxPos; i ++)
|
||||
{
|
||||
if (data[i] > peakLevel)
|
||||
{
|
||||
peakLevel = data[i];
|
||||
peakpos = i;
|
||||
}
|
||||
}
|
||||
|
||||
// find ground positions.
|
||||
gp1 = findGround(data, peakpos, -1);
|
||||
gp2 = findGround(data, peakpos, 1);
|
||||
|
||||
groundLevel = max(data[gp1], data[gp2]);
|
||||
|
||||
if (groundLevel < 1e-6) return 0; // ground level too small => detection failed
|
||||
if ((peakLevel / groundLevel) < 1.3) return 0; // peak less than 30% of the ground level => no good peak detected
|
||||
|
||||
// calculate 70%-level of the peak
|
||||
cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
|
||||
// find mid-level crossings
|
||||
crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
|
||||
crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
|
||||
|
||||
if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
|
||||
|
||||
// calculate mass center of the peak surroundings
|
||||
return calcMassCenter(data, crosspos1, crosspos2);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Peak detection routine.
|
||||
///
|
||||
/// The routine detects highest value on an array of values and calculates the
|
||||
/// precise peak location as a mass-center of the 'hump' around the peak value.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:07 $
|
||||
// File revision : $Revision: 1.3 $
|
||||
//
|
||||
// $Id: PeakFinder.cpp,v 1.3 2006/02/05 16:44:07 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "PeakFinder.h"
|
||||
|
||||
|
||||
PeakFinder::PeakFinder()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
|
||||
// to direction defined by 'direction' until next 'hump' after minimum value will
|
||||
// begin
|
||||
int PeakFinder::findGround(const float *data, int peakpos, int direction) const
|
||||
{
|
||||
float refvalue;
|
||||
int lowpos;
|
||||
int pos;
|
||||
int climb_count;
|
||||
float delta;
|
||||
|
||||
climb_count = 0;
|
||||
refvalue = data[peakpos];
|
||||
lowpos = peakpos;
|
||||
|
||||
pos = peakpos;
|
||||
|
||||
while ((pos > minPos) && (pos < maxPos))
|
||||
{
|
||||
int prevpos;
|
||||
|
||||
prevpos = pos;
|
||||
pos += direction;
|
||||
|
||||
// calculate derivate
|
||||
delta = data[pos] - data[prevpos];
|
||||
if (delta <= 0)
|
||||
{
|
||||
// going downhill, ok
|
||||
if (climb_count)
|
||||
{
|
||||
climb_count --; // decrease climb count
|
||||
}
|
||||
|
||||
// check if new minimum found
|
||||
if (data[pos] < refvalue)
|
||||
{
|
||||
// new minimum found
|
||||
lowpos = pos;
|
||||
refvalue = data[pos];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// going uphill, increase climbing counter
|
||||
climb_count ++;
|
||||
if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
|
||||
}
|
||||
}
|
||||
return lowpos;
|
||||
}
|
||||
|
||||
|
||||
// Find offset where the value crosses the given level, when starting from 'peakpos' and
|
||||
// proceeds to direction defined in 'direction'
|
||||
int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
|
||||
{
|
||||
float peaklevel;
|
||||
int pos;
|
||||
|
||||
peaklevel = data[peakpos];
|
||||
assert(peaklevel >= level);
|
||||
pos = peakpos;
|
||||
while ((pos >= minPos) && (pos < maxPos))
|
||||
{
|
||||
if (data[pos + direction] < level) return pos; // crossing found
|
||||
pos += direction;
|
||||
}
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
|
||||
// Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos'
|
||||
float PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const
|
||||
{
|
||||
int i;
|
||||
float sum;
|
||||
float wsum;
|
||||
|
||||
sum = 0;
|
||||
wsum = 0;
|
||||
for (i = firstPos; i <= lastPos; i ++)
|
||||
{
|
||||
sum += (float)i * data[i];
|
||||
wsum += data[i];
|
||||
}
|
||||
return sum / wsum;
|
||||
}
|
||||
|
||||
|
||||
float PeakFinder::detectPeak(const float *data, int minPos, int maxPos)
|
||||
{
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
int i;
|
||||
int peakpos; // position of peak level
|
||||
float peakLevel; // peak level
|
||||
int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
|
||||
float cutLevel; // cutting value
|
||||
float groundLevel; // ground level of the peak
|
||||
int gp1, gp2; // bottom positions of the peak 'hump'
|
||||
|
||||
this->minPos = minPos;
|
||||
this->maxPos = maxPos;
|
||||
|
||||
// find absolute peak
|
||||
peakpos = minPos;
|
||||
peakLevel = data[minPos];
|
||||
for (i = minPos + 1; i < maxPos; i ++)
|
||||
{
|
||||
if (data[i] > peakLevel)
|
||||
{
|
||||
peakLevel = data[i];
|
||||
peakpos = i;
|
||||
}
|
||||
}
|
||||
|
||||
// find ground positions.
|
||||
gp1 = findGround(data, peakpos, -1);
|
||||
gp2 = findGround(data, peakpos, 1);
|
||||
|
||||
groundLevel = max(data[gp1], data[gp2]);
|
||||
|
||||
if (groundLevel < 1e-6) return 0; // ground level too small => detection failed
|
||||
if ((peakLevel / groundLevel) < 1.3) return 0; // peak less than 30% of the ground level => no good peak detected
|
||||
|
||||
// calculate 70%-level of the peak
|
||||
cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
|
||||
// find mid-level crossings
|
||||
crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
|
||||
crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
|
||||
|
||||
if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
|
||||
|
||||
// calculate mass center of the peak surroundings
|
||||
return calcMassCenter(data, crosspos1, crosspos2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,235 +30,235 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CddaDataStream.h"
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#define RAW_SECTOR_SIZE 2352
|
||||
#define MSF2UINT(hgs) ((hgs[1]*4500) + (hgs[2]*75) + (hgs[3]))
|
||||
|
||||
static CddaDataStream* active = NULL;
|
||||
static boost::mutex activeMutex;
|
||||
|
||||
static void setActive(CddaDataStream* stream) {
|
||||
boost::mutex::scoped_lock lock(activeMutex);
|
||||
|
||||
if (active != NULL) {
|
||||
active->Close();
|
||||
active = NULL;
|
||||
}
|
||||
|
||||
active = stream;
|
||||
}
|
||||
|
||||
static void resetIfActive(CddaDataStream* stream) {
|
||||
boost::mutex::scoped_lock lock(activeMutex);
|
||||
|
||||
if (stream == active) {
|
||||
active = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CddaDataStream::CddaDataStream() {
|
||||
this->closed = false;
|
||||
this->drive = INVALID_HANDLE_VALUE;
|
||||
this->position = this->length = 0;
|
||||
memset(&this->toc, 0, sizeof(this->toc));
|
||||
this->startSector = this->stopSector = 0;
|
||||
}
|
||||
|
||||
CddaDataStream::~CddaDataStream() {
|
||||
this->Close();
|
||||
resetIfActive(this);
|
||||
}
|
||||
|
||||
int CddaDataStream::GetChannelCount() {
|
||||
return this->channels;
|
||||
}
|
||||
|
||||
static int getTrackNumber(const char* uri) {
|
||||
std::string filename(uri);
|
||||
size_t lastDot = filename.find_last_of(".");
|
||||
if (lastDot != std::string::npos) {
|
||||
/* always in the format F:\Track01.cda */
|
||||
std::string number = filename.substr(lastDot - 2, 2);
|
||||
try {
|
||||
return stoi(number);
|
||||
}
|
||||
catch (...) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool CddaDataStream::Open(const char *uri, unsigned int options) {
|
||||
int trackIndex;
|
||||
|
||||
char driveLetter = 'A' + PathGetDriveNumber(uri);
|
||||
std::string drivePath = "\\\\.\\" + std::string(1, driveLetter) + ":";
|
||||
|
||||
this->drive = CreateFile(
|
||||
drivePath.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_READONLY | FILE_FLAG_SEQUENTIAL_SCAN,
|
||||
(HANDLE) NULL);
|
||||
|
||||
if (this->drive == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
trackIndex = getTrackNumber(uri);
|
||||
|
||||
DWORD byteCount;
|
||||
|
||||
BOOL canReadFromDevice = DeviceIoControl(
|
||||
this->drive,
|
||||
IOCTL_CDROM_READ_TOC,
|
||||
NULL,
|
||||
0,
|
||||
&this->toc,
|
||||
sizeof(this->toc),
|
||||
&byteCount,
|
||||
0);
|
||||
|
||||
bool trackIndexValid =
|
||||
this->toc.FirstTrack <= trackIndex &&
|
||||
trackIndex <= this->toc.LastTrack;
|
||||
|
||||
if (!canReadFromDevice && !trackIndexValid) {
|
||||
this->Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* MMC-3 Draft Revision 10g: Table 222 – Q Sub-channel control field */
|
||||
this->toc.TrackData[trackIndex - 1].Control &= 5;
|
||||
if (!(
|
||||
this->toc.TrackData[trackIndex - 1].Control == 0 ||
|
||||
this->toc.TrackData[trackIndex - 1].Control == 1))
|
||||
{
|
||||
this->Close();
|
||||
return(false);
|
||||
}
|
||||
|
||||
this->channels = 2;
|
||||
if (this->toc.TrackData[trackIndex - 1].Control & 8) {
|
||||
this->channels = 4;
|
||||
}
|
||||
|
||||
this->startSector = MSF2UINT(this->toc.TrackData[trackIndex - 1].Address) - 150;
|
||||
this->stopSector = MSF2UINT(this->toc.TrackData[trackIndex].Address) - 150;
|
||||
this->length = (this->stopSector - this->startSector) * RAW_SECTOR_SIZE;
|
||||
|
||||
setActive(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CddaDataStream::Close() {
|
||||
if (this->drive != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(this->drive);
|
||||
this->drive = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
this->closed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CddaDataStream::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
PositionType CddaDataStream::Read(void* buffer, PositionType readBytes) {
|
||||
DWORD actualBytesRead = 0;
|
||||
this->Read((BYTE*) buffer, readBytes, true, &actualBytesRead);
|
||||
return (PositionType) actualBytesRead;
|
||||
}
|
||||
|
||||
bool CddaDataStream::SetPosition(PositionType position) {
|
||||
if (position > this->length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->position = position;
|
||||
|
||||
while (this->position % (2 * this->channels)) {
|
||||
this->position++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PositionType CddaDataStream::Position() {
|
||||
return (PositionType) this->position;
|
||||
}
|
||||
|
||||
bool CddaDataStream::Eof() {
|
||||
return (this->position >= this->length - 1);
|
||||
}
|
||||
|
||||
long CddaDataStream::Length() {
|
||||
return (long) this->length;
|
||||
}
|
||||
|
||||
const char* CddaDataStream::Type() {
|
||||
return "cda";
|
||||
}
|
||||
|
||||
HRESULT CddaDataStream::Read(PBYTE pbBuffer, DWORD dwBytesToRead, BOOL bAlign, LPDWORD pdwBytesRead) {
|
||||
if (this->closed) {
|
||||
pdwBytesRead = 0;
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
BYTE buff[RAW_SECTOR_SIZE];
|
||||
|
||||
PBYTE pbBufferOrg = pbBuffer;
|
||||
LONGLONG pos = this->position;
|
||||
size_t len = (size_t) dwBytesToRead;
|
||||
|
||||
while (pos >= 0 && pos < this->length && len > 0) {
|
||||
RAW_READ_INFO rawreadinfo;
|
||||
rawreadinfo.SectorCount = 1;
|
||||
rawreadinfo.TrackMode = CDDA;
|
||||
|
||||
UINT sector = this->startSector + int(pos / RAW_SECTOR_SIZE);
|
||||
__int64 offset = pos % RAW_SECTOR_SIZE;
|
||||
|
||||
rawreadinfo.DiskOffset.QuadPart = sector * 2048;
|
||||
DWORD bytesRead = 0;
|
||||
|
||||
DeviceIoControl(
|
||||
this->drive,
|
||||
IOCTL_CDROM_RAW_READ,
|
||||
&rawreadinfo,
|
||||
sizeof(rawreadinfo),
|
||||
buff, RAW_SECTOR_SIZE,
|
||||
&bytesRead,
|
||||
0);
|
||||
|
||||
size_t l = (size_t)min(min(len, RAW_SECTOR_SIZE - offset), this->length - pos);
|
||||
memcpy(pbBuffer, &buff[offset], l);
|
||||
|
||||
pbBuffer += l;
|
||||
pos += l;
|
||||
len -= l;
|
||||
}
|
||||
|
||||
if (pdwBytesRead) {
|
||||
*pdwBytesRead = pbBuffer - pbBufferOrg;
|
||||
}
|
||||
|
||||
this->position += pbBuffer - pbBufferOrg;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CddaDataStream.h"
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#define RAW_SECTOR_SIZE 2352
|
||||
#define MSF2UINT(hgs) ((hgs[1]*4500) + (hgs[2]*75) + (hgs[3]))
|
||||
|
||||
static CddaDataStream* active = NULL;
|
||||
static boost::mutex activeMutex;
|
||||
|
||||
static void setActive(CddaDataStream* stream) {
|
||||
boost::mutex::scoped_lock lock(activeMutex);
|
||||
|
||||
if (active != NULL) {
|
||||
active->Close();
|
||||
active = NULL;
|
||||
}
|
||||
|
||||
active = stream;
|
||||
}
|
||||
|
||||
static void resetIfActive(CddaDataStream* stream) {
|
||||
boost::mutex::scoped_lock lock(activeMutex);
|
||||
|
||||
if (stream == active) {
|
||||
active = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CddaDataStream::CddaDataStream() {
|
||||
this->closed = false;
|
||||
this->drive = INVALID_HANDLE_VALUE;
|
||||
this->position = this->length = 0;
|
||||
memset(&this->toc, 0, sizeof(this->toc));
|
||||
this->startSector = this->stopSector = 0;
|
||||
}
|
||||
|
||||
CddaDataStream::~CddaDataStream() {
|
||||
this->Close();
|
||||
resetIfActive(this);
|
||||
}
|
||||
|
||||
int CddaDataStream::GetChannelCount() {
|
||||
return this->channels;
|
||||
}
|
||||
|
||||
static int getTrackNumber(const char* uri) {
|
||||
std::string filename(uri);
|
||||
size_t lastDot = filename.find_last_of(".");
|
||||
if (lastDot != std::string::npos) {
|
||||
/* always in the format F:\Track01.cda */
|
||||
std::string number = filename.substr(lastDot - 2, 2);
|
||||
try {
|
||||
return stoi(number);
|
||||
}
|
||||
catch (...) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool CddaDataStream::Open(const char *uri, unsigned int options) {
|
||||
int trackIndex;
|
||||
|
||||
char driveLetter = 'A' + PathGetDriveNumber(uri);
|
||||
std::string drivePath = "\\\\.\\" + std::string(1, driveLetter) + ":";
|
||||
|
||||
this->drive = CreateFile(
|
||||
drivePath.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_READONLY | FILE_FLAG_SEQUENTIAL_SCAN,
|
||||
(HANDLE) NULL);
|
||||
|
||||
if (this->drive == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
trackIndex = getTrackNumber(uri);
|
||||
|
||||
DWORD byteCount;
|
||||
|
||||
BOOL canReadFromDevice = DeviceIoControl(
|
||||
this->drive,
|
||||
IOCTL_CDROM_READ_TOC,
|
||||
NULL,
|
||||
0,
|
||||
&this->toc,
|
||||
sizeof(this->toc),
|
||||
&byteCount,
|
||||
0);
|
||||
|
||||
bool trackIndexValid =
|
||||
this->toc.FirstTrack <= trackIndex &&
|
||||
trackIndex <= this->toc.LastTrack;
|
||||
|
||||
if (!canReadFromDevice && !trackIndexValid) {
|
||||
this->Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* MMC-3 Draft Revision 10g: Table 222 – Q Sub-channel control field */
|
||||
this->toc.TrackData[trackIndex - 1].Control &= 5;
|
||||
if (!(
|
||||
this->toc.TrackData[trackIndex - 1].Control == 0 ||
|
||||
this->toc.TrackData[trackIndex - 1].Control == 1))
|
||||
{
|
||||
this->Close();
|
||||
return(false);
|
||||
}
|
||||
|
||||
this->channels = 2;
|
||||
if (this->toc.TrackData[trackIndex - 1].Control & 8) {
|
||||
this->channels = 4;
|
||||
}
|
||||
|
||||
this->startSector = MSF2UINT(this->toc.TrackData[trackIndex - 1].Address) - 150;
|
||||
this->stopSector = MSF2UINT(this->toc.TrackData[trackIndex].Address) - 150;
|
||||
this->length = (this->stopSector - this->startSector) * RAW_SECTOR_SIZE;
|
||||
|
||||
setActive(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CddaDataStream::Close() {
|
||||
if (this->drive != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(this->drive);
|
||||
this->drive = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
this->closed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CddaDataStream::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
PositionType CddaDataStream::Read(void* buffer, PositionType readBytes) {
|
||||
DWORD actualBytesRead = 0;
|
||||
this->Read((BYTE*) buffer, readBytes, true, &actualBytesRead);
|
||||
return (PositionType) actualBytesRead;
|
||||
}
|
||||
|
||||
bool CddaDataStream::SetPosition(PositionType position) {
|
||||
if (position > this->length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->position = position;
|
||||
|
||||
while (this->position % (2 * this->channels)) {
|
||||
this->position++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PositionType CddaDataStream::Position() {
|
||||
return (PositionType) this->position;
|
||||
}
|
||||
|
||||
bool CddaDataStream::Eof() {
|
||||
return (this->position >= this->length - 1);
|
||||
}
|
||||
|
||||
long CddaDataStream::Length() {
|
||||
return (long) this->length;
|
||||
}
|
||||
|
||||
const char* CddaDataStream::Type() {
|
||||
return "cda";
|
||||
}
|
||||
|
||||
HRESULT CddaDataStream::Read(PBYTE pbBuffer, DWORD dwBytesToRead, BOOL bAlign, LPDWORD pdwBytesRead) {
|
||||
if (this->closed) {
|
||||
pdwBytesRead = 0;
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
BYTE buff[RAW_SECTOR_SIZE];
|
||||
|
||||
PBYTE pbBufferOrg = pbBuffer;
|
||||
LONGLONG pos = this->position;
|
||||
size_t len = (size_t) dwBytesToRead;
|
||||
|
||||
while (pos >= 0 && pos < this->length && len > 0) {
|
||||
RAW_READ_INFO rawreadinfo;
|
||||
rawreadinfo.SectorCount = 1;
|
||||
rawreadinfo.TrackMode = CDDA;
|
||||
|
||||
UINT sector = this->startSector + int(pos / RAW_SECTOR_SIZE);
|
||||
__int64 offset = pos % RAW_SECTOR_SIZE;
|
||||
|
||||
rawreadinfo.DiskOffset.QuadPart = sector * 2048;
|
||||
DWORD bytesRead = 0;
|
||||
|
||||
DeviceIoControl(
|
||||
this->drive,
|
||||
IOCTL_CDROM_RAW_READ,
|
||||
&rawreadinfo,
|
||||
sizeof(rawreadinfo),
|
||||
buff, RAW_SECTOR_SIZE,
|
||||
&bytesRead,
|
||||
0);
|
||||
|
||||
size_t l = (size_t)min(min(len, RAW_SECTOR_SIZE - offset), this->length - pos);
|
||||
memcpy(pbBuffer, &buff[offset], l);
|
||||
|
||||
pbBuffer += l;
|
||||
pos += l;
|
||||
len -= l;
|
||||
}
|
||||
|
||||
if (pdwBytesRead) {
|
||||
*pdwBytesRead = pbBuffer - pbBufferOrg;
|
||||
}
|
||||
|
||||
this->position += pbBuffer - pbBufferOrg;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -30,37 +30,37 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "shlwapi.h"
|
||||
#include <string>
|
||||
#include "CddaDataStreamFactory.h"
|
||||
#include "CddaDataStream.h"
|
||||
|
||||
CddaDataStreamFactory::CddaDataStreamFactory() {
|
||||
|
||||
}
|
||||
|
||||
CddaDataStreamFactory::~CddaDataStreamFactory() {
|
||||
|
||||
}
|
||||
|
||||
bool CddaDataStreamFactory::CanRead(const char *uri) {
|
||||
std::string extension = PathFindExtension(uri);
|
||||
return (extension == ".cda");
|
||||
}
|
||||
|
||||
IDataStream* CddaDataStreamFactory::Open(const char *uri, unsigned int options) {
|
||||
CddaDataStream* stream = new CddaDataStream();
|
||||
|
||||
if (stream->Open(uri, options)) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
delete stream;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CddaDataStreamFactory::Destroy() {
|
||||
delete this;
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "shlwapi.h"
|
||||
#include <string>
|
||||
#include "CddaDataStreamFactory.h"
|
||||
#include "CddaDataStream.h"
|
||||
|
||||
CddaDataStreamFactory::CddaDataStreamFactory() {
|
||||
|
||||
}
|
||||
|
||||
CddaDataStreamFactory::~CddaDataStreamFactory() {
|
||||
|
||||
}
|
||||
|
||||
bool CddaDataStreamFactory::CanRead(const char *uri) {
|
||||
std::string extension = PathFindExtension(uri);
|
||||
return (extension == ".cda");
|
||||
}
|
||||
|
||||
IDataStream* CddaDataStreamFactory::Open(const char *uri, unsigned int options) {
|
||||
CddaDataStream* stream = new CddaDataStream();
|
||||
|
||||
if (stream->Open(uri, options)) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
delete stream;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CddaDataStreamFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
@ -30,66 +30,66 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CddaDecoder.h"
|
||||
|
||||
#define CDDA_SAMPLE_RATE 44100
|
||||
#define CDDA_BUFFER_SIZE 4096
|
||||
#define BYTES_PER_RAW_SAMPLE 2
|
||||
|
||||
CddaDecoder::CddaDecoder() {
|
||||
this->data = NULL;
|
||||
this->buffer = new BYTE[CDDA_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
CddaDecoder::~CddaDecoder() {
|
||||
delete[] this->buffer;
|
||||
}
|
||||
|
||||
void CddaDecoder::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool CddaDecoder::Open(IDataStream* data) {
|
||||
this->data = (CddaDataStream *) data;
|
||||
return (data != NULL);
|
||||
}
|
||||
|
||||
double CddaDecoder::SetPosition(double seconds) {
|
||||
LONGLONG bytesPerSecond = (CDDA_SAMPLE_RATE * data->GetChannelCount() * sizeof(short));
|
||||
LONGLONG offset = (LONGLONG)((float)bytesPerSecond * seconds);
|
||||
if (this->data->SetPosition(offset)) {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CddaDecoder::GetBuffer(IBuffer *buffer) {
|
||||
int channels = data->GetChannelCount();
|
||||
buffer->SetSamples(CDDA_BUFFER_SIZE / BYTES_PER_RAW_SAMPLE / channels);
|
||||
buffer->SetChannels(data->GetChannelCount());
|
||||
buffer->SetSampleRate(CDDA_SAMPLE_RATE);
|
||||
|
||||
float* target = buffer->BufferPointer();
|
||||
|
||||
PositionType count = this->data->Read(
|
||||
(void *) this->buffer, CDDA_BUFFER_SIZE);
|
||||
|
||||
if (count > 0) {
|
||||
short* t = (short*) this->buffer;
|
||||
|
||||
for (int x = 0; x < (count / BYTES_PER_RAW_SAMPLE); x++)
|
||||
{
|
||||
target[x] = (float) t[x] / 16384.0f;
|
||||
}
|
||||
|
||||
buffer->SetSamples(count / BYTES_PER_RAW_SAMPLE / channels);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CddaDecoder.h"
|
||||
|
||||
#define CDDA_SAMPLE_RATE 44100
|
||||
#define CDDA_BUFFER_SIZE 4096
|
||||
#define BYTES_PER_RAW_SAMPLE 2
|
||||
|
||||
CddaDecoder::CddaDecoder() {
|
||||
this->data = NULL;
|
||||
this->buffer = new BYTE[CDDA_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
CddaDecoder::~CddaDecoder() {
|
||||
delete[] this->buffer;
|
||||
}
|
||||
|
||||
void CddaDecoder::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool CddaDecoder::Open(IDataStream* data) {
|
||||
this->data = (CddaDataStream *) data;
|
||||
return (data != NULL);
|
||||
}
|
||||
|
||||
double CddaDecoder::SetPosition(double seconds) {
|
||||
LONGLONG bytesPerSecond = (CDDA_SAMPLE_RATE * data->GetChannelCount() * sizeof(short));
|
||||
LONGLONG offset = (LONGLONG)((float)bytesPerSecond * seconds);
|
||||
if (this->data->SetPosition(offset)) {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CddaDecoder::GetBuffer(IBuffer *buffer) {
|
||||
int channels = data->GetChannelCount();
|
||||
buffer->SetSamples(CDDA_BUFFER_SIZE / BYTES_PER_RAW_SAMPLE / channels);
|
||||
buffer->SetChannels(data->GetChannelCount());
|
||||
buffer->SetSampleRate(CDDA_SAMPLE_RATE);
|
||||
|
||||
float* target = buffer->BufferPointer();
|
||||
|
||||
PositionType count = this->data->Read(
|
||||
(void *) this->buffer, CDDA_BUFFER_SIZE);
|
||||
|
||||
if (count > 0) {
|
||||
short* t = (short*) this->buffer;
|
||||
|
||||
for (int x = 0; x < (count / BYTES_PER_RAW_SAMPLE); x++)
|
||||
{
|
||||
target[x] = (float) t[x] / 16384.0f;
|
||||
}
|
||||
|
||||
buffer->SetSamples(count / BYTES_PER_RAW_SAMPLE / channels);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -30,28 +30,28 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <string>
|
||||
#include "CddaDecoderFactory.h"
|
||||
#include "CddaDecoder.h"
|
||||
|
||||
CddaDecoderFactory::CddaDecoderFactory() {
|
||||
}
|
||||
|
||||
CddaDecoderFactory::~CddaDecoderFactory() {
|
||||
}
|
||||
|
||||
void CddaDecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* CddaDecoderFactory::CreateDecoder() {
|
||||
return new CddaDecoder();
|
||||
}
|
||||
|
||||
bool CddaDecoderFactory::CanHandle(const char* source) const {
|
||||
std::string type(source);
|
||||
return type == "cda";
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <string>
|
||||
#include "CddaDecoderFactory.h"
|
||||
#include "CddaDecoder.h"
|
||||
|
||||
CddaDecoderFactory::CddaDecoderFactory() {
|
||||
}
|
||||
|
||||
CddaDecoderFactory::~CddaDecoderFactory() {
|
||||
}
|
||||
|
||||
void CddaDecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* CddaDecoderFactory::CreateDecoder() {
|
||||
return new CddaDecoder();
|
||||
}
|
||||
|
||||
bool CddaDecoderFactory::CanHandle(const char* source) const {
|
||||
std::string type(source);
|
||||
return type == "cda";
|
||||
}
|
||||
|
@ -30,33 +30,33 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "CddaDecoderFactory.h"
|
||||
#include "CddaDataStreamFactory.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
|
||||
class CddaDecoderPlugin : public musik::core::IPlugin {
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "CD Audio (CDDA) IDecoder"; };
|
||||
const char* Version() { return "0.2"; };
|
||||
const char* Author() { return "Björn Olievier, clangen"; };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin() {
|
||||
return new CddaDecoderPlugin();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) IDecoderFactory* GetDecoderFactory() {
|
||||
return new CddaDecoderFactory();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) IDataStreamFactory* GetDataStreamFactory() {
|
||||
return new CddaDataStreamFactory();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "CddaDecoderFactory.h"
|
||||
#include "CddaDataStreamFactory.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
|
||||
class CddaDecoderPlugin : public musik::core::IPlugin {
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "CD Audio (CDDA) IDecoder"; };
|
||||
const char* Version() { return "0.2"; };
|
||||
const char* Author() { return "Björn Olievier, clangen"; };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin() {
|
||||
return new CddaDecoderPlugin();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) IDecoderFactory* GetDecoderFactory() {
|
||||
return new CddaDecoderFactory();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) IDataStreamFactory* GetDataStreamFactory() {
|
||||
return new CddaDataStreamFactory();
|
||||
}
|
||||
|
@ -30,6 +30,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
@ -1,64 +1,64 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/sdk/IOutput.h>
|
||||
#include "CoreAudioOut.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class CoreAudioOutPlugin : public musik::core::IPlugin {
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "CoreAudio IOutput"; };
|
||||
const char* Version() { return "0.1"; };
|
||||
const char* Author() { return "clangen"; };
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new CoreAudioOutPlugin;
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT musik::core::audio::IOutput* GetAudioOutput() {
|
||||
return new CoreAudioOut();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/sdk/IOutput.h>
|
||||
#include "CoreAudioOut.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class CoreAudioOutPlugin : public musik::core::IPlugin {
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "CoreAudio IOutput"; };
|
||||
const char* Version() { return "0.1"; };
|
||||
const char* Author() { return "clangen"; };
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new CoreAudioOutPlugin;
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT musik::core::audio::IOutput* GetAudioOutput() {
|
||||
return new CoreAudioOut();
|
||||
}
|
||||
|
@ -1,102 +1,102 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2007, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "pch.h"
|
||||
#include "DSPEcho.h"
|
||||
|
||||
DSPEcho::DSPEcho()
|
||||
:internalBuffer(NULL)
|
||||
,channels(0)
|
||||
,bufferSampleSize(0)
|
||||
,bufferPosition(0)
|
||||
,sampleRate(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DSPEcho::~DSPEcho(){
|
||||
delete this->internalBuffer;
|
||||
}
|
||||
|
||||
|
||||
void DSPEcho::Destroy(){
|
||||
delete this;
|
||||
}
|
||||
|
||||
void DSPEcho::SetBuffer(const IBuffer *inputBuffer){
|
||||
if(inputBuffer->Channels()!=this->channels || inputBuffer->SampleRate()!=this->sampleRate){
|
||||
|
||||
this->channels = inputBuffer->Channels();
|
||||
this->sampleRate = inputBuffer->SampleRate();
|
||||
this->bufferSampleSize = this->sampleRate; // 1 second
|
||||
|
||||
delete this->internalBuffer;
|
||||
this->internalBuffer = new float[this->channels*this->bufferSampleSize];
|
||||
|
||||
// empty the internal buffer
|
||||
for(long i=0;i<this->channels*this->bufferSampleSize;++i){
|
||||
this->internalBuffer[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DSPEcho::ProcessBuffers(const IBuffer *inputBuffer,IBuffer *outputBuffer){
|
||||
this->SetBuffer(inputBuffer);
|
||||
|
||||
// loop though the buffer and apply echo
|
||||
long bufferLength( this->channels*inputBuffer->Samples() );
|
||||
float *inBuffer = inputBuffer->BufferPointer();
|
||||
float *outBuffer = outputBuffer->BufferPointer();
|
||||
long internalBufferSize(this->channels*this->bufferSampleSize);
|
||||
|
||||
long echo1distance(internalBufferSize-((long)(0.3*(double)this->sampleRate))*this->channels);
|
||||
long echo2distance(internalBufferSize-((long)(0.2*(double)this->sampleRate))*this->channels);
|
||||
|
||||
for(long i(0);i<bufferLength;++i){
|
||||
float inSample(inBuffer[i]);
|
||||
float outSample(inSample);
|
||||
//add a 0.5 of 0.3 seconds away
|
||||
//outSample += 0.5f*this->internalBuffer[(this->bufferPosition+echo1distance)%internalBufferSize];
|
||||
//add a 0.2 of 0.6 seconds away
|
||||
outSample += 0.2f*this->internalBuffer[(this->bufferPosition+echo2distance)%internalBufferSize];
|
||||
|
||||
// set the out buffer
|
||||
outBuffer[i] = outSample;
|
||||
// Save the insample to internal buffer
|
||||
this->internalBuffer[this->bufferPosition] = outSample;
|
||||
this->bufferPosition = (this->bufferPosition+1)%internalBufferSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2007, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "pch.h"
|
||||
#include "DSPEcho.h"
|
||||
|
||||
DSPEcho::DSPEcho()
|
||||
:internalBuffer(NULL)
|
||||
,channels(0)
|
||||
,bufferSampleSize(0)
|
||||
,bufferPosition(0)
|
||||
,sampleRate(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DSPEcho::~DSPEcho(){
|
||||
delete this->internalBuffer;
|
||||
}
|
||||
|
||||
|
||||
void DSPEcho::Destroy(){
|
||||
delete this;
|
||||
}
|
||||
|
||||
void DSPEcho::SetBuffer(const IBuffer *inputBuffer){
|
||||
if(inputBuffer->Channels()!=this->channels || inputBuffer->SampleRate()!=this->sampleRate){
|
||||
|
||||
this->channels = inputBuffer->Channels();
|
||||
this->sampleRate = inputBuffer->SampleRate();
|
||||
this->bufferSampleSize = this->sampleRate; // 1 second
|
||||
|
||||
delete this->internalBuffer;
|
||||
this->internalBuffer = new float[this->channels*this->bufferSampleSize];
|
||||
|
||||
// empty the internal buffer
|
||||
for(long i=0;i<this->channels*this->bufferSampleSize;++i){
|
||||
this->internalBuffer[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DSPEcho::ProcessBuffers(const IBuffer *inputBuffer,IBuffer *outputBuffer){
|
||||
this->SetBuffer(inputBuffer);
|
||||
|
||||
// loop though the buffer and apply echo
|
||||
long bufferLength( this->channels*inputBuffer->Samples() );
|
||||
float *inBuffer = inputBuffer->BufferPointer();
|
||||
float *outBuffer = outputBuffer->BufferPointer();
|
||||
long internalBufferSize(this->channels*this->bufferSampleSize);
|
||||
|
||||
long echo1distance(internalBufferSize-((long)(0.3*(double)this->sampleRate))*this->channels);
|
||||
long echo2distance(internalBufferSize-((long)(0.2*(double)this->sampleRate))*this->channels);
|
||||
|
||||
for(long i(0);i<bufferLength;++i){
|
||||
float inSample(inBuffer[i]);
|
||||
float outSample(inSample);
|
||||
//add a 0.5 of 0.3 seconds away
|
||||
//outSample += 0.5f*this->internalBuffer[(this->bufferPosition+echo1distance)%internalBufferSize];
|
||||
//add a 0.2 of 0.6 seconds away
|
||||
outSample += 0.2f*this->internalBuffer[(this->bufferPosition+echo2distance)%internalBufferSize];
|
||||
|
||||
// set the out buffer
|
||||
outBuffer[i] = outSample;
|
||||
// Save the insample to internal buffer
|
||||
this->internalBuffer[this->bufferPosition] = outSample;
|
||||
this->bufferPosition = (this->bufferPosition+1)%internalBufferSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,66 +1,66 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <core/IPlugin.h>
|
||||
#include "DSPEcho.h"
|
||||
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
class DSPEchoPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; };
|
||||
|
||||
const utfchar* Name() { return TEXT("DSP echo example"); };
|
||||
const utfchar* Version() { return TEXT("1"); };
|
||||
const utfchar* Author() { return TEXT("Daniel Önnerby"); };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin()
|
||||
{
|
||||
return new DSPEchoPlugin();
|
||||
}
|
||||
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::audio::IDSP* GetDSP()
|
||||
{
|
||||
return new DSPEcho();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <core/IPlugin.h>
|
||||
#include "DSPEcho.h"
|
||||
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
class DSPEchoPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; };
|
||||
|
||||
const utfchar* Name() { return TEXT("DSP echo example"); };
|
||||
const utfchar* Version() { return TEXT("1"); };
|
||||
const utfchar* Author() { return TEXT("Daniel Önnerby"); };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin()
|
||||
{
|
||||
return new DSPEchoPlugin();
|
||||
}
|
||||
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::audio::IDSP* GetDSP()
|
||||
{
|
||||
return new DSPEcho();
|
||||
}
|
||||
|
@ -1,37 +1,37 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
@ -30,216 +30,216 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "FlacDecoder.h"
|
||||
#include <complex>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
static inline void copy(float* dst, float* src, size_t count) {
|
||||
#ifdef WIN32
|
||||
CopyMemory(dst, src, count * sizeof(float));
|
||||
#else
|
||||
memcpy(dst, src, count * sizeof(float));
|
||||
#endif
|
||||
}
|
||||
|
||||
FlacDecoder::FlacDecoder()
|
||||
: decoder(NULL)
|
||||
, outputBufferSize(0)
|
||||
, outputBufferUsed(0)
|
||||
, outputBuffer(NULL)
|
||||
, channels(0)
|
||||
, sampleRate(0)
|
||||
, bitsPerSample(0)
|
||||
, totalSamples(0) {
|
||||
this->decoder = FLAC__stream_decoder_new();
|
||||
}
|
||||
|
||||
FlacDecoder::~FlacDecoder() {
|
||||
if (this->decoder) {
|
||||
FLAC__stream_decoder_delete(this->decoder);
|
||||
this->decoder = NULL;
|
||||
}
|
||||
|
||||
if (this->outputBuffer) {
|
||||
delete this->outputBuffer;
|
||||
this->outputBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderReadStatus FlacDecoder::FlacRead(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__byte buffer[],
|
||||
size_t *bytes,
|
||||
void *clientData)
|
||||
{
|
||||
size_t readBytes = (size_t)((FlacDecoder*) clientData)->stream->Read(buffer,(long)(*bytes));
|
||||
*bytes = readBytes;
|
||||
|
||||
if (readBytes == 0) {
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
||||
}
|
||||
else if(readBytes == (size_t) -1) {
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
FLAC__bool FlacDecoder::FlacEof(const FLAC__StreamDecoder *decoder, void *clientData) {
|
||||
return ((FlacDecoder*) clientData)->stream->Eof() ? 1 : 0;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderSeekStatus FlacDecoder::FlacSeek(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__uint64 absolute_byte_offset,
|
||||
void *clientData)
|
||||
{
|
||||
if(((FlacDecoder*) clientData)->stream->SetPosition((long) absolute_byte_offset)) {
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderTellStatus FlacDecoder::FlacTell(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__uint64 *absolute_byte_offset,
|
||||
void *clientData)
|
||||
{
|
||||
*absolute_byte_offset = (FLAC__uint64)((FlacDecoder*) clientData)->stream->Position();
|
||||
|
||||
if(*absolute_byte_offset == (FLAC__uint64) -1) {
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderLengthStatus FlacDecoder::FlacFileSize(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__uint64 *stream_length,
|
||||
void *clientData)
|
||||
{
|
||||
*stream_length = (FLAC__uint64)((FlacDecoder*) clientData)->stream->Length();
|
||||
|
||||
if(*stream_length <= 0) {
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
}
|
||||
|
||||
bool FlacDecoder::Open(musik::core::io::IDataStream *stream){
|
||||
this->stream = stream;
|
||||
|
||||
FLAC__StreamDecoderInitStatus init_status =
|
||||
FLAC__stream_decoder_init_stream(
|
||||
this->decoder,
|
||||
FlacDecoder::FlacRead,
|
||||
FlacDecoder::FlacSeek,
|
||||
FlacDecoder::FlacTell,
|
||||
FlacDecoder::FlacFileSize,
|
||||
FlacDecoder::FlacEof,
|
||||
FlacDecoder::FlacWrite,
|
||||
FlacDecoder::FlacMetadata,
|
||||
FlacDecoder::FlacError,
|
||||
this);
|
||||
|
||||
if (init_status == FLAC__STREAM_DECODER_INIT_STATUS_OK) {
|
||||
FLAC__stream_decoder_process_until_end_of_metadata(this->decoder);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FlacDecoder::FlacError(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__StreamDecoderErrorStatus status,
|
||||
void *clientData)
|
||||
{
|
||||
/* nothing for us to do here... */
|
||||
}
|
||||
|
||||
void FlacDecoder::FlacMetadata(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
const FLAC__StreamMetadata *metadata,
|
||||
void *clientData)
|
||||
{
|
||||
FlacDecoder *fdec = (FlacDecoder*)clientData;
|
||||
|
||||
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
|
||||
fdec->totalSamples = metadata->data.stream_info.total_samples;
|
||||
fdec->sampleRate = metadata->data.stream_info.sample_rate;
|
||||
fdec->channels = metadata->data.stream_info.channels;
|
||||
fdec->bitsPerSample = metadata->data.stream_info.bits_per_sample;
|
||||
}
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderWriteStatus FlacDecoder::FlacWrite(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
const FLAC__Frame *frame,
|
||||
const FLAC__int32 *const buffer[],
|
||||
void *clientData)
|
||||
{
|
||||
FlacDecoder *fdec = (FlacDecoder*) clientData;
|
||||
int sampleCount = fdec->channels * frame->header.blocksize;
|
||||
|
||||
/* initialize the output buffer if it doesn't exist */
|
||||
if (sampleCount > fdec->outputBufferSize) {
|
||||
delete fdec->outputBuffer;
|
||||
fdec->outputBuffer = NULL;
|
||||
fdec->outputBufferSize = sampleCount;
|
||||
fdec->outputBuffer = new float[sampleCount];
|
||||
}
|
||||
|
||||
/* we need to convert the fixed point samples to floating point samples. figure
|
||||
out the maximum amplitude of the fixed point samples based on the resolution */
|
||||
float maxAmplitude = pow(2.0f, (fdec->bitsPerSample - 1));
|
||||
|
||||
/* run the conversion */
|
||||
fdec->outputBufferUsed = 0;
|
||||
for (unsigned int i = 0; i < frame->header.blocksize; ++i) {
|
||||
for (int j = 0; j < fdec->channels; ++j) {
|
||||
fdec->outputBuffer[fdec->outputBufferUsed] = (((float) buffer[j][i]) / maxAmplitude);
|
||||
fdec->outputBufferUsed++;
|
||||
}
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
void FlacDecoder::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
double FlacDecoder::SetPosition(double seconds) {
|
||||
FLAC__uint64 seekToSample = (FLAC__uint64)(this->sampleRate * seconds);
|
||||
|
||||
if (FLAC__stream_decoder_seek_absolute(this->decoder, seekToSample)) {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool FlacDecoder::GetBuffer(IBuffer *buffer) {
|
||||
buffer->SetSampleRate(this->sampleRate);
|
||||
buffer->SetChannels(this->channels);
|
||||
|
||||
/* read the next chunk */
|
||||
if (FLAC__stream_decoder_process_single(this->decoder)) {
|
||||
if (this->outputBuffer && this->outputBufferUsed > 0) {
|
||||
buffer->SetSamples(this->outputBufferUsed / this->channels);
|
||||
copy(buffer->BufferPointer(), this->outputBuffer, this->outputBufferUsed);
|
||||
this->outputBufferUsed = 0; /* mark consumed */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "FlacDecoder.h"
|
||||
#include <complex>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
static inline void copy(float* dst, float* src, size_t count) {
|
||||
#ifdef WIN32
|
||||
CopyMemory(dst, src, count * sizeof(float));
|
||||
#else
|
||||
memcpy(dst, src, count * sizeof(float));
|
||||
#endif
|
||||
}
|
||||
|
||||
FlacDecoder::FlacDecoder()
|
||||
: decoder(NULL)
|
||||
, outputBufferSize(0)
|
||||
, outputBufferUsed(0)
|
||||
, outputBuffer(NULL)
|
||||
, channels(0)
|
||||
, sampleRate(0)
|
||||
, bitsPerSample(0)
|
||||
, totalSamples(0) {
|
||||
this->decoder = FLAC__stream_decoder_new();
|
||||
}
|
||||
|
||||
FlacDecoder::~FlacDecoder() {
|
||||
if (this->decoder) {
|
||||
FLAC__stream_decoder_delete(this->decoder);
|
||||
this->decoder = NULL;
|
||||
}
|
||||
|
||||
if (this->outputBuffer) {
|
||||
delete this->outputBuffer;
|
||||
this->outputBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderReadStatus FlacDecoder::FlacRead(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__byte buffer[],
|
||||
size_t *bytes,
|
||||
void *clientData)
|
||||
{
|
||||
size_t readBytes = (size_t)((FlacDecoder*) clientData)->stream->Read(buffer,(long)(*bytes));
|
||||
*bytes = readBytes;
|
||||
|
||||
if (readBytes == 0) {
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
||||
}
|
||||
else if(readBytes == (size_t) -1) {
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
FLAC__bool FlacDecoder::FlacEof(const FLAC__StreamDecoder *decoder, void *clientData) {
|
||||
return ((FlacDecoder*) clientData)->stream->Eof() ? 1 : 0;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderSeekStatus FlacDecoder::FlacSeek(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__uint64 absolute_byte_offset,
|
||||
void *clientData)
|
||||
{
|
||||
if(((FlacDecoder*) clientData)->stream->SetPosition((long) absolute_byte_offset)) {
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderTellStatus FlacDecoder::FlacTell(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__uint64 *absolute_byte_offset,
|
||||
void *clientData)
|
||||
{
|
||||
*absolute_byte_offset = (FLAC__uint64)((FlacDecoder*) clientData)->stream->Position();
|
||||
|
||||
if(*absolute_byte_offset == (FLAC__uint64) -1) {
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderLengthStatus FlacDecoder::FlacFileSize(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__uint64 *stream_length,
|
||||
void *clientData)
|
||||
{
|
||||
*stream_length = (FLAC__uint64)((FlacDecoder*) clientData)->stream->Length();
|
||||
|
||||
if(*stream_length <= 0) {
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
}
|
||||
|
||||
bool FlacDecoder::Open(musik::core::io::IDataStream *stream){
|
||||
this->stream = stream;
|
||||
|
||||
FLAC__StreamDecoderInitStatus init_status =
|
||||
FLAC__stream_decoder_init_stream(
|
||||
this->decoder,
|
||||
FlacDecoder::FlacRead,
|
||||
FlacDecoder::FlacSeek,
|
||||
FlacDecoder::FlacTell,
|
||||
FlacDecoder::FlacFileSize,
|
||||
FlacDecoder::FlacEof,
|
||||
FlacDecoder::FlacWrite,
|
||||
FlacDecoder::FlacMetadata,
|
||||
FlacDecoder::FlacError,
|
||||
this);
|
||||
|
||||
if (init_status == FLAC__STREAM_DECODER_INIT_STATUS_OK) {
|
||||
FLAC__stream_decoder_process_until_end_of_metadata(this->decoder);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FlacDecoder::FlacError(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
FLAC__StreamDecoderErrorStatus status,
|
||||
void *clientData)
|
||||
{
|
||||
/* nothing for us to do here... */
|
||||
}
|
||||
|
||||
void FlacDecoder::FlacMetadata(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
const FLAC__StreamMetadata *metadata,
|
||||
void *clientData)
|
||||
{
|
||||
FlacDecoder *fdec = (FlacDecoder*)clientData;
|
||||
|
||||
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
|
||||
fdec->totalSamples = metadata->data.stream_info.total_samples;
|
||||
fdec->sampleRate = metadata->data.stream_info.sample_rate;
|
||||
fdec->channels = metadata->data.stream_info.channels;
|
||||
fdec->bitsPerSample = metadata->data.stream_info.bits_per_sample;
|
||||
}
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderWriteStatus FlacDecoder::FlacWrite(
|
||||
const FLAC__StreamDecoder *decoder,
|
||||
const FLAC__Frame *frame,
|
||||
const FLAC__int32 *const buffer[],
|
||||
void *clientData)
|
||||
{
|
||||
FlacDecoder *fdec = (FlacDecoder*) clientData;
|
||||
int sampleCount = fdec->channels * frame->header.blocksize;
|
||||
|
||||
/* initialize the output buffer if it doesn't exist */
|
||||
if (sampleCount > fdec->outputBufferSize) {
|
||||
delete fdec->outputBuffer;
|
||||
fdec->outputBuffer = NULL;
|
||||
fdec->outputBufferSize = sampleCount;
|
||||
fdec->outputBuffer = new float[sampleCount];
|
||||
}
|
||||
|
||||
/* we need to convert the fixed point samples to floating point samples. figure
|
||||
out the maximum amplitude of the fixed point samples based on the resolution */
|
||||
float maxAmplitude = pow(2.0f, (fdec->bitsPerSample - 1));
|
||||
|
||||
/* run the conversion */
|
||||
fdec->outputBufferUsed = 0;
|
||||
for (unsigned int i = 0; i < frame->header.blocksize; ++i) {
|
||||
for (int j = 0; j < fdec->channels; ++j) {
|
||||
fdec->outputBuffer[fdec->outputBufferUsed] = (((float) buffer[j][i]) / maxAmplitude);
|
||||
fdec->outputBufferUsed++;
|
||||
}
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
void FlacDecoder::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
double FlacDecoder::SetPosition(double seconds) {
|
||||
FLAC__uint64 seekToSample = (FLAC__uint64)(this->sampleRate * seconds);
|
||||
|
||||
if (FLAC__stream_decoder_seek_absolute(this->decoder, seekToSample)) {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool FlacDecoder::GetBuffer(IBuffer *buffer) {
|
||||
buffer->SetSampleRate(this->sampleRate);
|
||||
buffer->SetChannels(this->channels);
|
||||
|
||||
/* read the next chunk */
|
||||
if (FLAC__stream_decoder_process_single(this->decoder)) {
|
||||
if (this->outputBuffer && this->outputBufferUsed > 0) {
|
||||
buffer->SetSamples(this->outputBufferUsed / this->channels);
|
||||
copy(buffer->BufferPointer(), this->outputBuffer, this->outputBufferUsed);
|
||||
this->outputBufferUsed = 0; /* mark consumed */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -30,37 +30,37 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "FlacDecoderFactory.h"
|
||||
#include "FlacDecoder.h"
|
||||
|
||||
using namespace musik::core::audio;
|
||||
|
||||
FlacDecoderFactory::FlacDecoderFactory() {
|
||||
}
|
||||
|
||||
FlacDecoderFactory::~FlacDecoderFactory() {
|
||||
}
|
||||
|
||||
void FlacDecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* FlacDecoderFactory::CreateDecoder() {
|
||||
return new FlacDecoder();
|
||||
}
|
||||
|
||||
bool FlacDecoderFactory::CanHandle(const char* type) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
return
|
||||
musik::sdk::endsWith(type, ".flac") ||
|
||||
str.find("audio/flag") != std::string::npos;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "FlacDecoderFactory.h"
|
||||
#include "FlacDecoder.h"
|
||||
|
||||
using namespace musik::core::audio;
|
||||
|
||||
FlacDecoderFactory::FlacDecoderFactory() {
|
||||
}
|
||||
|
||||
FlacDecoderFactory::~FlacDecoderFactory() {
|
||||
}
|
||||
|
||||
void FlacDecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* FlacDecoderFactory::CreateDecoder() {
|
||||
return new FlacDecoder();
|
||||
}
|
||||
|
||||
bool FlacDecoderFactory::CanHandle(const char* type) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
return
|
||||
musik::sdk::endsWith(type, ".flac") ||
|
||||
str.find("audio/flag") != std::string::npos;
|
||||
}
|
||||
|
@ -30,36 +30,36 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include "FlacDecoderFactory.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class FlacPlugin : public musik::core::IPlugin {
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "FLAC IDecoder"; }
|
||||
const char* Version() { return "0.2"; }
|
||||
const char* Author() { return "Daniel Önnerby, clangen"; }
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new FlacPlugin();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT musik::core::audio::IDecoderFactory* GetDecoderFactory() {
|
||||
return new FlacDecoderFactory();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include "FlacDecoderFactory.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class FlacPlugin : public musik::core::IPlugin {
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "FLAC IDecoder"; }
|
||||
const char* Version() { return "0.2"; }
|
||||
const char* Author() { return "Daniel Önnerby, clangen"; }
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new FlacPlugin();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT musik::core::audio::IDecoderFactory* GetDecoderFactory() {
|
||||
return new FlacDecoderFactory();
|
||||
}
|
||||
|
@ -30,6 +30,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
@ -30,195 +30,195 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "M4aDecoder.h"
|
||||
#include <string.h>
|
||||
|
||||
using musik::core::io::IDataStream;
|
||||
using musik::core::audio::IBuffer;
|
||||
|
||||
static uint32_t streamReadCallback(void *userData, void *buffer, uint32_t length) {
|
||||
IDataStream *stream = static_cast<IDataStream*>(userData);
|
||||
return (uint32_t) stream->Read(buffer, length);
|
||||
}
|
||||
|
||||
static uint32_t streamSeekCallback(void *userData, uint64_t position) {
|
||||
IDataStream *stream = static_cast<IDataStream*>(userData);
|
||||
return (uint32_t) stream->SetPosition((long) position);
|
||||
}
|
||||
|
||||
static int FindAacTrack(mp4ff_t *infile) {
|
||||
int i, rc;
|
||||
int numTracks = mp4ff_total_tracks(infile);
|
||||
|
||||
for (i = 0; i < numTracks; i++) {
|
||||
unsigned char *buff = NULL;
|
||||
int size = 0;
|
||||
mp4AudioSpecificConfig mp4ASC;
|
||||
|
||||
mp4ff_get_decoder_config(infile, i, &buff, (unsigned int *) &size);
|
||||
|
||||
if (buff) {
|
||||
rc = NeAACDecAudioSpecificConfig(buff, size, &mp4ASC);
|
||||
free(buff);
|
||||
|
||||
if (rc < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/* can't decode this */
|
||||
return -1;
|
||||
}
|
||||
|
||||
M4aDecoder::M4aDecoder() {
|
||||
this->decoder = NULL;
|
||||
this->decoderFile = NULL;
|
||||
memset(&decoderCallbacks, 0, sizeof(this->decoderCallbacks));
|
||||
}
|
||||
|
||||
M4aDecoder::~M4aDecoder() {
|
||||
}
|
||||
|
||||
bool M4aDecoder::Open(musik::core::io::IDataStream *stream)
|
||||
{
|
||||
decoder = NeAACDecOpen();
|
||||
if (!decoder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NeAACDecConfigurationPtr config;
|
||||
config = NeAACDecGetCurrentConfiguration(decoder);
|
||||
|
||||
config->outputFormat = FAAD_FMT_FLOAT;
|
||||
config->downMatrix = 0;
|
||||
|
||||
int ret = NeAACDecSetConfiguration(decoder, config);
|
||||
|
||||
decoderCallbacks.read = streamReadCallback;
|
||||
decoderCallbacks.seek = streamSeekCallback;
|
||||
decoderCallbacks.user_data = stream;
|
||||
|
||||
decoderFile = mp4ff_open_read(&decoderCallbacks);
|
||||
|
||||
if (decoderFile) {
|
||||
if ((audioTrackId = FindAacTrack(decoderFile)) >= 0) {
|
||||
unsigned char* buffer = NULL;
|
||||
unsigned int bufferSize = 0;
|
||||
|
||||
mp4ff_get_decoder_config(
|
||||
decoderFile, audioTrackId, &buffer, &bufferSize);
|
||||
|
||||
if (buffer) {
|
||||
if (NeAACDecInit2(
|
||||
decoder,
|
||||
buffer,
|
||||
bufferSize,
|
||||
&this->sampleRate,
|
||||
&this->channelCount) >= 0)
|
||||
{
|
||||
this->totalSamples = mp4ff_num_samples(decoderFile, audioTrackId);
|
||||
this->decoderSampleId = 0;
|
||||
free(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void M4aDecoder::Destroy(void)
|
||||
{
|
||||
mp4ff_close(decoderFile);
|
||||
|
||||
if (decoder) {
|
||||
NeAACDecClose(decoder);
|
||||
decoder = NULL;
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
double M4aDecoder::SetPosition(double seconds) {
|
||||
int64_t duration;
|
||||
float fms = (float) seconds * 1000;
|
||||
int32_t skip_samples = 0;
|
||||
|
||||
duration = (int64_t)(fms / 1000.0 * sampleRate + 0.5);
|
||||
|
||||
decoderSampleId = mp4ff_find_sample_use_offsets(
|
||||
decoderFile, audioTrackId, duration, &skip_samples);
|
||||
|
||||
return seconds;
|
||||
}
|
||||
|
||||
bool M4aDecoder::GetBuffer(IBuffer* target) {
|
||||
if (this->decoderSampleId < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* sampleBuffer = NULL;
|
||||
unsigned char* encodedData = NULL;
|
||||
unsigned int encodedDataLength = 0;
|
||||
NeAACDecFrameInfo frameInfo;
|
||||
|
||||
long duration = mp4ff_get_sample_duration(
|
||||
decoderFile, audioTrackId, decoderSampleId);
|
||||
|
||||
if (duration <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* read the raw data required */
|
||||
|
||||
int rc =
|
||||
mp4ff_read_sample(
|
||||
decoderFile,
|
||||
audioTrackId,
|
||||
decoderSampleId,
|
||||
&encodedData,
|
||||
&encodedDataLength);
|
||||
|
||||
decoderSampleId++;
|
||||
|
||||
if (rc == 0 || encodedData == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sampleBuffer =
|
||||
NeAACDecDecode(
|
||||
decoder,
|
||||
&frameInfo,
|
||||
encodedData,
|
||||
encodedDataLength);
|
||||
|
||||
free(encodedData);
|
||||
|
||||
if (frameInfo.error > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decoderSampleId > this->totalSamples) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target->SetSampleRate(frameInfo.samplerate);
|
||||
target->SetChannels(frameInfo.channels);
|
||||
target->SetSamples(frameInfo.samples / frameInfo.channels);
|
||||
|
||||
memcpy(
|
||||
static_cast<void*>(target->BufferPointer()),
|
||||
static_cast<void*>(sampleBuffer),
|
||||
sizeof(float) * frameInfo.samples);
|
||||
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "M4aDecoder.h"
|
||||
#include <string.h>
|
||||
|
||||
using musik::core::io::IDataStream;
|
||||
using musik::core::audio::IBuffer;
|
||||
|
||||
static uint32_t streamReadCallback(void *userData, void *buffer, uint32_t length) {
|
||||
IDataStream *stream = static_cast<IDataStream*>(userData);
|
||||
return (uint32_t) stream->Read(buffer, length);
|
||||
}
|
||||
|
||||
static uint32_t streamSeekCallback(void *userData, uint64_t position) {
|
||||
IDataStream *stream = static_cast<IDataStream*>(userData);
|
||||
return (uint32_t) stream->SetPosition((long) position);
|
||||
}
|
||||
|
||||
static int FindAacTrack(mp4ff_t *infile) {
|
||||
int i, rc;
|
||||
int numTracks = mp4ff_total_tracks(infile);
|
||||
|
||||
for (i = 0; i < numTracks; i++) {
|
||||
unsigned char *buff = NULL;
|
||||
int size = 0;
|
||||
mp4AudioSpecificConfig mp4ASC;
|
||||
|
||||
mp4ff_get_decoder_config(infile, i, &buff, (unsigned int *) &size);
|
||||
|
||||
if (buff) {
|
||||
rc = NeAACDecAudioSpecificConfig(buff, size, &mp4ASC);
|
||||
free(buff);
|
||||
|
||||
if (rc < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/* can't decode this */
|
||||
return -1;
|
||||
}
|
||||
|
||||
M4aDecoder::M4aDecoder() {
|
||||
this->decoder = NULL;
|
||||
this->decoderFile = NULL;
|
||||
memset(&decoderCallbacks, 0, sizeof(this->decoderCallbacks));
|
||||
}
|
||||
|
||||
M4aDecoder::~M4aDecoder() {
|
||||
}
|
||||
|
||||
bool M4aDecoder::Open(musik::core::io::IDataStream *stream)
|
||||
{
|
||||
decoder = NeAACDecOpen();
|
||||
if (!decoder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NeAACDecConfigurationPtr config;
|
||||
config = NeAACDecGetCurrentConfiguration(decoder);
|
||||
|
||||
config->outputFormat = FAAD_FMT_FLOAT;
|
||||
config->downMatrix = 0;
|
||||
|
||||
int ret = NeAACDecSetConfiguration(decoder, config);
|
||||
|
||||
decoderCallbacks.read = streamReadCallback;
|
||||
decoderCallbacks.seek = streamSeekCallback;
|
||||
decoderCallbacks.user_data = stream;
|
||||
|
||||
decoderFile = mp4ff_open_read(&decoderCallbacks);
|
||||
|
||||
if (decoderFile) {
|
||||
if ((audioTrackId = FindAacTrack(decoderFile)) >= 0) {
|
||||
unsigned char* buffer = NULL;
|
||||
unsigned int bufferSize = 0;
|
||||
|
||||
mp4ff_get_decoder_config(
|
||||
decoderFile, audioTrackId, &buffer, &bufferSize);
|
||||
|
||||
if (buffer) {
|
||||
if (NeAACDecInit2(
|
||||
decoder,
|
||||
buffer,
|
||||
bufferSize,
|
||||
&this->sampleRate,
|
||||
&this->channelCount) >= 0)
|
||||
{
|
||||
this->totalSamples = mp4ff_num_samples(decoderFile, audioTrackId);
|
||||
this->decoderSampleId = 0;
|
||||
free(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void M4aDecoder::Destroy(void)
|
||||
{
|
||||
mp4ff_close(decoderFile);
|
||||
|
||||
if (decoder) {
|
||||
NeAACDecClose(decoder);
|
||||
decoder = NULL;
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
double M4aDecoder::SetPosition(double seconds) {
|
||||
int64_t duration;
|
||||
float fms = (float) seconds * 1000;
|
||||
int32_t skip_samples = 0;
|
||||
|
||||
duration = (int64_t)(fms / 1000.0 * sampleRate + 0.5);
|
||||
|
||||
decoderSampleId = mp4ff_find_sample_use_offsets(
|
||||
decoderFile, audioTrackId, duration, &skip_samples);
|
||||
|
||||
return seconds;
|
||||
}
|
||||
|
||||
bool M4aDecoder::GetBuffer(IBuffer* target) {
|
||||
if (this->decoderSampleId < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* sampleBuffer = NULL;
|
||||
unsigned char* encodedData = NULL;
|
||||
unsigned int encodedDataLength = 0;
|
||||
NeAACDecFrameInfo frameInfo;
|
||||
|
||||
long duration = mp4ff_get_sample_duration(
|
||||
decoderFile, audioTrackId, decoderSampleId);
|
||||
|
||||
if (duration <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* read the raw data required */
|
||||
|
||||
int rc =
|
||||
mp4ff_read_sample(
|
||||
decoderFile,
|
||||
audioTrackId,
|
||||
decoderSampleId,
|
||||
&encodedData,
|
||||
&encodedDataLength);
|
||||
|
||||
decoderSampleId++;
|
||||
|
||||
if (rc == 0 || encodedData == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sampleBuffer =
|
||||
NeAACDecDecode(
|
||||
decoder,
|
||||
&frameInfo,
|
||||
encodedData,
|
||||
encodedDataLength);
|
||||
|
||||
free(encodedData);
|
||||
|
||||
if (frameInfo.error > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decoderSampleId > this->totalSamples) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target->SetSampleRate(frameInfo.samplerate);
|
||||
target->SetChannels(frameInfo.channels);
|
||||
target->SetSamples(frameInfo.samples / frameInfo.channels);
|
||||
|
||||
memcpy(
|
||||
static_cast<void*>(target->BufferPointer()),
|
||||
static_cast<void*>(sampleBuffer),
|
||||
sizeof(float) * frameInfo.samples);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -30,38 +30,38 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "M4aDecoderFactory.h"
|
||||
#include "M4aDecoder.h"
|
||||
#include <algorithm>
|
||||
|
||||
using musik::core::audio::IDecoder;
|
||||
|
||||
M4aDecoderFactory::M4aDecoderFactory() {
|
||||
}
|
||||
|
||||
M4aDecoderFactory::~M4aDecoderFactory() {
|
||||
}
|
||||
|
||||
void M4aDecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* M4aDecoderFactory::CreateDecoder() {
|
||||
return new M4aDecoder();
|
||||
}
|
||||
|
||||
bool M4aDecoderFactory::CanHandle(const char* type) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
return
|
||||
musik::sdk::endsWith(str, ".m4a") ||
|
||||
str.find("audio/mp4") != std::string::npos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "M4aDecoderFactory.h"
|
||||
#include "M4aDecoder.h"
|
||||
#include <algorithm>
|
||||
|
||||
using musik::core::audio::IDecoder;
|
||||
|
||||
M4aDecoderFactory::M4aDecoderFactory() {
|
||||
}
|
||||
|
||||
M4aDecoderFactory::~M4aDecoderFactory() {
|
||||
}
|
||||
|
||||
void M4aDecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* M4aDecoderFactory::CreateDecoder() {
|
||||
return new M4aDecoder();
|
||||
}
|
||||
|
||||
bool M4aDecoderFactory::CanHandle(const char* type) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
return
|
||||
musik::sdk::endsWith(str, ".m4a") ||
|
||||
str.find("audio/mp4") != std::string::npos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -30,6 +30,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
@ -1,459 +1,459 @@
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <math.h>
|
||||
#include "BaseDecoder.h"
|
||||
|
||||
const float CBaseDecoder::D[512] =
|
||||
{
|
||||
0.000000000f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000030518f,
|
||||
-0.000030518f, -0.000030518f, -0.000030518f, -0.000045776f, -0.000045776f, -0.000061035f, -0.000061035f, -0.000076294f,
|
||||
-0.000076294f, -0.000091553f, -0.000106812f, -0.000106812f, -0.000122070f, -0.000137329f, -0.000152588f, -0.000167847f,
|
||||
-0.000198364f, -0.000213623f, -0.000244141f, -0.000259399f, -0.000289917f, -0.000320435f, -0.000366211f, -0.000396729f,
|
||||
|
||||
-0.000442505f, -0.000473022f, -0.000534058f, -0.000579834f, -0.000625610f, -0.000686646f, -0.000747681f, -0.000808716f,
|
||||
-0.000885010f, -0.000961304f, -0.001037598f, -0.001113892f, -0.001205444f, -0.001296997f, -0.001388550f, -0.001480103f,
|
||||
-0.001586914f, -0.001693726f, -0.001785278f, -0.001907349f, -0.002014160f, -0.002120972f, -0.002243042f, -0.002349854f,
|
||||
-0.002456665f, -0.002578735f, -0.002685547f, -0.002792358f, -0.002899170f, -0.002990723f, -0.003082275f, -0.003173828f,
|
||||
|
||||
0.003250122f, 0.003326416f, 0.003387451f, 0.003433228f, 0.003463745f, 0.003479004f, 0.003479004f, 0.003463745f,
|
||||
0.003417969f, 0.003372192f, 0.003280640f, 0.003173828f, 0.003051758f, 0.002883911f, 0.002700806f, 0.002487183f,
|
||||
0.002227783f, 0.001937866f, 0.001617432f, 0.001266479f, 0.000869751f, 0.000442505f, -0.000030518f, -0.000549316f,
|
||||
-0.001098633f, -0.001693726f, -0.002334595f, -0.003005981f, -0.003723145f, -0.004486084f, -0.005294800f, -0.006118774f,
|
||||
|
||||
-0.007003784f, -0.007919312f, -0.008865356f, -0.009841919f, -0.010848999f, -0.011886597f, -0.012939453f, -0.014022827f,
|
||||
-0.015121460f, -0.016235352f, -0.017349243f, -0.018463135f, -0.019577026f, -0.020690918f, -0.021789551f, -0.022857666f,
|
||||
-0.023910522f, -0.024932861f, -0.025909424f, -0.026840210f, -0.027725220f, -0.028533936f, -0.029281616f, -0.029937744f,
|
||||
-0.030532837f, -0.031005859f, -0.031387329f, -0.031661987f, -0.031814575f, -0.031845093f, -0.031738281f, -0.031478882f,
|
||||
|
||||
0.031082153f, 0.030517578f, 0.029785156f, 0.028884888f, 0.027801514f, 0.026535034f, 0.025085449f, 0.023422241f,
|
||||
0.021575928f, 0.019531250f, 0.017257690f, 0.014801025f, 0.012115479f, 0.009231567f, 0.006134033f, 0.002822876f,
|
||||
-0.000686646f, -0.004394531f, -0.008316040f, -0.012420654f, -0.016708374f, -0.021179199f, -0.025817871f, -0.030609131f,
|
||||
-0.035552979f, -0.040634155f, -0.045837402f, -0.051132202f, -0.056533813f, -0.061996460f, -0.067520142f, -0.073059082f,
|
||||
|
||||
-0.078628540f, -0.084182739f, -0.089706421f, -0.095169067f, -0.100540161f, -0.105819702f, -0.110946655f, -0.115921021f,
|
||||
-0.120697021f, -0.125259399f, -0.129562378f, -0.133590698f, -0.137298584f, -0.140670776f, -0.143676758f, -0.146255493f,
|
||||
-0.148422241f, -0.150115967f, -0.151306152f, -0.151962280f, -0.152069092f, -0.151596069f, -0.150497437f, -0.148773193f,
|
||||
-0.146362305f, -0.143264771f, -0.139450073f, -0.134887695f, -0.129577637f, -0.123474121f, -0.116577148f, -0.108856201f,
|
||||
|
||||
0.100311279f, 0.090927124f, 0.080688477f, 0.069595337f, 0.057617187f, 0.044784546f, 0.031082153f, 0.016510010f,
|
||||
0.001068115f, -0.015228271f, -0.032379150f, -0.050354004f, -0.069168091f, -0.088775635f, -0.109161377f, -0.130310059f,
|
||||
-0.152206421f, -0.174789429f, -0.198059082f, -0.221984863f, -0.246505737f, -0.271591187f, -0.297210693f, -0.323318481f,
|
||||
-0.349868774f, -0.376800537f, -0.404083252f, -0.431655884f, -0.459472656f, -0.487472534f, -0.515609741f, -0.543823242f,
|
||||
|
||||
-0.572036743f, -0.600219727f, -0.628295898f, -0.656219482f, -0.683914185f, -0.711318970f, -0.738372803f, -0.765029907f,
|
||||
-0.791213989f, -0.816864014f, -0.841949463f, -0.866363525f, -0.890090942f, -0.913055420f, -0.935195923f, -0.956481934f,
|
||||
-0.976852417f, -0.996246338f, -1.014617920f, -1.031936646f, -1.048156738f, -1.063217163f, -1.077117920f, -1.089782715f,
|
||||
-1.101211548f, -1.111373901f, -1.120223999f, -1.127746582f, -1.133926392f, -1.138763428f, -1.142211914f, -1.144287109f,
|
||||
|
||||
1.144989014f, 1.144287109f, 1.142211914f, 1.138763428f, 1.133926392f, 1.127746582f, 1.120223999f, 1.111373901f,
|
||||
1.101211548f, 1.089782715f, 1.077117920f, 1.063217163f, 1.048156738f, 1.031936646f, 1.014617920f, 0.996246338f,
|
||||
0.976852417f, 0.956481934f, 0.935195923f, 0.913055420f, 0.890090942f, 0.866363525f, 0.841949463f, 0.816864014f,
|
||||
0.791213989f, 0.765029907f, 0.738372803f, 0.711318970f, 0.683914185f, 0.656219482f, 0.628295898f, 0.600219727f,
|
||||
|
||||
0.572036743f, 0.543823242f, 0.515609741f, 0.487472534f, 0.459472656f, 0.431655884f, 0.404083252f, 0.376800537f,
|
||||
0.349868774f, 0.323318481f, 0.297210693f, 0.271591187f, 0.246505737f, 0.221984863f, 0.198059082f, 0.174789429f,
|
||||
0.152206421f, 0.130310059f, 0.109161377f, 0.088775635f, 0.069168091f, 0.050354004f, 0.032379150f, 0.015228271f,
|
||||
-0.001068115f, -0.016510010f, -0.031082153f, -0.044784546f, -0.057617187f, -0.069595337f, -0.080688477f, -0.090927124f,
|
||||
|
||||
0.100311279f, 0.108856201f, 0.116577148f, 0.123474121f, 0.129577637f, 0.134887695f, 0.139450073f, 0.143264771f,
|
||||
0.146362305f, 0.148773193f, 0.150497437f, 0.151596069f, 0.152069092f, 0.151962280f, 0.151306152f, 0.150115967f,
|
||||
0.148422241f, 0.146255493f, 0.143676758f, 0.140670776f, 0.137298584f, 0.133590698f, 0.129562378f, 0.125259399f,
|
||||
0.120697021f, 0.115921021f, 0.110946655f, 0.105819702f, 0.100540161f, 0.095169067f, 0.089706421f, 0.084182739f,
|
||||
|
||||
0.078628540f, 0.073059082f, 0.067520142f, 0.061996460f, 0.056533813f, 0.051132202f, 0.045837402f, 0.040634155f,
|
||||
0.035552979f, 0.030609131f, 0.025817871f, 0.021179199f, 0.016708374f, 0.012420654f, 0.008316040f, 0.004394531f,
|
||||
0.000686646f, -0.002822876f, -0.006134033f, -0.009231567f, -0.012115479f, -0.014801025f, -0.017257690f, -0.019531250f,
|
||||
-0.021575928f, -0.023422241f, -0.025085449f, -0.026535034f, -0.027801514f, -0.028884888f, -0.029785156f, -0.030517578f,
|
||||
|
||||
0.031082153f, 0.031478882f, 0.031738281f, 0.031845093f, 0.031814575f, 0.031661987f, 0.031387329f, 0.031005859f,
|
||||
0.030532837f, 0.029937744f, 0.029281616f, 0.028533936f, 0.027725220f, 0.026840210f, 0.025909424f, 0.024932861f,
|
||||
0.023910522f, 0.022857666f, 0.021789551f, 0.020690918f, 0.019577026f, 0.018463135f, 0.017349243f, 0.016235352f,
|
||||
0.015121460f, 0.014022827f, 0.012939453f, 0.011886597f, 0.010848999f, 0.009841919f, 0.008865356f, 0.007919312f,
|
||||
|
||||
0.007003784f, 0.006118774f, 0.005294800f, 0.004486084f, 0.003723145f, 0.003005981f, 0.002334595f, 0.001693726f,
|
||||
0.001098633f, 0.000549316f, 0.000030518f, -0.000442505f, -0.000869751f, -0.001266479f, -0.001617432f, -0.001937866f,
|
||||
-0.002227783f, -0.002487183f, -0.002700806f, -0.002883911f, -0.003051758f, -0.003173828f, -0.003280640f, -0.003372192f,
|
||||
-0.003417969f, -0.003463745f, -0.003479004f, -0.003479004f, -0.003463745f, -0.003433228f, -0.003387451f, -0.003326416f,
|
||||
|
||||
0.003250122f, 0.003173828f, 0.003082275f, 0.002990723f, 0.002899170f, 0.002792358f, 0.002685547f, 0.002578735f,
|
||||
0.002456665f, 0.002349854f, 0.002243042f, 0.002120972f, 0.002014160f, 0.001907349f, 0.001785278f, 0.001693726f,
|
||||
0.001586914f, 0.001480103f, 0.001388550f, 0.001296997f, 0.001205444f, 0.001113892f, 0.001037598f, 0.000961304f,
|
||||
0.000885010f, 0.000808716f, 0.000747681f, 0.000686646f, 0.000625610f, 0.000579834f, 0.000534058f, 0.000473022f,
|
||||
|
||||
0.000442505f, 0.000396729f, 0.000366211f, 0.000320435f, 0.000289917f, 0.000259399f, 0.000244141f, 0.000213623f,
|
||||
0.000198364f, 0.000167847f, 0.000152588f, 0.000137329f, 0.000122070f, 0.000106812f, 0.000106812f, 0.000091553f,
|
||||
0.000076294f, 0.000076294f, 0.000061035f, 0.000061035f, 0.000045776f, 0.000045776f, 0.000030518f, 0.000030518f,
|
||||
0.000030518f, 0.000030518f, 0.000015259f, 0.000015259f, 0.000015259f, 0.000015259f, 0.000015259f, 0.000015259f
|
||||
};
|
||||
|
||||
|
||||
CBaseDecoder::CBaseDecoder()
|
||||
{
|
||||
int i,j;
|
||||
float t16[16][16], t8[8][8];
|
||||
|
||||
/* assign and clear the subband synthesis V buffer */
|
||||
|
||||
for(i = 0; i < 2; i++)
|
||||
for(j = 0; j < 1024; j++)
|
||||
V[i][j] = (float)0.0f;
|
||||
|
||||
/* create the 16 matrixes */
|
||||
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
for(j = 0; j < 16; j++)
|
||||
{
|
||||
A16[i][j] = (float)cos((2*j+1)*i*PI/32);
|
||||
if(i == j || j == (i + 1))
|
||||
G16[i][j] = 1.0f;
|
||||
else
|
||||
G16[i][j] = 0.0f;
|
||||
|
||||
if(i == j)
|
||||
H16[i][j] = 1.0f/(2.0f*(float)cos((2*i+1)*PI/64));
|
||||
else
|
||||
H16[i][j] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/* create the 8 matrixes */
|
||||
|
||||
for(i = 0; i < 8; i++)
|
||||
{
|
||||
for(j = 0; j < 8; j++)
|
||||
{
|
||||
A8[i][j] = (float)cos((2*j+1)*i*PI/16);
|
||||
if(i == j || j == (i + 1))
|
||||
G8[i][j] = 1.0f;
|
||||
else
|
||||
G8[i][j] = 0.0f;
|
||||
|
||||
if(i == j)
|
||||
H8[i][j] = 1.0f/(2.0f*(float)cos((2*i+1)*PI/32));
|
||||
else
|
||||
H8[i][j] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate the B matrixes */
|
||||
|
||||
MultiplyMatrix16(A16, H16, t16);
|
||||
MultiplyMatrix16(G16, t16, B16);
|
||||
|
||||
MultiplyMatrix8(A8, H8, t8);
|
||||
MultiplyMatrix8(G8, t8, B8);
|
||||
|
||||
int a, o = 0;
|
||||
|
||||
for (j = 0; j < 32; j++)
|
||||
{
|
||||
a = j;
|
||||
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
DTable[o] = D[a];
|
||||
o++;
|
||||
|
||||
DTable[o] = D[a+32];
|
||||
o++;
|
||||
|
||||
DTable[o] = D[a+64];
|
||||
o++;
|
||||
|
||||
DTable[o] = D[a+96];
|
||||
o++;
|
||||
|
||||
a+=128;
|
||||
}
|
||||
}
|
||||
|
||||
Vpointer[0] = 64;
|
||||
Vpointer[1] = 64;
|
||||
}
|
||||
|
||||
CBaseDecoder::~CBaseDecoder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CBaseDecoder::PerformSynthesis(float *InputFreq, float *ToHere, int Channel, int step)
|
||||
{
|
||||
if(Channel)
|
||||
ToHere ++;
|
||||
|
||||
/* We have 18 time-vectors of 32 subband magnitudes each. For every
|
||||
vector of 32 magnitudes, the subband synthesis generates 32
|
||||
PCM samples, so the result of 18 of these is 18*32=576 samples.
|
||||
*/
|
||||
|
||||
/* advance the buffer position */
|
||||
Vpointer[Channel] = (Vpointer[Channel] - 64) & 0x3ff;
|
||||
|
||||
IDCT(InputFreq, &V[ Channel ][ Vpointer[Channel] ]);
|
||||
|
||||
/* 32*16=512 mac's */
|
||||
|
||||
Window(Channel, ToHere, step);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
void __forceinline CBaseDecoder::IDCT(float *input, float *out)
|
||||
{
|
||||
float odd[16];
|
||||
float t[32];
|
||||
|
||||
float s1, s2;
|
||||
|
||||
int i;
|
||||
int offset1 = 0;
|
||||
int offset = 0;
|
||||
int ti = 0; // table index
|
||||
|
||||
float even[16], ee[8], eo[8];
|
||||
|
||||
/* input butterflies - level 1 */
|
||||
/* 32 adds */
|
||||
|
||||
even[0] = input[0] + input[31];
|
||||
even[1] = input[1] + input[30];
|
||||
even[2] = input[2] + input[29];
|
||||
even[3] = input[3] + input[28];
|
||||
even[4] = input[4] + input[27];
|
||||
even[5] = input[5] + input[26];
|
||||
even[6] = input[6] + input[25];
|
||||
even[7] = input[7] + input[24];
|
||||
even[8] = input[8] + input[23];
|
||||
even[9] = input[9] + input[22];
|
||||
even[10] = input[10] + input[21];
|
||||
even[11] = input[11] + input[20];
|
||||
even[12] = input[12] + input[19];
|
||||
even[13] = input[13] + input[18];
|
||||
even[14] = input[14] + input[17];
|
||||
even[15] = input[15] + input[16];
|
||||
|
||||
odd[0] = input[0] - input[31];
|
||||
odd[1] = input[1] - input[30];
|
||||
odd[2] = input[2] - input[29];
|
||||
odd[3] = input[3] - input[28];
|
||||
odd[4] = input[4] - input[27];
|
||||
odd[5] = input[5] - input[26];
|
||||
odd[6] = input[6] - input[25];
|
||||
odd[7] = input[7] - input[24];
|
||||
odd[8] = input[8] - input[23];
|
||||
odd[9] = input[9] - input[22];
|
||||
odd[10] = input[10] - input[21];
|
||||
odd[11] = input[11] - input[20];
|
||||
odd[12] = input[12] - input[19];
|
||||
odd[13] = input[13] - input[18];
|
||||
odd[14] = input[14] - input[17];
|
||||
odd[15] = input[15] - input[16];
|
||||
|
||||
/* input butterflies - level 2 */
|
||||
/* 16 adds */
|
||||
|
||||
ee[0] = even[0] + even[15];
|
||||
ee[1] = even[1] + even[14];
|
||||
ee[2] = even[2] + even[13];
|
||||
ee[3] = even[3] + even[12];
|
||||
ee[4] = even[4] + even[11];
|
||||
ee[5] = even[5] + even[10];
|
||||
ee[6] = even[6] + even[9];
|
||||
ee[7] = even[7] + even[8];
|
||||
|
||||
eo[0] = even[0] - even[15];
|
||||
eo[1] = even[1] - even[14];
|
||||
eo[2] = even[2] - even[13];
|
||||
eo[3] = even[3] - even[12];
|
||||
eo[4] = even[4] - even[11];
|
||||
eo[5] = even[5] - even[10];
|
||||
eo[6] = even[6] - even[9];
|
||||
eo[7] = even[7] - even[8];
|
||||
|
||||
|
||||
/* multiply the even_even vector (ee) with the ee matrix (A8) */
|
||||
/* multiply the even_odd vector (eo) with the eo matrix (B8) */
|
||||
/* 128 muls, 128 adds */
|
||||
|
||||
i=8;
|
||||
|
||||
do
|
||||
{
|
||||
s1 = A8[ti][0] * ee[0];
|
||||
s1 += A8[ti][1] * ee[1];
|
||||
s1 += A8[ti][2] * ee[2];
|
||||
s1 += A8[ti][3] * ee[3];
|
||||
s1 += A8[ti][4] * ee[4];
|
||||
s1 += A8[ti][5] * ee[5];
|
||||
s1 += A8[ti][6] * ee[6];
|
||||
s1 += A8[ti][7] * ee[7];
|
||||
|
||||
s2 = B8[ti][0] * eo[0];
|
||||
s2 += B8[ti][1] * eo[1];
|
||||
s2 += B8[ti][2] * eo[2];
|
||||
s2 += B8[ti][3] * eo[3];
|
||||
s2 += B8[ti][4] * eo[4];
|
||||
s2 += B8[ti][5] * eo[5];
|
||||
s2 += B8[ti][6] * eo[6];
|
||||
s2 += B8[ti][7] * eo[7];
|
||||
|
||||
t[offset1] = s1;
|
||||
t[offset1+2] = s2;
|
||||
|
||||
offset1 += 4;
|
||||
ti++;
|
||||
} while(--i);
|
||||
|
||||
|
||||
/* multiply the odd vector (odd) with the odd matrix (B16) */
|
||||
/* 256 muls, 256 adds */
|
||||
|
||||
i = 16;
|
||||
ti = 0;
|
||||
|
||||
do
|
||||
{
|
||||
s1 = B16[ti][0] * odd[0];
|
||||
s1 += B16[ti][1] * odd[1];
|
||||
s1 += B16[ti][2] * odd[2];
|
||||
s1 += B16[ti][3] * odd[3];
|
||||
s1 += B16[ti][4] * odd[4];
|
||||
s1 += B16[ti][5] * odd[5];
|
||||
s1 += B16[ti][6] * odd[6];
|
||||
s1 += B16[ti][7] * odd[7];
|
||||
s1 += B16[ti][8] * odd[8];
|
||||
s1 += B16[ti][9] * odd[9];
|
||||
s1 += B16[ti][10] * odd[10];
|
||||
s1 += B16[ti][11] * odd[11];
|
||||
s1 += B16[ti][12] * odd[12];
|
||||
s1 += B16[ti][13] * odd[13];
|
||||
s1 += B16[ti][14] * odd[14];
|
||||
s1 += B16[ti][15] * odd[15];
|
||||
|
||||
t[offset+1] = s1;
|
||||
|
||||
offset += 2;
|
||||
ti++;
|
||||
} while(--i);
|
||||
|
||||
/* the output vector t now is expanded to 64 values using the
|
||||
symmetric property of the cosinus function */
|
||||
|
||||
i = 16;
|
||||
ti = 0;
|
||||
|
||||
do
|
||||
{
|
||||
out[ti] = t[ti+16];
|
||||
out[ti+17] = -t[31-ti];
|
||||
out[ti+32] = -t[16-ti];
|
||||
out[ti+48] = -t[ti];
|
||||
|
||||
ti++;
|
||||
|
||||
} while(--i);
|
||||
|
||||
out[16] = 0.0;
|
||||
}
|
||||
|
||||
void __forceinline CBaseDecoder::Window(int ch, float *S, int step)
|
||||
{
|
||||
int j, k;
|
||||
register float sum;
|
||||
|
||||
//int a = 0;
|
||||
|
||||
/* calculate 32 samples. each sample is the sum of 16 terms */
|
||||
|
||||
/* 15 */
|
||||
/* Sj = E W((j*16)+i) */
|
||||
/* i=0 */
|
||||
|
||||
|
||||
k = Vpointer[ch];
|
||||
float * TV = V[ch];
|
||||
float * DT = DTable;
|
||||
|
||||
for(j = 0; j < 32; j++)
|
||||
{
|
||||
sum = DT[0x0] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x1] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0x2] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x3] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0x4] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x5] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0x6] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x7] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0x8] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x9] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0xA] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0xB] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0xC] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0xD] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0xE] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0xF] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
DT += 16;
|
||||
|
||||
|
||||
if( sum < -1.0 )
|
||||
sum = -1.0;
|
||||
else if( sum > 1.0 )
|
||||
sum = 1.0;
|
||||
*S = sum;
|
||||
|
||||
S+=step;
|
||||
k++;
|
||||
|
||||
}
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <math.h>
|
||||
#include "BaseDecoder.h"
|
||||
|
||||
const float CBaseDecoder::D[512] =
|
||||
{
|
||||
0.000000000f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000030518f,
|
||||
-0.000030518f, -0.000030518f, -0.000030518f, -0.000045776f, -0.000045776f, -0.000061035f, -0.000061035f, -0.000076294f,
|
||||
-0.000076294f, -0.000091553f, -0.000106812f, -0.000106812f, -0.000122070f, -0.000137329f, -0.000152588f, -0.000167847f,
|
||||
-0.000198364f, -0.000213623f, -0.000244141f, -0.000259399f, -0.000289917f, -0.000320435f, -0.000366211f, -0.000396729f,
|
||||
|
||||
-0.000442505f, -0.000473022f, -0.000534058f, -0.000579834f, -0.000625610f, -0.000686646f, -0.000747681f, -0.000808716f,
|
||||
-0.000885010f, -0.000961304f, -0.001037598f, -0.001113892f, -0.001205444f, -0.001296997f, -0.001388550f, -0.001480103f,
|
||||
-0.001586914f, -0.001693726f, -0.001785278f, -0.001907349f, -0.002014160f, -0.002120972f, -0.002243042f, -0.002349854f,
|
||||
-0.002456665f, -0.002578735f, -0.002685547f, -0.002792358f, -0.002899170f, -0.002990723f, -0.003082275f, -0.003173828f,
|
||||
|
||||
0.003250122f, 0.003326416f, 0.003387451f, 0.003433228f, 0.003463745f, 0.003479004f, 0.003479004f, 0.003463745f,
|
||||
0.003417969f, 0.003372192f, 0.003280640f, 0.003173828f, 0.003051758f, 0.002883911f, 0.002700806f, 0.002487183f,
|
||||
0.002227783f, 0.001937866f, 0.001617432f, 0.001266479f, 0.000869751f, 0.000442505f, -0.000030518f, -0.000549316f,
|
||||
-0.001098633f, -0.001693726f, -0.002334595f, -0.003005981f, -0.003723145f, -0.004486084f, -0.005294800f, -0.006118774f,
|
||||
|
||||
-0.007003784f, -0.007919312f, -0.008865356f, -0.009841919f, -0.010848999f, -0.011886597f, -0.012939453f, -0.014022827f,
|
||||
-0.015121460f, -0.016235352f, -0.017349243f, -0.018463135f, -0.019577026f, -0.020690918f, -0.021789551f, -0.022857666f,
|
||||
-0.023910522f, -0.024932861f, -0.025909424f, -0.026840210f, -0.027725220f, -0.028533936f, -0.029281616f, -0.029937744f,
|
||||
-0.030532837f, -0.031005859f, -0.031387329f, -0.031661987f, -0.031814575f, -0.031845093f, -0.031738281f, -0.031478882f,
|
||||
|
||||
0.031082153f, 0.030517578f, 0.029785156f, 0.028884888f, 0.027801514f, 0.026535034f, 0.025085449f, 0.023422241f,
|
||||
0.021575928f, 0.019531250f, 0.017257690f, 0.014801025f, 0.012115479f, 0.009231567f, 0.006134033f, 0.002822876f,
|
||||
-0.000686646f, -0.004394531f, -0.008316040f, -0.012420654f, -0.016708374f, -0.021179199f, -0.025817871f, -0.030609131f,
|
||||
-0.035552979f, -0.040634155f, -0.045837402f, -0.051132202f, -0.056533813f, -0.061996460f, -0.067520142f, -0.073059082f,
|
||||
|
||||
-0.078628540f, -0.084182739f, -0.089706421f, -0.095169067f, -0.100540161f, -0.105819702f, -0.110946655f, -0.115921021f,
|
||||
-0.120697021f, -0.125259399f, -0.129562378f, -0.133590698f, -0.137298584f, -0.140670776f, -0.143676758f, -0.146255493f,
|
||||
-0.148422241f, -0.150115967f, -0.151306152f, -0.151962280f, -0.152069092f, -0.151596069f, -0.150497437f, -0.148773193f,
|
||||
-0.146362305f, -0.143264771f, -0.139450073f, -0.134887695f, -0.129577637f, -0.123474121f, -0.116577148f, -0.108856201f,
|
||||
|
||||
0.100311279f, 0.090927124f, 0.080688477f, 0.069595337f, 0.057617187f, 0.044784546f, 0.031082153f, 0.016510010f,
|
||||
0.001068115f, -0.015228271f, -0.032379150f, -0.050354004f, -0.069168091f, -0.088775635f, -0.109161377f, -0.130310059f,
|
||||
-0.152206421f, -0.174789429f, -0.198059082f, -0.221984863f, -0.246505737f, -0.271591187f, -0.297210693f, -0.323318481f,
|
||||
-0.349868774f, -0.376800537f, -0.404083252f, -0.431655884f, -0.459472656f, -0.487472534f, -0.515609741f, -0.543823242f,
|
||||
|
||||
-0.572036743f, -0.600219727f, -0.628295898f, -0.656219482f, -0.683914185f, -0.711318970f, -0.738372803f, -0.765029907f,
|
||||
-0.791213989f, -0.816864014f, -0.841949463f, -0.866363525f, -0.890090942f, -0.913055420f, -0.935195923f, -0.956481934f,
|
||||
-0.976852417f, -0.996246338f, -1.014617920f, -1.031936646f, -1.048156738f, -1.063217163f, -1.077117920f, -1.089782715f,
|
||||
-1.101211548f, -1.111373901f, -1.120223999f, -1.127746582f, -1.133926392f, -1.138763428f, -1.142211914f, -1.144287109f,
|
||||
|
||||
1.144989014f, 1.144287109f, 1.142211914f, 1.138763428f, 1.133926392f, 1.127746582f, 1.120223999f, 1.111373901f,
|
||||
1.101211548f, 1.089782715f, 1.077117920f, 1.063217163f, 1.048156738f, 1.031936646f, 1.014617920f, 0.996246338f,
|
||||
0.976852417f, 0.956481934f, 0.935195923f, 0.913055420f, 0.890090942f, 0.866363525f, 0.841949463f, 0.816864014f,
|
||||
0.791213989f, 0.765029907f, 0.738372803f, 0.711318970f, 0.683914185f, 0.656219482f, 0.628295898f, 0.600219727f,
|
||||
|
||||
0.572036743f, 0.543823242f, 0.515609741f, 0.487472534f, 0.459472656f, 0.431655884f, 0.404083252f, 0.376800537f,
|
||||
0.349868774f, 0.323318481f, 0.297210693f, 0.271591187f, 0.246505737f, 0.221984863f, 0.198059082f, 0.174789429f,
|
||||
0.152206421f, 0.130310059f, 0.109161377f, 0.088775635f, 0.069168091f, 0.050354004f, 0.032379150f, 0.015228271f,
|
||||
-0.001068115f, -0.016510010f, -0.031082153f, -0.044784546f, -0.057617187f, -0.069595337f, -0.080688477f, -0.090927124f,
|
||||
|
||||
0.100311279f, 0.108856201f, 0.116577148f, 0.123474121f, 0.129577637f, 0.134887695f, 0.139450073f, 0.143264771f,
|
||||
0.146362305f, 0.148773193f, 0.150497437f, 0.151596069f, 0.152069092f, 0.151962280f, 0.151306152f, 0.150115967f,
|
||||
0.148422241f, 0.146255493f, 0.143676758f, 0.140670776f, 0.137298584f, 0.133590698f, 0.129562378f, 0.125259399f,
|
||||
0.120697021f, 0.115921021f, 0.110946655f, 0.105819702f, 0.100540161f, 0.095169067f, 0.089706421f, 0.084182739f,
|
||||
|
||||
0.078628540f, 0.073059082f, 0.067520142f, 0.061996460f, 0.056533813f, 0.051132202f, 0.045837402f, 0.040634155f,
|
||||
0.035552979f, 0.030609131f, 0.025817871f, 0.021179199f, 0.016708374f, 0.012420654f, 0.008316040f, 0.004394531f,
|
||||
0.000686646f, -0.002822876f, -0.006134033f, -0.009231567f, -0.012115479f, -0.014801025f, -0.017257690f, -0.019531250f,
|
||||
-0.021575928f, -0.023422241f, -0.025085449f, -0.026535034f, -0.027801514f, -0.028884888f, -0.029785156f, -0.030517578f,
|
||||
|
||||
0.031082153f, 0.031478882f, 0.031738281f, 0.031845093f, 0.031814575f, 0.031661987f, 0.031387329f, 0.031005859f,
|
||||
0.030532837f, 0.029937744f, 0.029281616f, 0.028533936f, 0.027725220f, 0.026840210f, 0.025909424f, 0.024932861f,
|
||||
0.023910522f, 0.022857666f, 0.021789551f, 0.020690918f, 0.019577026f, 0.018463135f, 0.017349243f, 0.016235352f,
|
||||
0.015121460f, 0.014022827f, 0.012939453f, 0.011886597f, 0.010848999f, 0.009841919f, 0.008865356f, 0.007919312f,
|
||||
|
||||
0.007003784f, 0.006118774f, 0.005294800f, 0.004486084f, 0.003723145f, 0.003005981f, 0.002334595f, 0.001693726f,
|
||||
0.001098633f, 0.000549316f, 0.000030518f, -0.000442505f, -0.000869751f, -0.001266479f, -0.001617432f, -0.001937866f,
|
||||
-0.002227783f, -0.002487183f, -0.002700806f, -0.002883911f, -0.003051758f, -0.003173828f, -0.003280640f, -0.003372192f,
|
||||
-0.003417969f, -0.003463745f, -0.003479004f, -0.003479004f, -0.003463745f, -0.003433228f, -0.003387451f, -0.003326416f,
|
||||
|
||||
0.003250122f, 0.003173828f, 0.003082275f, 0.002990723f, 0.002899170f, 0.002792358f, 0.002685547f, 0.002578735f,
|
||||
0.002456665f, 0.002349854f, 0.002243042f, 0.002120972f, 0.002014160f, 0.001907349f, 0.001785278f, 0.001693726f,
|
||||
0.001586914f, 0.001480103f, 0.001388550f, 0.001296997f, 0.001205444f, 0.001113892f, 0.001037598f, 0.000961304f,
|
||||
0.000885010f, 0.000808716f, 0.000747681f, 0.000686646f, 0.000625610f, 0.000579834f, 0.000534058f, 0.000473022f,
|
||||
|
||||
0.000442505f, 0.000396729f, 0.000366211f, 0.000320435f, 0.000289917f, 0.000259399f, 0.000244141f, 0.000213623f,
|
||||
0.000198364f, 0.000167847f, 0.000152588f, 0.000137329f, 0.000122070f, 0.000106812f, 0.000106812f, 0.000091553f,
|
||||
0.000076294f, 0.000076294f, 0.000061035f, 0.000061035f, 0.000045776f, 0.000045776f, 0.000030518f, 0.000030518f,
|
||||
0.000030518f, 0.000030518f, 0.000015259f, 0.000015259f, 0.000015259f, 0.000015259f, 0.000015259f, 0.000015259f
|
||||
};
|
||||
|
||||
|
||||
CBaseDecoder::CBaseDecoder()
|
||||
{
|
||||
int i,j;
|
||||
float t16[16][16], t8[8][8];
|
||||
|
||||
/* assign and clear the subband synthesis V buffer */
|
||||
|
||||
for(i = 0; i < 2; i++)
|
||||
for(j = 0; j < 1024; j++)
|
||||
V[i][j] = (float)0.0f;
|
||||
|
||||
/* create the 16 matrixes */
|
||||
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
for(j = 0; j < 16; j++)
|
||||
{
|
||||
A16[i][j] = (float)cos((2*j+1)*i*PI/32);
|
||||
if(i == j || j == (i + 1))
|
||||
G16[i][j] = 1.0f;
|
||||
else
|
||||
G16[i][j] = 0.0f;
|
||||
|
||||
if(i == j)
|
||||
H16[i][j] = 1.0f/(2.0f*(float)cos((2*i+1)*PI/64));
|
||||
else
|
||||
H16[i][j] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/* create the 8 matrixes */
|
||||
|
||||
for(i = 0; i < 8; i++)
|
||||
{
|
||||
for(j = 0; j < 8; j++)
|
||||
{
|
||||
A8[i][j] = (float)cos((2*j+1)*i*PI/16);
|
||||
if(i == j || j == (i + 1))
|
||||
G8[i][j] = 1.0f;
|
||||
else
|
||||
G8[i][j] = 0.0f;
|
||||
|
||||
if(i == j)
|
||||
H8[i][j] = 1.0f/(2.0f*(float)cos((2*i+1)*PI/32));
|
||||
else
|
||||
H8[i][j] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate the B matrixes */
|
||||
|
||||
MultiplyMatrix16(A16, H16, t16);
|
||||
MultiplyMatrix16(G16, t16, B16);
|
||||
|
||||
MultiplyMatrix8(A8, H8, t8);
|
||||
MultiplyMatrix8(G8, t8, B8);
|
||||
|
||||
int a, o = 0;
|
||||
|
||||
for (j = 0; j < 32; j++)
|
||||
{
|
||||
a = j;
|
||||
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
DTable[o] = D[a];
|
||||
o++;
|
||||
|
||||
DTable[o] = D[a+32];
|
||||
o++;
|
||||
|
||||
DTable[o] = D[a+64];
|
||||
o++;
|
||||
|
||||
DTable[o] = D[a+96];
|
||||
o++;
|
||||
|
||||
a+=128;
|
||||
}
|
||||
}
|
||||
|
||||
Vpointer[0] = 64;
|
||||
Vpointer[1] = 64;
|
||||
}
|
||||
|
||||
CBaseDecoder::~CBaseDecoder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CBaseDecoder::PerformSynthesis(float *InputFreq, float *ToHere, int Channel, int step)
|
||||
{
|
||||
if(Channel)
|
||||
ToHere ++;
|
||||
|
||||
/* We have 18 time-vectors of 32 subband magnitudes each. For every
|
||||
vector of 32 magnitudes, the subband synthesis generates 32
|
||||
PCM samples, so the result of 18 of these is 18*32=576 samples.
|
||||
*/
|
||||
|
||||
/* advance the buffer position */
|
||||
Vpointer[Channel] = (Vpointer[Channel] - 64) & 0x3ff;
|
||||
|
||||
IDCT(InputFreq, &V[ Channel ][ Vpointer[Channel] ]);
|
||||
|
||||
/* 32*16=512 mac's */
|
||||
|
||||
Window(Channel, ToHere, step);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
void __forceinline CBaseDecoder::IDCT(float *input, float *out)
|
||||
{
|
||||
float odd[16];
|
||||
float t[32];
|
||||
|
||||
float s1, s2;
|
||||
|
||||
int i;
|
||||
int offset1 = 0;
|
||||
int offset = 0;
|
||||
int ti = 0; // table index
|
||||
|
||||
float even[16], ee[8], eo[8];
|
||||
|
||||
/* input butterflies - level 1 */
|
||||
/* 32 adds */
|
||||
|
||||
even[0] = input[0] + input[31];
|
||||
even[1] = input[1] + input[30];
|
||||
even[2] = input[2] + input[29];
|
||||
even[3] = input[3] + input[28];
|
||||
even[4] = input[4] + input[27];
|
||||
even[5] = input[5] + input[26];
|
||||
even[6] = input[6] + input[25];
|
||||
even[7] = input[7] + input[24];
|
||||
even[8] = input[8] + input[23];
|
||||
even[9] = input[9] + input[22];
|
||||
even[10] = input[10] + input[21];
|
||||
even[11] = input[11] + input[20];
|
||||
even[12] = input[12] + input[19];
|
||||
even[13] = input[13] + input[18];
|
||||
even[14] = input[14] + input[17];
|
||||
even[15] = input[15] + input[16];
|
||||
|
||||
odd[0] = input[0] - input[31];
|
||||
odd[1] = input[1] - input[30];
|
||||
odd[2] = input[2] - input[29];
|
||||
odd[3] = input[3] - input[28];
|
||||
odd[4] = input[4] - input[27];
|
||||
odd[5] = input[5] - input[26];
|
||||
odd[6] = input[6] - input[25];
|
||||
odd[7] = input[7] - input[24];
|
||||
odd[8] = input[8] - input[23];
|
||||
odd[9] = input[9] - input[22];
|
||||
odd[10] = input[10] - input[21];
|
||||
odd[11] = input[11] - input[20];
|
||||
odd[12] = input[12] - input[19];
|
||||
odd[13] = input[13] - input[18];
|
||||
odd[14] = input[14] - input[17];
|
||||
odd[15] = input[15] - input[16];
|
||||
|
||||
/* input butterflies - level 2 */
|
||||
/* 16 adds */
|
||||
|
||||
ee[0] = even[0] + even[15];
|
||||
ee[1] = even[1] + even[14];
|
||||
ee[2] = even[2] + even[13];
|
||||
ee[3] = even[3] + even[12];
|
||||
ee[4] = even[4] + even[11];
|
||||
ee[5] = even[5] + even[10];
|
||||
ee[6] = even[6] + even[9];
|
||||
ee[7] = even[7] + even[8];
|
||||
|
||||
eo[0] = even[0] - even[15];
|
||||
eo[1] = even[1] - even[14];
|
||||
eo[2] = even[2] - even[13];
|
||||
eo[3] = even[3] - even[12];
|
||||
eo[4] = even[4] - even[11];
|
||||
eo[5] = even[5] - even[10];
|
||||
eo[6] = even[6] - even[9];
|
||||
eo[7] = even[7] - even[8];
|
||||
|
||||
|
||||
/* multiply the even_even vector (ee) with the ee matrix (A8) */
|
||||
/* multiply the even_odd vector (eo) with the eo matrix (B8) */
|
||||
/* 128 muls, 128 adds */
|
||||
|
||||
i=8;
|
||||
|
||||
do
|
||||
{
|
||||
s1 = A8[ti][0] * ee[0];
|
||||
s1 += A8[ti][1] * ee[1];
|
||||
s1 += A8[ti][2] * ee[2];
|
||||
s1 += A8[ti][3] * ee[3];
|
||||
s1 += A8[ti][4] * ee[4];
|
||||
s1 += A8[ti][5] * ee[5];
|
||||
s1 += A8[ti][6] * ee[6];
|
||||
s1 += A8[ti][7] * ee[7];
|
||||
|
||||
s2 = B8[ti][0] * eo[0];
|
||||
s2 += B8[ti][1] * eo[1];
|
||||
s2 += B8[ti][2] * eo[2];
|
||||
s2 += B8[ti][3] * eo[3];
|
||||
s2 += B8[ti][4] * eo[4];
|
||||
s2 += B8[ti][5] * eo[5];
|
||||
s2 += B8[ti][6] * eo[6];
|
||||
s2 += B8[ti][7] * eo[7];
|
||||
|
||||
t[offset1] = s1;
|
||||
t[offset1+2] = s2;
|
||||
|
||||
offset1 += 4;
|
||||
ti++;
|
||||
} while(--i);
|
||||
|
||||
|
||||
/* multiply the odd vector (odd) with the odd matrix (B16) */
|
||||
/* 256 muls, 256 adds */
|
||||
|
||||
i = 16;
|
||||
ti = 0;
|
||||
|
||||
do
|
||||
{
|
||||
s1 = B16[ti][0] * odd[0];
|
||||
s1 += B16[ti][1] * odd[1];
|
||||
s1 += B16[ti][2] * odd[2];
|
||||
s1 += B16[ti][3] * odd[3];
|
||||
s1 += B16[ti][4] * odd[4];
|
||||
s1 += B16[ti][5] * odd[5];
|
||||
s1 += B16[ti][6] * odd[6];
|
||||
s1 += B16[ti][7] * odd[7];
|
||||
s1 += B16[ti][8] * odd[8];
|
||||
s1 += B16[ti][9] * odd[9];
|
||||
s1 += B16[ti][10] * odd[10];
|
||||
s1 += B16[ti][11] * odd[11];
|
||||
s1 += B16[ti][12] * odd[12];
|
||||
s1 += B16[ti][13] * odd[13];
|
||||
s1 += B16[ti][14] * odd[14];
|
||||
s1 += B16[ti][15] * odd[15];
|
||||
|
||||
t[offset+1] = s1;
|
||||
|
||||
offset += 2;
|
||||
ti++;
|
||||
} while(--i);
|
||||
|
||||
/* the output vector t now is expanded to 64 values using the
|
||||
symmetric property of the cosinus function */
|
||||
|
||||
i = 16;
|
||||
ti = 0;
|
||||
|
||||
do
|
||||
{
|
||||
out[ti] = t[ti+16];
|
||||
out[ti+17] = -t[31-ti];
|
||||
out[ti+32] = -t[16-ti];
|
||||
out[ti+48] = -t[ti];
|
||||
|
||||
ti++;
|
||||
|
||||
} while(--i);
|
||||
|
||||
out[16] = 0.0;
|
||||
}
|
||||
|
||||
void __forceinline CBaseDecoder::Window(int ch, float *S, int step)
|
||||
{
|
||||
int j, k;
|
||||
register float sum;
|
||||
|
||||
//int a = 0;
|
||||
|
||||
/* calculate 32 samples. each sample is the sum of 16 terms */
|
||||
|
||||
/* 15 */
|
||||
/* Sj = E W((j*16)+i) */
|
||||
/* i=0 */
|
||||
|
||||
|
||||
k = Vpointer[ch];
|
||||
float * TV = V[ch];
|
||||
float * DT = DTable;
|
||||
|
||||
for(j = 0; j < 32; j++)
|
||||
{
|
||||
sum = DT[0x0] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x1] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0x2] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x3] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0x4] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x5] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0x6] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x7] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0x8] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0x9] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0xA] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0xB] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0xC] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0xD] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
sum += DT[0xE] * TV[k];
|
||||
k = (k + 96) & 0x3ff;
|
||||
sum += DT[0xF] * TV[k];
|
||||
k = (k + 32) & 0x3ff;
|
||||
|
||||
DT += 16;
|
||||
|
||||
|
||||
if( sum < -1.0 )
|
||||
sum = -1.0;
|
||||
else if( sum > 1.0 )
|
||||
sum = 1.0;
|
||||
*S = sum;
|
||||
|
||||
S+=step;
|
||||
k++;
|
||||
|
||||
}
|
||||
}
|
@ -1,70 +1,70 @@
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "BitStream.h"
|
||||
|
||||
|
||||
BitStream::BitStream()
|
||||
{
|
||||
buffer = 0;
|
||||
bitindex = 0;
|
||||
}
|
||||
|
||||
BitStream::~BitStream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
unsigned long BitStream::GetBits(unsigned long N)
|
||||
{
|
||||
unsigned long rval;
|
||||
int pos;
|
||||
|
||||
if(N == 0)
|
||||
return 0;
|
||||
|
||||
pos = (unsigned long)(bitindex >> 3);
|
||||
|
||||
rval = buffer[pos] << 24 |
|
||||
buffer[pos+1] << 16 |
|
||||
buffer[pos+2] << 8 |
|
||||
buffer[pos+3];
|
||||
|
||||
rval <<= bitindex & 7;
|
||||
rval >>= 32 - N;
|
||||
bitindex += N;
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
bool BitStream::Load(unsigned char *FromHere)
|
||||
{
|
||||
buffer = FromHere;
|
||||
bitindex = 0;
|
||||
|
||||
return(true);
|
||||
}
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "BitStream.h"
|
||||
|
||||
|
||||
BitStream::BitStream()
|
||||
{
|
||||
buffer = 0;
|
||||
bitindex = 0;
|
||||
}
|
||||
|
||||
BitStream::~BitStream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
unsigned long BitStream::GetBits(unsigned long N)
|
||||
{
|
||||
unsigned long rval;
|
||||
int pos;
|
||||
|
||||
if(N == 0)
|
||||
return 0;
|
||||
|
||||
pos = (unsigned long)(bitindex >> 3);
|
||||
|
||||
rval = buffer[pos] << 24 |
|
||||
buffer[pos+1] << 16 |
|
||||
buffer[pos+2] << 8 |
|
||||
buffer[pos+3];
|
||||
|
||||
rval <<= bitindex & 7;
|
||||
rval >>= 32 - N;
|
||||
bitindex += N;
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
bool BitStream::Load(unsigned char *FromHere)
|
||||
{
|
||||
buffer = FromHere;
|
||||
bitindex = 0;
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
@ -1,43 +1,43 @@
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CRC.h"
|
||||
|
||||
CRC::CRC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CRC::~CRC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CRC::Load(unsigned char *FromHere)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CRC.h"
|
||||
|
||||
CRC::CRC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CRC::~CRC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CRC::Load(unsigned char *FromHere)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
@ -1,107 +1,107 @@
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "FrameSplitter.h"
|
||||
|
||||
CFrameSplitter::CFrameSplitter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CFrameSplitter::~CFrameSplitter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CFrameSplitter::Process(HANDLE hRead, Frame & fr)
|
||||
{
|
||||
unsigned char headerbuf[4] = {0,0,0,0};
|
||||
unsigned char crcbuf[2];
|
||||
unsigned char sideInfoBuffer[32];
|
||||
unsigned long BytesRead;
|
||||
unsigned long NumErrors = 0;
|
||||
unsigned long CurrentOffset = SetFilePointer(hRead, 0, NULL, FILE_CURRENT);
|
||||
|
||||
if(!ReadFile(hRead, (char*)headerbuf, 4, &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != 4)
|
||||
return false;
|
||||
|
||||
while(!fr.m_Header.Load(headerbuf))
|
||||
{
|
||||
headerbuf[0] = headerbuf[1];
|
||||
headerbuf[1] = headerbuf[2];
|
||||
headerbuf[2] = headerbuf[3];
|
||||
|
||||
if(!ReadFile(hRead, (char*)&headerbuf[3], 1, &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != 1)
|
||||
return false;
|
||||
|
||||
if(NumErrors++ >= fr.m_Header.GetDataSize())
|
||||
{
|
||||
fr.m_Header.Reset();
|
||||
}
|
||||
|
||||
CurrentOffset++;
|
||||
}
|
||||
|
||||
// check we have enough for the whole of this frame...
|
||||
//if(BytesUsed <= ((int)Length - (int)FrameSize))
|
||||
|
||||
if(fr.m_Header.IsCRC())
|
||||
{
|
||||
if(!ReadFile(hRead, (char*)crcbuf, 2, &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != 2)
|
||||
return false;
|
||||
|
||||
fr.m_CRC.Load(crcbuf);
|
||||
}
|
||||
|
||||
|
||||
if(fr.m_Header.GetLayer() == LAYER3)
|
||||
{
|
||||
// read side info for layer 3 files
|
||||
|
||||
if(!ReadFile(hRead, (char*)sideInfoBuffer, fr.m_Header.GetSideInfoSize(), &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != fr.m_Header.GetSideInfoSize())
|
||||
return false;
|
||||
|
||||
if(!fr.m_SI.Load(&fr.m_Header, sideInfoBuffer))
|
||||
return(false);
|
||||
|
||||
}
|
||||
|
||||
if(!ReadFile(hRead, (char*)fr.m_Data, fr.m_Header.GetDataSize(), &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != fr.m_Header.GetDataSize())
|
||||
return false;
|
||||
|
||||
return(true);
|
||||
}
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "FrameSplitter.h"
|
||||
|
||||
CFrameSplitter::CFrameSplitter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CFrameSplitter::~CFrameSplitter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CFrameSplitter::Process(HANDLE hRead, Frame & fr)
|
||||
{
|
||||
unsigned char headerbuf[4] = {0,0,0,0};
|
||||
unsigned char crcbuf[2];
|
||||
unsigned char sideInfoBuffer[32];
|
||||
unsigned long BytesRead;
|
||||
unsigned long NumErrors = 0;
|
||||
unsigned long CurrentOffset = SetFilePointer(hRead, 0, NULL, FILE_CURRENT);
|
||||
|
||||
if(!ReadFile(hRead, (char*)headerbuf, 4, &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != 4)
|
||||
return false;
|
||||
|
||||
while(!fr.m_Header.Load(headerbuf))
|
||||
{
|
||||
headerbuf[0] = headerbuf[1];
|
||||
headerbuf[1] = headerbuf[2];
|
||||
headerbuf[2] = headerbuf[3];
|
||||
|
||||
if(!ReadFile(hRead, (char*)&headerbuf[3], 1, &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != 1)
|
||||
return false;
|
||||
|
||||
if(NumErrors++ >= fr.m_Header.GetDataSize())
|
||||
{
|
||||
fr.m_Header.Reset();
|
||||
}
|
||||
|
||||
CurrentOffset++;
|
||||
}
|
||||
|
||||
// check we have enough for the whole of this frame...
|
||||
//if(BytesUsed <= ((int)Length - (int)FrameSize))
|
||||
|
||||
if(fr.m_Header.IsCRC())
|
||||
{
|
||||
if(!ReadFile(hRead, (char*)crcbuf, 2, &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != 2)
|
||||
return false;
|
||||
|
||||
fr.m_CRC.Load(crcbuf);
|
||||
}
|
||||
|
||||
|
||||
if(fr.m_Header.GetLayer() == LAYER3)
|
||||
{
|
||||
// read side info for layer 3 files
|
||||
|
||||
if(!ReadFile(hRead, (char*)sideInfoBuffer, fr.m_Header.GetSideInfoSize(), &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != fr.m_Header.GetSideInfoSize())
|
||||
return false;
|
||||
|
||||
if(!fr.m_SI.Load(&fr.m_Header, sideInfoBuffer))
|
||||
return(false);
|
||||
|
||||
}
|
||||
|
||||
if(!ReadFile(hRead, (char*)fr.m_Data, fr.m_Header.GetDataSize(), &BytesRead, NULL))
|
||||
return false;
|
||||
if(BytesRead != fr.m_Header.GetDataSize())
|
||||
return false;
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
@ -1,285 +1,285 @@
|
||||
// Header.cpp: implementation of the Header class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Header.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define HDRMAIN_BITS 0xfffffc00 // 1111 1111 1111 1111 1111 1100 0000 0000
|
||||
#define MPEG_BITS 0x00180000 // 0000 0000 0001 1000 0000 0000 0000 0000
|
||||
#define LAYER_BITS 0x00060000 // 0000 0000 0000 0110 0000 0000 0000 0000
|
||||
#define CRC_BIT 0x00010000 // 0000 0000 0000 0001 0000 0000 0000 0000
|
||||
#define BITRATE_BITS 0x0000f000 // 0000 0000 0000 0000 ffff 0000 0000 0000
|
||||
#define SAMP_BITS 0x00000C00 // 0000 0000 0000 0000 0000 1100 0000 0000
|
||||
#define PADING_BIT 0x00000200 // 0000 0000 0000 0000 0000 0010 0000 0000
|
||||
#define PRIVATE_BIT 0x00000100 // 0000 0000 0000 0000 0000 0001 0000 0000
|
||||
#define MODE_BITS 0x000000C0 // 0000 0000 0000 0000 0000 0000 1100 0000
|
||||
#define MODEEXT_BITS 0x00000030 // 0000 0000 0000 0000 0000 0000 0011 0000
|
||||
#define COPYRIGHT_BIT 0x00000008 // 0000 0000 0000 0000 0000 0000 0000 1000
|
||||
#define ORIGINAL_BIT 0x00000004 // 0000 0000 0000 0000 0000 0000 0000 0100
|
||||
#define EMPHASIS_BITS 0x00000003 // 0000 0000 0000 0000 0000 0000 0000 0011
|
||||
|
||||
#define CHECK_BITS 0xfffe0c00
|
||||
|
||||
#define MPEG_BITS_1 0x00180000
|
||||
#define MPEG_BITS_2 0x00100000
|
||||
#define MPEG_BITS_25 0x00000000
|
||||
|
||||
#define LAYER_BITS_1 0x00060000
|
||||
#define LAYER_BITS_2 0x00040000
|
||||
#define LAYER_BITS_3 0x00020000
|
||||
|
||||
|
||||
const unsigned long Header::BitRates[2][3][16] =
|
||||
{
|
||||
{
|
||||
{0 /*free format*/, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000, 0},
|
||||
{0 /*free format*/, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000, 0},
|
||||
{0 /*free format*/, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0}
|
||||
},
|
||||
{
|
||||
{0 /*free format*/, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000, 0},
|
||||
{0 /*free format*/, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 0},
|
||||
{0 /*free format*/, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 0}
|
||||
},
|
||||
};
|
||||
|
||||
const unsigned long Header::SampleRates[3][3] =
|
||||
{
|
||||
{
|
||||
44100, 48000, 32000
|
||||
},
|
||||
{
|
||||
22050, 24000, 16000
|
||||
},
|
||||
{
|
||||
11025, 12000, 8000
|
||||
}
|
||||
};
|
||||
|
||||
Header::Header()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
Header::~Header()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Header::Reset()
|
||||
{
|
||||
MpegVersion = -1;
|
||||
Layer = -1;
|
||||
SampleFrequency = -1;
|
||||
Mode = -1;
|
||||
}
|
||||
|
||||
bool Header::Load(unsigned char *FromHere)
|
||||
{
|
||||
bs.Load(FromHere);
|
||||
|
||||
if(bs.GetBits(11) != 0x7ff)
|
||||
return(false);
|
||||
|
||||
unsigned long OldMpeg = MpegVersion;
|
||||
if(bs.GetBits(1)) // mpeg 1 or 2
|
||||
{
|
||||
if(bs.GetBits(1))
|
||||
{
|
||||
MpegVersion = MPEG1;
|
||||
}
|
||||
else
|
||||
{
|
||||
MpegVersion = MPEG2;
|
||||
}
|
||||
}
|
||||
else // mpeg 2.5 or invalid
|
||||
{
|
||||
if(bs.GetBits(1))
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
return(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
MpegVersion = MPEG25;
|
||||
}
|
||||
}
|
||||
|
||||
if(OldMpeg != -1)
|
||||
{
|
||||
if(MpegVersion != OldMpeg)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned OldLayer = Layer;
|
||||
switch(bs.GetBits(2))
|
||||
{
|
||||
case 0:
|
||||
Layer = OldLayer;
|
||||
return(false);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Layer = LAYER3;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Layer = LAYER2;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Layer = LAYER1;
|
||||
break;
|
||||
}
|
||||
if(OldLayer != -1)
|
||||
{
|
||||
if(Layer != OldLayer)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ProtectionBit = !(bs.GetBits(1));
|
||||
|
||||
BitrateIndex = bs.GetBits(4);
|
||||
if(BitrateIndex == 15)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
return(false);
|
||||
}
|
||||
|
||||
if(BitrateIndex == 0)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
return(false);
|
||||
}
|
||||
|
||||
unsigned long OldSampleFrequency = SampleFrequency;
|
||||
SampleFrequency = bs.GetBits(2);
|
||||
if(OldSampleFrequency != -1)
|
||||
{
|
||||
if(SampleFrequency != OldSampleFrequency)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
SampleFrequency = OldSampleFrequency;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(SampleFrequency == 3)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
SampleFrequency = OldSampleFrequency;
|
||||
return false;
|
||||
}
|
||||
|
||||
PaddingBit = bs.GetBits(1);
|
||||
PrivateBit = bs.GetBits(1);
|
||||
|
||||
unsigned long OldMode = Mode;
|
||||
Mode = bs.GetBits(2);
|
||||
if(OldMode != -1)
|
||||
{
|
||||
if(OldMode != Mode)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
SampleFrequency = OldSampleFrequency;
|
||||
Mode = OldMode;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mode_Extension = bs.GetBits(2);
|
||||
|
||||
Copyright = bs.GetBits(1);
|
||||
Original = bs.GetBits(1);
|
||||
|
||||
Emphasis = bs.GetBits(2);
|
||||
if(Emphasis == EMPH_RESERVED)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
SampleFrequency = OldSampleFrequency;
|
||||
Mode = OldMode;
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
if(Mode == MODE_MONO)
|
||||
Channels = 1;
|
||||
else
|
||||
Channels = 2;
|
||||
|
||||
|
||||
if(Layer == LAYER3)
|
||||
{
|
||||
if(MpegVersion == MPEG1)
|
||||
{
|
||||
if(Channels==1)
|
||||
SideInfoSize = 17;
|
||||
else
|
||||
SideInfoSize = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Channels==1)
|
||||
SideInfoSize = 9;
|
||||
else
|
||||
SideInfoSize = 17;
|
||||
}
|
||||
}
|
||||
else
|
||||
SideInfoSize = 0;
|
||||
|
||||
unsigned long base;
|
||||
unsigned long Multiple;
|
||||
|
||||
if(Layer == LAYER1)
|
||||
{
|
||||
base = 12;
|
||||
Multiple = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// framesize is HALF of what it is for MPEG 1
|
||||
// really we should change that 144 * BitRate...
|
||||
// to be 72 * BitRate....
|
||||
if(MpegVersion != MPEG1)
|
||||
base = 72;
|
||||
else
|
||||
base = 144;
|
||||
|
||||
Multiple = 1;
|
||||
}
|
||||
|
||||
FrameSize = ((base * BitRates[MpegVersion == MPEG1 ? 0 : 1][Layer][BitrateIndex]) / SampleRates[MpegVersion][SampleFrequency]);
|
||||
if(PaddingBit)
|
||||
FrameSize++;
|
||||
|
||||
FrameSize *= Multiple;
|
||||
FrameSize -= 4; // for the Header;
|
||||
if(ProtectionBit)
|
||||
FrameSize -= 2;
|
||||
FrameSize -= SideInfoSize;
|
||||
|
||||
HeaderSize = 4;
|
||||
|
||||
return(true);
|
||||
}
|
||||
// Header.cpp: implementation of the Header class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Header.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define HDRMAIN_BITS 0xfffffc00 // 1111 1111 1111 1111 1111 1100 0000 0000
|
||||
#define MPEG_BITS 0x00180000 // 0000 0000 0001 1000 0000 0000 0000 0000
|
||||
#define LAYER_BITS 0x00060000 // 0000 0000 0000 0110 0000 0000 0000 0000
|
||||
#define CRC_BIT 0x00010000 // 0000 0000 0000 0001 0000 0000 0000 0000
|
||||
#define BITRATE_BITS 0x0000f000 // 0000 0000 0000 0000 ffff 0000 0000 0000
|
||||
#define SAMP_BITS 0x00000C00 // 0000 0000 0000 0000 0000 1100 0000 0000
|
||||
#define PADING_BIT 0x00000200 // 0000 0000 0000 0000 0000 0010 0000 0000
|
||||
#define PRIVATE_BIT 0x00000100 // 0000 0000 0000 0000 0000 0001 0000 0000
|
||||
#define MODE_BITS 0x000000C0 // 0000 0000 0000 0000 0000 0000 1100 0000
|
||||
#define MODEEXT_BITS 0x00000030 // 0000 0000 0000 0000 0000 0000 0011 0000
|
||||
#define COPYRIGHT_BIT 0x00000008 // 0000 0000 0000 0000 0000 0000 0000 1000
|
||||
#define ORIGINAL_BIT 0x00000004 // 0000 0000 0000 0000 0000 0000 0000 0100
|
||||
#define EMPHASIS_BITS 0x00000003 // 0000 0000 0000 0000 0000 0000 0000 0011
|
||||
|
||||
#define CHECK_BITS 0xfffe0c00
|
||||
|
||||
#define MPEG_BITS_1 0x00180000
|
||||
#define MPEG_BITS_2 0x00100000
|
||||
#define MPEG_BITS_25 0x00000000
|
||||
|
||||
#define LAYER_BITS_1 0x00060000
|
||||
#define LAYER_BITS_2 0x00040000
|
||||
#define LAYER_BITS_3 0x00020000
|
||||
|
||||
|
||||
const unsigned long Header::BitRates[2][3][16] =
|
||||
{
|
||||
{
|
||||
{0 /*free format*/, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000, 0},
|
||||
{0 /*free format*/, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000, 0},
|
||||
{0 /*free format*/, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0}
|
||||
},
|
||||
{
|
||||
{0 /*free format*/, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000, 0},
|
||||
{0 /*free format*/, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 0},
|
||||
{0 /*free format*/, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 0}
|
||||
},
|
||||
};
|
||||
|
||||
const unsigned long Header::SampleRates[3][3] =
|
||||
{
|
||||
{
|
||||
44100, 48000, 32000
|
||||
},
|
||||
{
|
||||
22050, 24000, 16000
|
||||
},
|
||||
{
|
||||
11025, 12000, 8000
|
||||
}
|
||||
};
|
||||
|
||||
Header::Header()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
Header::~Header()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Header::Reset()
|
||||
{
|
||||
MpegVersion = -1;
|
||||
Layer = -1;
|
||||
SampleFrequency = -1;
|
||||
Mode = -1;
|
||||
}
|
||||
|
||||
bool Header::Load(unsigned char *FromHere)
|
||||
{
|
||||
bs.Load(FromHere);
|
||||
|
||||
if(bs.GetBits(11) != 0x7ff)
|
||||
return(false);
|
||||
|
||||
unsigned long OldMpeg = MpegVersion;
|
||||
if(bs.GetBits(1)) // mpeg 1 or 2
|
||||
{
|
||||
if(bs.GetBits(1))
|
||||
{
|
||||
MpegVersion = MPEG1;
|
||||
}
|
||||
else
|
||||
{
|
||||
MpegVersion = MPEG2;
|
||||
}
|
||||
}
|
||||
else // mpeg 2.5 or invalid
|
||||
{
|
||||
if(bs.GetBits(1))
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
return(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
MpegVersion = MPEG25;
|
||||
}
|
||||
}
|
||||
|
||||
if(OldMpeg != -1)
|
||||
{
|
||||
if(MpegVersion != OldMpeg)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned OldLayer = Layer;
|
||||
switch(bs.GetBits(2))
|
||||
{
|
||||
case 0:
|
||||
Layer = OldLayer;
|
||||
return(false);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Layer = LAYER3;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Layer = LAYER2;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Layer = LAYER1;
|
||||
break;
|
||||
}
|
||||
if(OldLayer != -1)
|
||||
{
|
||||
if(Layer != OldLayer)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ProtectionBit = !(bs.GetBits(1));
|
||||
|
||||
BitrateIndex = bs.GetBits(4);
|
||||
if(BitrateIndex == 15)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
return(false);
|
||||
}
|
||||
|
||||
if(BitrateIndex == 0)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
return(false);
|
||||
}
|
||||
|
||||
unsigned long OldSampleFrequency = SampleFrequency;
|
||||
SampleFrequency = bs.GetBits(2);
|
||||
if(OldSampleFrequency != -1)
|
||||
{
|
||||
if(SampleFrequency != OldSampleFrequency)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
SampleFrequency = OldSampleFrequency;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(SampleFrequency == 3)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
SampleFrequency = OldSampleFrequency;
|
||||
return false;
|
||||
}
|
||||
|
||||
PaddingBit = bs.GetBits(1);
|
||||
PrivateBit = bs.GetBits(1);
|
||||
|
||||
unsigned long OldMode = Mode;
|
||||
Mode = bs.GetBits(2);
|
||||
if(OldMode != -1)
|
||||
{
|
||||
if(OldMode != Mode)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
SampleFrequency = OldSampleFrequency;
|
||||
Mode = OldMode;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mode_Extension = bs.GetBits(2);
|
||||
|
||||
Copyright = bs.GetBits(1);
|
||||
Original = bs.GetBits(1);
|
||||
|
||||
Emphasis = bs.GetBits(2);
|
||||
if(Emphasis == EMPH_RESERVED)
|
||||
{
|
||||
MpegVersion = OldMpeg;
|
||||
Layer = OldLayer;
|
||||
SampleFrequency = OldSampleFrequency;
|
||||
Mode = OldMode;
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
if(Mode == MODE_MONO)
|
||||
Channels = 1;
|
||||
else
|
||||
Channels = 2;
|
||||
|
||||
|
||||
if(Layer == LAYER3)
|
||||
{
|
||||
if(MpegVersion == MPEG1)
|
||||
{
|
||||
if(Channels==1)
|
||||
SideInfoSize = 17;
|
||||
else
|
||||
SideInfoSize = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Channels==1)
|
||||
SideInfoSize = 9;
|
||||
else
|
||||
SideInfoSize = 17;
|
||||
}
|
||||
}
|
||||
else
|
||||
SideInfoSize = 0;
|
||||
|
||||
unsigned long base;
|
||||
unsigned long Multiple;
|
||||
|
||||
if(Layer == LAYER1)
|
||||
{
|
||||
base = 12;
|
||||
Multiple = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// framesize is HALF of what it is for MPEG 1
|
||||
// really we should change that 144 * BitRate...
|
||||
// to be 72 * BitRate....
|
||||
if(MpegVersion != MPEG1)
|
||||
base = 72;
|
||||
else
|
||||
base = 144;
|
||||
|
||||
Multiple = 1;
|
||||
}
|
||||
|
||||
FrameSize = ((base * BitRates[MpegVersion == MPEG1 ? 0 : 1][Layer][BitrateIndex]) / SampleRates[MpegVersion][SampleFrequency]);
|
||||
if(PaddingBit)
|
||||
FrameSize++;
|
||||
|
||||
FrameSize *= Multiple;
|
||||
FrameSize -= 4; // for the Header;
|
||||
if(ProtectionBit)
|
||||
FrameSize -= 2;
|
||||
FrameSize -= SideInfoSize;
|
||||
|
||||
HeaderSize = 4;
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,280 +1,280 @@
|
||||
#include "StdAfx.h"
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include "mp3decoder.h"
|
||||
|
||||
static bool splitFrame(musik::core::io::IDataStream *dataStream, Frame &fr) {
|
||||
if (dataStream->Eof()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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()
|
||||
: decoder(NULL) {
|
||||
}
|
||||
|
||||
Mp3Decoder::~Mp3Decoder() {
|
||||
delete decoder;
|
||||
}
|
||||
|
||||
unsigned long Mp3Decoder::GetID3HeaderLength(unsigned char * buffer) {
|
||||
unsigned char VerMajor;
|
||||
unsigned char VerMinor;
|
||||
unsigned char Flags;
|
||||
unsigned long Length;
|
||||
|
||||
if( (toupper(buffer[0]) == 'I') &&
|
||||
(toupper(buffer[1]) == 'D') &&
|
||||
(toupper(buffer[2]) == '3'))
|
||||
{
|
||||
VerMajor = buffer[3];
|
||||
VerMinor = buffer[4];
|
||||
Flags = buffer[5];
|
||||
|
||||
Length = (buffer[6] << 21) | (buffer[7] << 14) | (buffer[8] << 7) | buffer[9];
|
||||
Length += 10; // the header
|
||||
|
||||
return(Length);
|
||||
}
|
||||
|
||||
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) /* windows only? */
|
||||
|
||||
unsigned long i;
|
||||
|
||||
this->xingValid = false;
|
||||
|
||||
if (strncmp((char *) xingBuffer, "Xing", 4)) {
|
||||
if (strncmp((char *)xingBuffer, "Info", 4)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
xingBuffer += 4;
|
||||
|
||||
unsigned long headFlags = GET_INT32BE(xingBuffer);
|
||||
|
||||
this->numFrames = 0;
|
||||
if (headFlags & FRAMES_FLAG) {
|
||||
this->numFrames = GET_INT32BE(xingBuffer);
|
||||
}
|
||||
|
||||
if (this->numFrames < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->streamDataLength = 0;
|
||||
if (headFlags & BYTES_FLAG) {
|
||||
this->streamDataLength = GET_INT32BE(xingBuffer);
|
||||
}
|
||||
|
||||
if (headFlags & TOC_FLAG) {
|
||||
for (i = 0; i < 100; i++) {
|
||||
this->toc[i] = xingBuffer[i];
|
||||
}
|
||||
|
||||
xingBuffer += 100;
|
||||
}
|
||||
|
||||
this->vbrScale = -1;
|
||||
if (headFlags & VBR_SCALE_FLAG) {
|
||||
this->vbrScale = GET_INT32BE(xingBuffer);
|
||||
}
|
||||
|
||||
this->xingValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mp3Decoder::GetStreamData() {
|
||||
unsigned char tbuf[11];
|
||||
unsigned long bytesread;
|
||||
Frame fr;
|
||||
|
||||
this->dataStream->Read(tbuf, 10);
|
||||
this->id3v2Length = GetID3HeaderLength(tbuf);
|
||||
this->dataStream->SetPosition(this->id3v2Length);
|
||||
|
||||
if (splitFrame(this->dataStream, fr)) {
|
||||
unsigned char * pHeader = fr.m_Data;
|
||||
if(!GetXingHeader(pHeader)) {
|
||||
this->dataStream->SetPosition(this->id3v2Length);
|
||||
|
||||
// just guesstimate the number of frames!
|
||||
/* DANGEROUS -- ASSUMES FINITE LENGTH*/
|
||||
this->streamDataLength = this->dataStream->Length() - this->id3v2Length;
|
||||
|
||||
// TODO: check for ID3 TAG at the end of the file and subtract
|
||||
// also remove the size of this current header
|
||||
this->streamDataLength -= fr.m_Header.GetTotalFrameSize();
|
||||
this->numFrames = this->streamDataLength / fr.m_Header.GetTotalFrameSize();
|
||||
}
|
||||
else {
|
||||
if (!this->xingValid) {
|
||||
std::cout << "Mp3Decoder.cpp: Mp3 has Xing header but it is invalid.";
|
||||
}
|
||||
}
|
||||
|
||||
double bs[3] = { 384.0, 1152.0, 1152.0 };
|
||||
double timePerFrame = (double)bs[fr.m_Header.GetLayer()] / (((double)fr.m_Header.GetSampleFrequency() / 1000.0));
|
||||
|
||||
this->streamLengthMs = timePerFrame * this->numFrames;
|
||||
|
||||
if (fr.m_Header.GetMpegVersion() != MPEG1) {
|
||||
this->streamLengthMs /= 2;
|
||||
}
|
||||
|
||||
this->sampleRate = fr.m_Header.GetSampleFrequency();
|
||||
this->numChannels = fr.m_Header.GetChannels();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mp3Decoder::Open(musik::core::io::IDataStream *dataStream) {
|
||||
this->dataStream = dataStream;
|
||||
this->lastLayer = -1;
|
||||
return GetStreamData();
|
||||
}
|
||||
|
||||
void Mp3Decoder::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
#define MP3_BUFFER_FLOAT_ALLOWANCE 2304 /* why? */
|
||||
|
||||
bool Mp3Decoder::GetBuffer(IBuffer *buffer) {
|
||||
buffer->SetChannels(this->numChannels);
|
||||
buffer->SetSamples(MP3_BUFFER_FLOAT_ALLOWANCE / buffer->Channels());
|
||||
buffer->SetSampleRate(this->sampleRate);
|
||||
|
||||
if (splitFrame(this->dataStream, this->frame)) {
|
||||
/* bail if the mpeg layer is incorrect*/
|
||||
if ((this->frame.m_Header.GetLayer() != this->lastLayer) || (this->decoder == NULL)) {
|
||||
switch (this->frame.m_Header.GetLayer()) {
|
||||
case LAYER3: {
|
||||
delete this->decoder;
|
||||
this->decoder = NULL;
|
||||
this->decoder = new CLayer3Decoder();
|
||||
this->lastLayer = LAYER3;
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* we have an mp3... */
|
||||
unsigned long bufferCount = 0;
|
||||
if (!this->decoder->ProcessFrame(&this->frame, buffer->BufferPointer(), &bufferCount)) {
|
||||
this->frame.m_Header.Reset(); /* we're done if ProcessFrame returns false */
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer->SetSamples(bufferCount / buffer->Channels());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
double Mp3Decoder::SetPosition(double seconds) {
|
||||
float milliseconds = (float) seconds * 1000.0f;
|
||||
float percent = 100.00f * ((float) milliseconds / (float) this->streamLengthMs);
|
||||
unsigned long offset;
|
||||
|
||||
if (this->xingValid) {
|
||||
/* interpolate in TOC to get file seek point in bytes */
|
||||
int a = std::min(percent, 99.0f);
|
||||
float fa, fb, fx;
|
||||
|
||||
fa = this->toc[a];
|
||||
|
||||
if (a < 99) {
|
||||
fb = this->toc[a + 1];
|
||||
}
|
||||
else {
|
||||
fb = 256;
|
||||
}
|
||||
|
||||
fx = fa + (fb - fa) * (percent - a);
|
||||
offset = (1.0f / 256.0f) * fx * this->streamDataLength;
|
||||
}
|
||||
else {
|
||||
offset = (float) this->streamDataLength * (float)(percent/ 100.0f) ;
|
||||
}
|
||||
|
||||
this->dataStream->SetPosition(offset + this->id3v2Length);
|
||||
bool result = splitFrame(this->dataStream, this->frame);
|
||||
|
||||
delete this->decoder;
|
||||
this->decoder = NULL;
|
||||
|
||||
return result ? seconds : -1;
|
||||
}
|
||||
#include "StdAfx.h"
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include "mp3decoder.h"
|
||||
|
||||
static bool splitFrame(musik::core::io::IDataStream *dataStream, Frame &fr) {
|
||||
if (dataStream->Eof()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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()
|
||||
: decoder(NULL) {
|
||||
}
|
||||
|
||||
Mp3Decoder::~Mp3Decoder() {
|
||||
delete decoder;
|
||||
}
|
||||
|
||||
unsigned long Mp3Decoder::GetID3HeaderLength(unsigned char * buffer) {
|
||||
unsigned char VerMajor;
|
||||
unsigned char VerMinor;
|
||||
unsigned char Flags;
|
||||
unsigned long Length;
|
||||
|
||||
if( (toupper(buffer[0]) == 'I') &&
|
||||
(toupper(buffer[1]) == 'D') &&
|
||||
(toupper(buffer[2]) == '3'))
|
||||
{
|
||||
VerMajor = buffer[3];
|
||||
VerMinor = buffer[4];
|
||||
Flags = buffer[5];
|
||||
|
||||
Length = (buffer[6] << 21) | (buffer[7] << 14) | (buffer[8] << 7) | buffer[9];
|
||||
Length += 10; // the header
|
||||
|
||||
return(Length);
|
||||
}
|
||||
|
||||
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) /* windows only? */
|
||||
|
||||
unsigned long i;
|
||||
|
||||
this->xingValid = false;
|
||||
|
||||
if (strncmp((char *) xingBuffer, "Xing", 4)) {
|
||||
if (strncmp((char *)xingBuffer, "Info", 4)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
xingBuffer += 4;
|
||||
|
||||
unsigned long headFlags = GET_INT32BE(xingBuffer);
|
||||
|
||||
this->numFrames = 0;
|
||||
if (headFlags & FRAMES_FLAG) {
|
||||
this->numFrames = GET_INT32BE(xingBuffer);
|
||||
}
|
||||
|
||||
if (this->numFrames < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->streamDataLength = 0;
|
||||
if (headFlags & BYTES_FLAG) {
|
||||
this->streamDataLength = GET_INT32BE(xingBuffer);
|
||||
}
|
||||
|
||||
if (headFlags & TOC_FLAG) {
|
||||
for (i = 0; i < 100; i++) {
|
||||
this->toc[i] = xingBuffer[i];
|
||||
}
|
||||
|
||||
xingBuffer += 100;
|
||||
}
|
||||
|
||||
this->vbrScale = -1;
|
||||
if (headFlags & VBR_SCALE_FLAG) {
|
||||
this->vbrScale = GET_INT32BE(xingBuffer);
|
||||
}
|
||||
|
||||
this->xingValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mp3Decoder::GetStreamData() {
|
||||
unsigned char tbuf[11];
|
||||
unsigned long bytesread;
|
||||
Frame fr;
|
||||
|
||||
this->dataStream->Read(tbuf, 10);
|
||||
this->id3v2Length = GetID3HeaderLength(tbuf);
|
||||
this->dataStream->SetPosition(this->id3v2Length);
|
||||
|
||||
if (splitFrame(this->dataStream, fr)) {
|
||||
unsigned char * pHeader = fr.m_Data;
|
||||
if(!GetXingHeader(pHeader)) {
|
||||
this->dataStream->SetPosition(this->id3v2Length);
|
||||
|
||||
// just guesstimate the number of frames!
|
||||
/* DANGEROUS -- ASSUMES FINITE LENGTH*/
|
||||
this->streamDataLength = this->dataStream->Length() - this->id3v2Length;
|
||||
|
||||
// TODO: check for ID3 TAG at the end of the file and subtract
|
||||
// also remove the size of this current header
|
||||
this->streamDataLength -= fr.m_Header.GetTotalFrameSize();
|
||||
this->numFrames = this->streamDataLength / fr.m_Header.GetTotalFrameSize();
|
||||
}
|
||||
else {
|
||||
if (!this->xingValid) {
|
||||
std::cout << "Mp3Decoder.cpp: Mp3 has Xing header but it is invalid.";
|
||||
}
|
||||
}
|
||||
|
||||
double bs[3] = { 384.0, 1152.0, 1152.0 };
|
||||
double timePerFrame = (double)bs[fr.m_Header.GetLayer()] / (((double)fr.m_Header.GetSampleFrequency() / 1000.0));
|
||||
|
||||
this->streamLengthMs = timePerFrame * this->numFrames;
|
||||
|
||||
if (fr.m_Header.GetMpegVersion() != MPEG1) {
|
||||
this->streamLengthMs /= 2;
|
||||
}
|
||||
|
||||
this->sampleRate = fr.m_Header.GetSampleFrequency();
|
||||
this->numChannels = fr.m_Header.GetChannels();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mp3Decoder::Open(musik::core::io::IDataStream *dataStream) {
|
||||
this->dataStream = dataStream;
|
||||
this->lastLayer = -1;
|
||||
return GetStreamData();
|
||||
}
|
||||
|
||||
void Mp3Decoder::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
#define MP3_BUFFER_FLOAT_ALLOWANCE 2304 /* why? */
|
||||
|
||||
bool Mp3Decoder::GetBuffer(IBuffer *buffer) {
|
||||
buffer->SetChannels(this->numChannels);
|
||||
buffer->SetSamples(MP3_BUFFER_FLOAT_ALLOWANCE / buffer->Channels());
|
||||
buffer->SetSampleRate(this->sampleRate);
|
||||
|
||||
if (splitFrame(this->dataStream, this->frame)) {
|
||||
/* bail if the mpeg layer is incorrect*/
|
||||
if ((this->frame.m_Header.GetLayer() != this->lastLayer) || (this->decoder == NULL)) {
|
||||
switch (this->frame.m_Header.GetLayer()) {
|
||||
case LAYER3: {
|
||||
delete this->decoder;
|
||||
this->decoder = NULL;
|
||||
this->decoder = new CLayer3Decoder();
|
||||
this->lastLayer = LAYER3;
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* we have an mp3... */
|
||||
unsigned long bufferCount = 0;
|
||||
if (!this->decoder->ProcessFrame(&this->frame, buffer->BufferPointer(), &bufferCount)) {
|
||||
this->frame.m_Header.Reset(); /* we're done if ProcessFrame returns false */
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer->SetSamples(bufferCount / buffer->Channels());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
double Mp3Decoder::SetPosition(double seconds) {
|
||||
float milliseconds = (float) seconds * 1000.0f;
|
||||
float percent = 100.00f * ((float) milliseconds / (float) this->streamLengthMs);
|
||||
unsigned long offset;
|
||||
|
||||
if (this->xingValid) {
|
||||
/* interpolate in TOC to get file seek point in bytes */
|
||||
int a = std::min(percent, 99.0f);
|
||||
float fa, fb, fx;
|
||||
|
||||
fa = this->toc[a];
|
||||
|
||||
if (a < 99) {
|
||||
fb = this->toc[a + 1];
|
||||
}
|
||||
else {
|
||||
fb = 256;
|
||||
}
|
||||
|
||||
fx = fa + (fb - fa) * (percent - a);
|
||||
offset = (1.0f / 256.0f) * fx * this->streamDataLength;
|
||||
}
|
||||
else {
|
||||
offset = (float) this->streamDataLength * (float)(percent/ 100.0f) ;
|
||||
}
|
||||
|
||||
this->dataStream->SetPosition(offset + this->id3v2Length);
|
||||
bool result = splitFrame(this->dataStream, this->frame);
|
||||
|
||||
delete this->decoder;
|
||||
this->decoder = NULL;
|
||||
|
||||
return result ? seconds : -1;
|
||||
}
|
||||
|
@ -1,71 +1,71 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright <20> 2007, Bj<42>rn Olievier
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "boost/filesystem.hpp"
|
||||
|
||||
#include "Mp3DecoderFactory.h"
|
||||
#include "Mp3Decoder.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
Mp3DecoderFactory::Mp3DecoderFactory() {
|
||||
}
|
||||
|
||||
Mp3DecoderFactory::~Mp3DecoderFactory() {
|
||||
}
|
||||
|
||||
void Mp3DecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* Mp3DecoderFactory::CreateDecoder() {
|
||||
return new Mp3Decoder();
|
||||
}
|
||||
|
||||
bool Mp3DecoderFactory::CanHandle(const char* source) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
if (musik::sdk::endsWith(str, ".mp3") ||
|
||||
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;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright <20> 2007, Bj<42>rn Olievier
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "boost/filesystem.hpp"
|
||||
|
||||
#include "Mp3DecoderFactory.h"
|
||||
#include "Mp3Decoder.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
Mp3DecoderFactory::Mp3DecoderFactory() {
|
||||
}
|
||||
|
||||
Mp3DecoderFactory::~Mp3DecoderFactory() {
|
||||
}
|
||||
|
||||
void Mp3DecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* Mp3DecoderFactory::CreateDecoder() {
|
||||
return new Mp3Decoder();
|
||||
}
|
||||
|
||||
bool Mp3DecoderFactory::CanHandle(const char* source) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
if (musik::sdk::endsWith(str, ".mp3") ||
|
||||
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;
|
||||
}
|
||||
|
@ -1,189 +1,189 @@
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "SideInfo.h"
|
||||
|
||||
SideInfo::SideInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SideInfo::~SideInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool SideInfo::Load(Header * pHead, unsigned char *FromHere)
|
||||
{
|
||||
unsigned long ch, gr, scfsi_band, region, window;
|
||||
|
||||
bs.Load(FromHere);
|
||||
|
||||
if(pHead->GetMpegVersion() == MPEG1)
|
||||
{
|
||||
MainDataBegin = bs.GetBits(9);
|
||||
|
||||
if(pHead->GetMode() == MODE_MONO)
|
||||
{
|
||||
PrivateBits = bs.GetBits(5);
|
||||
}
|
||||
else
|
||||
PrivateBits = bs.GetBits(3);
|
||||
|
||||
for(ch=0; ch<pHead->GetChannels(); ch++)
|
||||
for(scfsi_band=0; scfsi_band<4; scfsi_band++)
|
||||
ScfSi[ch][scfsi_band] = bs.GetBits(1);
|
||||
|
||||
for(gr=0; gr<2; gr++)
|
||||
{
|
||||
for(ch=0; ch<pHead->GetChannels(); ch++)
|
||||
{
|
||||
grinf[gr][ch].Part23Length = bs.GetBits(12);
|
||||
grinf[gr][ch].BigValues = bs.GetBits(9);
|
||||
grinf[gr][ch].GlobalGain = bs.GetBits(8);
|
||||
|
||||
grinf[gr][ch].ScalefacCompress = bs.GetBits(4);
|
||||
|
||||
grinf[gr][ch].WindowSwitchingFlag = bs.GetBits(1);
|
||||
|
||||
if(grinf[gr][ch].WindowSwitchingFlag == 1)
|
||||
{
|
||||
grinf[gr][ch].BlockType = bs.GetBits(2);
|
||||
if(grinf[gr][ch].BlockType == 0)
|
||||
return false;
|
||||
|
||||
grinf[gr][ch].MixedBlockFlag = bs.GetBits(1);
|
||||
|
||||
grinf[gr][ch].TableSelect[0] = bs.GetBits(5);
|
||||
grinf[gr][ch].TableSelect[1] = bs.GetBits(5);
|
||||
grinf[gr][ch].TableSelect[2] = 0;
|
||||
|
||||
grinf[gr][ch].SubblockGain[0] = bs.GetBits(3);
|
||||
grinf[gr][ch].SubblockGain[1] = bs.GetBits(3);
|
||||
grinf[gr][ch].SubblockGain[2] = bs.GetBits(3);
|
||||
|
||||
// not used in short blocks
|
||||
if( (grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) &&
|
||||
(grinf[gr][ch].MixedBlockFlag == 0) )
|
||||
{
|
||||
grinf[gr][ch].Region0Count = 8;
|
||||
}
|
||||
else
|
||||
grinf[gr][ch].Region0Count = 7;
|
||||
|
||||
grinf[gr][ch].Region1Count = 20 - grinf[gr][ch].Region0Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
grinf[gr][ch].BlockType = 0;
|
||||
grinf[gr][ch].MixedBlockFlag = 0;
|
||||
|
||||
grinf[gr][ch].TableSelect[0] = bs.GetBits(5);
|
||||
grinf[gr][ch].TableSelect[1] = bs.GetBits(5);
|
||||
grinf[gr][ch].TableSelect[2] = bs.GetBits(5);
|
||||
|
||||
grinf[gr][ch].SubblockGain[0] = 0;
|
||||
grinf[gr][ch].SubblockGain[1] = 0;
|
||||
grinf[gr][ch].SubblockGain[2] = 0;
|
||||
|
||||
grinf[gr][ch].Region0Count = bs.GetBits(4);
|
||||
grinf[gr][ch].Region1Count = bs.GetBits(3);
|
||||
}
|
||||
|
||||
grinf[gr][ch].PreFlag = bs.GetBits(1);
|
||||
grinf[gr][ch].ScalefacScale = bs.GetBits(1);
|
||||
grinf[gr][ch].Count1Table_Select = bs.GetBits(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainDataBegin = bs.GetBits(8);
|
||||
|
||||
if(pHead->GetMode() == MODE_MONO)
|
||||
PrivateBits = bs.GetBits(1);
|
||||
else
|
||||
PrivateBits = bs.GetBits(2);
|
||||
|
||||
gr=0;
|
||||
|
||||
for(ch=0; ch<pHead->GetChannels(); ch++)
|
||||
{
|
||||
grinf[gr][ch].Part23Length = bs.GetBits(12);
|
||||
grinf[gr][ch].BigValues = bs.GetBits(9);
|
||||
grinf[gr][ch].GlobalGain = bs.GetBits(8);
|
||||
|
||||
grinf[gr][ch].ScalefacCompress = bs.GetBits(9);
|
||||
|
||||
grinf[gr][ch].WindowSwitchingFlag = bs.GetBits(1);
|
||||
|
||||
if(grinf[gr][ch].WindowSwitchingFlag == 1)
|
||||
{
|
||||
grinf[gr][ch].BlockType = bs.GetBits(2);
|
||||
grinf[gr][ch].MixedBlockFlag = bs.GetBits(1);
|
||||
|
||||
for(region = 0; region < 2; region++)
|
||||
{
|
||||
grinf[gr][ch].TableSelect[region] = bs.GetBits(5);
|
||||
}
|
||||
grinf[gr][ch].TableSelect[2] = 0;
|
||||
|
||||
for(window = 0; window < 3; window++)
|
||||
{
|
||||
grinf[gr][ch].SubblockGain[window] = bs.GetBits(3);
|
||||
}
|
||||
|
||||
// not used in short blocks
|
||||
if( (grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) &&
|
||||
(grinf[gr][ch].MixedBlockFlag == 0) )
|
||||
{
|
||||
grinf[gr][ch].Region0Count = 8;
|
||||
}
|
||||
else
|
||||
grinf[gr][ch].Region0Count = 7;
|
||||
|
||||
grinf[gr][ch].Region1Count = 20 - grinf[gr][ch].Region0Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
grinf[gr][ch].BlockType = 0;
|
||||
|
||||
for(region = 0; region < 3; region++)
|
||||
{
|
||||
grinf[gr][ch].TableSelect[region] = bs.GetBits(5);
|
||||
}
|
||||
|
||||
grinf[gr][ch].Region0Count = bs.GetBits(4);
|
||||
grinf[gr][ch].Region1Count = bs.GetBits(3);
|
||||
}
|
||||
|
||||
grinf[gr][ch].ScalefacScale = bs.GetBits(1);
|
||||
grinf[gr][ch].Count1Table_Select = bs.GetBits(1);
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
/*
|
||||
Copyright (c) 2002 Tony Million
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held liable
|
||||
for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it
|
||||
and redistribute it freely, subject to the following
|
||||
restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you wrote
|
||||
the original software. If you use this software in a
|
||||
product, an acknowledgment in the product
|
||||
documentation is required.
|
||||
|
||||
2. Altered source versions must be plainly marked as
|
||||
such, and must not be misrepresented as being the
|
||||
original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*/
|
||||
|
||||
#include "SideInfo.h"
|
||||
|
||||
SideInfo::SideInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SideInfo::~SideInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool SideInfo::Load(Header * pHead, unsigned char *FromHere)
|
||||
{
|
||||
unsigned long ch, gr, scfsi_band, region, window;
|
||||
|
||||
bs.Load(FromHere);
|
||||
|
||||
if(pHead->GetMpegVersion() == MPEG1)
|
||||
{
|
||||
MainDataBegin = bs.GetBits(9);
|
||||
|
||||
if(pHead->GetMode() == MODE_MONO)
|
||||
{
|
||||
PrivateBits = bs.GetBits(5);
|
||||
}
|
||||
else
|
||||
PrivateBits = bs.GetBits(3);
|
||||
|
||||
for(ch=0; ch<pHead->GetChannels(); ch++)
|
||||
for(scfsi_band=0; scfsi_band<4; scfsi_band++)
|
||||
ScfSi[ch][scfsi_band] = bs.GetBits(1);
|
||||
|
||||
for(gr=0; gr<2; gr++)
|
||||
{
|
||||
for(ch=0; ch<pHead->GetChannels(); ch++)
|
||||
{
|
||||
grinf[gr][ch].Part23Length = bs.GetBits(12);
|
||||
grinf[gr][ch].BigValues = bs.GetBits(9);
|
||||
grinf[gr][ch].GlobalGain = bs.GetBits(8);
|
||||
|
||||
grinf[gr][ch].ScalefacCompress = bs.GetBits(4);
|
||||
|
||||
grinf[gr][ch].WindowSwitchingFlag = bs.GetBits(1);
|
||||
|
||||
if(grinf[gr][ch].WindowSwitchingFlag == 1)
|
||||
{
|
||||
grinf[gr][ch].BlockType = bs.GetBits(2);
|
||||
if(grinf[gr][ch].BlockType == 0)
|
||||
return false;
|
||||
|
||||
grinf[gr][ch].MixedBlockFlag = bs.GetBits(1);
|
||||
|
||||
grinf[gr][ch].TableSelect[0] = bs.GetBits(5);
|
||||
grinf[gr][ch].TableSelect[1] = bs.GetBits(5);
|
||||
grinf[gr][ch].TableSelect[2] = 0;
|
||||
|
||||
grinf[gr][ch].SubblockGain[0] = bs.GetBits(3);
|
||||
grinf[gr][ch].SubblockGain[1] = bs.GetBits(3);
|
||||
grinf[gr][ch].SubblockGain[2] = bs.GetBits(3);
|
||||
|
||||
// not used in short blocks
|
||||
if( (grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) &&
|
||||
(grinf[gr][ch].MixedBlockFlag == 0) )
|
||||
{
|
||||
grinf[gr][ch].Region0Count = 8;
|
||||
}
|
||||
else
|
||||
grinf[gr][ch].Region0Count = 7;
|
||||
|
||||
grinf[gr][ch].Region1Count = 20 - grinf[gr][ch].Region0Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
grinf[gr][ch].BlockType = 0;
|
||||
grinf[gr][ch].MixedBlockFlag = 0;
|
||||
|
||||
grinf[gr][ch].TableSelect[0] = bs.GetBits(5);
|
||||
grinf[gr][ch].TableSelect[1] = bs.GetBits(5);
|
||||
grinf[gr][ch].TableSelect[2] = bs.GetBits(5);
|
||||
|
||||
grinf[gr][ch].SubblockGain[0] = 0;
|
||||
grinf[gr][ch].SubblockGain[1] = 0;
|
||||
grinf[gr][ch].SubblockGain[2] = 0;
|
||||
|
||||
grinf[gr][ch].Region0Count = bs.GetBits(4);
|
||||
grinf[gr][ch].Region1Count = bs.GetBits(3);
|
||||
}
|
||||
|
||||
grinf[gr][ch].PreFlag = bs.GetBits(1);
|
||||
grinf[gr][ch].ScalefacScale = bs.GetBits(1);
|
||||
grinf[gr][ch].Count1Table_Select = bs.GetBits(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainDataBegin = bs.GetBits(8);
|
||||
|
||||
if(pHead->GetMode() == MODE_MONO)
|
||||
PrivateBits = bs.GetBits(1);
|
||||
else
|
||||
PrivateBits = bs.GetBits(2);
|
||||
|
||||
gr=0;
|
||||
|
||||
for(ch=0; ch<pHead->GetChannels(); ch++)
|
||||
{
|
||||
grinf[gr][ch].Part23Length = bs.GetBits(12);
|
||||
grinf[gr][ch].BigValues = bs.GetBits(9);
|
||||
grinf[gr][ch].GlobalGain = bs.GetBits(8);
|
||||
|
||||
grinf[gr][ch].ScalefacCompress = bs.GetBits(9);
|
||||
|
||||
grinf[gr][ch].WindowSwitchingFlag = bs.GetBits(1);
|
||||
|
||||
if(grinf[gr][ch].WindowSwitchingFlag == 1)
|
||||
{
|
||||
grinf[gr][ch].BlockType = bs.GetBits(2);
|
||||
grinf[gr][ch].MixedBlockFlag = bs.GetBits(1);
|
||||
|
||||
for(region = 0; region < 2; region++)
|
||||
{
|
||||
grinf[gr][ch].TableSelect[region] = bs.GetBits(5);
|
||||
}
|
||||
grinf[gr][ch].TableSelect[2] = 0;
|
||||
|
||||
for(window = 0; window < 3; window++)
|
||||
{
|
||||
grinf[gr][ch].SubblockGain[window] = bs.GetBits(3);
|
||||
}
|
||||
|
||||
// not used in short blocks
|
||||
if( (grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) &&
|
||||
(grinf[gr][ch].MixedBlockFlag == 0) )
|
||||
{
|
||||
grinf[gr][ch].Region0Count = 8;
|
||||
}
|
||||
else
|
||||
grinf[gr][ch].Region0Count = 7;
|
||||
|
||||
grinf[gr][ch].Region1Count = 20 - grinf[gr][ch].Region0Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
grinf[gr][ch].BlockType = 0;
|
||||
|
||||
for(region = 0; region < 3; region++)
|
||||
{
|
||||
grinf[gr][ch].TableSelect[region] = bs.GetBits(5);
|
||||
}
|
||||
|
||||
grinf[gr][ch].Region0Count = bs.GetBits(4);
|
||||
grinf[gr][ch].Region1Count = bs.GetBits(3);
|
||||
}
|
||||
|
||||
grinf[gr][ch].ScalefacScale = bs.GetBits(1);
|
||||
grinf[gr][ch].Count1Table_Select = bs.GetBits(1);
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
@ -1,68 +1,68 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Björn Olievier
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/sdk/IDecoder.h>
|
||||
#include "Mp3DecoderFactory.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class MP3DecoderPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; }
|
||||
const char* Name() { return "MP3 IDecoder"; };
|
||||
const char* Version() { return "0.2"; };
|
||||
const char* Author() { return "Björn Olievier, clangen"; };
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new MP3DecoderPlugin();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT IDecoderFactory* GetDecoderFactory() {
|
||||
return new Mp3DecoderFactory();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Björn Olievier
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/sdk/IDecoder.h>
|
||||
#include "Mp3DecoderFactory.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class MP3DecoderPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; }
|
||||
const char* Name() { return "MP3 IDecoder"; };
|
||||
const char* Version() { return "0.2"; };
|
||||
const char* Author() { return "Björn Olievier, clangen"; };
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new MP3DecoderPlugin();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT IDecoderFactory* GetDecoderFactory() {
|
||||
return new Mp3DecoderFactory();
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// oggPlugin.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// oggPlugin.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
|
@ -30,182 +30,182 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Mpg123Decoder.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define STREAM_FEED_SIZE 2048 * 8
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG > 0
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::io;
|
||||
|
||||
Mpg123Decoder::Mpg123Decoder()
|
||||
: cachedLength(0)
|
||||
, decoder(NULL)
|
||||
, sampleRate(44100)
|
||||
, channels(2)
|
||||
, fileStream(NULL)
|
||||
, lastMpg123Status(MPG123_NEED_MORE) {
|
||||
this->decoder = mpg123_new(NULL, NULL);
|
||||
this->sampleSizeBytes = this->channels * sizeof(float);
|
||||
}
|
||||
|
||||
Mpg123Decoder::~Mpg123Decoder() {
|
||||
if (this->decoder) {
|
||||
mpg123_close(this->decoder);
|
||||
mpg123_delete(this->decoder);
|
||||
this->decoder = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Mpg123Decoder::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
double Mpg123Decoder::SetPosition(double second) {
|
||||
off_t seekToFileOffset = 0;
|
||||
off_t seekToSampleOffset = second * (double)this->sampleRate;
|
||||
off_t *indexOffset = 0;
|
||||
off_t indexSet = 0;
|
||||
size_t indexFill = 0;
|
||||
int err = mpg123_index(this->decoder, &indexOffset, &indexSet, &indexFill);
|
||||
|
||||
off_t seekedTo = 0;
|
||||
int feedMore = 20;
|
||||
while((seekedTo = mpg123_feedseek(
|
||||
this->decoder,
|
||||
seekToSampleOffset,
|
||||
SEEK_SET,
|
||||
&seekToFileOffset)) == MPG123_NEED_MORE && feedMore > 0)
|
||||
{
|
||||
if (!this->Feed()) {
|
||||
feedMore = 0;
|
||||
}
|
||||
|
||||
feedMore--;
|
||||
}
|
||||
|
||||
if (seekedTo >= 0) {
|
||||
if (this->fileStream->SetPosition(seekToFileOffset)) {
|
||||
return second;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Mpg123Decoder::GetBuffer(IBuffer *buffer) {
|
||||
buffer->SetChannels(this->channels);
|
||||
buffer->SetSampleRate(this->sampleRate);
|
||||
buffer->SetSamples(STREAM_FEED_SIZE / this->channels);
|
||||
|
||||
unsigned char* targetBuffer = (unsigned char*) (buffer->BufferPointer());
|
||||
|
||||
bool done = false;
|
||||
|
||||
size_t bytesWritten = 0;
|
||||
|
||||
do {
|
||||
int readResult = mpg123_read(
|
||||
this->decoder,
|
||||
targetBuffer,
|
||||
buffer->Bytes(),
|
||||
&bytesWritten);
|
||||
|
||||
switch (readResult) {
|
||||
case MPG123_OK:
|
||||
break;
|
||||
|
||||
case MPG123_DONE:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case MPG123_NEED_MORE:
|
||||
if (!this->Feed()) {
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case MPG123_ERR:
|
||||
return false;
|
||||
break;
|
||||
|
||||
case MPG123_NEW_FORMAT: {
|
||||
#if DEBUG > 0
|
||||
int encoding = 0;
|
||||
|
||||
mpg123_getformat(
|
||||
this->decoder,
|
||||
&this->sampleRate,
|
||||
&this->channels,
|
||||
&encoding);
|
||||
|
||||
std::cerr << "output format:"
|
||||
"\n type: " << encoding <<
|
||||
"\n rate " << this->sampleRate <<
|
||||
"\n channels " << this->channels << "\n";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} while (bytesWritten == 0 && !done);
|
||||
|
||||
buffer->SetSamples(bytesWritten / this->sampleSizeBytes);
|
||||
|
||||
return (bytesWritten > 0);
|
||||
}
|
||||
|
||||
bool Mpg123Decoder::Feed() {
|
||||
if (this->fileStream) {
|
||||
unsigned char buffer[STREAM_FEED_SIZE];
|
||||
|
||||
long long bytesRead =
|
||||
this->fileStream->Read(&buffer, STREAM_FEED_SIZE);
|
||||
|
||||
if (bytesRead) {
|
||||
if (mpg123_feed(this->decoder, buffer, bytesRead) == MPG123_OK) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mpg123Decoder::Open(IDataStream *fileStream){
|
||||
if (this->decoder && fileStream) {
|
||||
this->fileStream = fileStream;
|
||||
|
||||
if (mpg123_open_feed(this->decoder) == MPG123_OK) {
|
||||
int result = mpg123_param(
|
||||
this->decoder,
|
||||
MPG123_ADD_FLAGS,
|
||||
MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS | MPG123_QUIET,
|
||||
0);
|
||||
|
||||
mpg123_set_filesize(this->decoder, this->fileStream->Length());
|
||||
|
||||
/* reset output table */
|
||||
mpg123_format_none(this->decoder);
|
||||
|
||||
/* force the output encoding to float32. note that this needs to
|
||||
be done before any data is fed to the decoder! */
|
||||
mpg123_format(
|
||||
this->decoder,
|
||||
this->sampleRate,
|
||||
MPG123_STEREO,
|
||||
MPG123_ENC_FLOAT_32);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Mpg123Decoder.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define STREAM_FEED_SIZE 2048 * 8
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG > 0
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::io;
|
||||
|
||||
Mpg123Decoder::Mpg123Decoder()
|
||||
: cachedLength(0)
|
||||
, decoder(NULL)
|
||||
, sampleRate(44100)
|
||||
, channels(2)
|
||||
, fileStream(NULL)
|
||||
, lastMpg123Status(MPG123_NEED_MORE) {
|
||||
this->decoder = mpg123_new(NULL, NULL);
|
||||
this->sampleSizeBytes = this->channels * sizeof(float);
|
||||
}
|
||||
|
||||
Mpg123Decoder::~Mpg123Decoder() {
|
||||
if (this->decoder) {
|
||||
mpg123_close(this->decoder);
|
||||
mpg123_delete(this->decoder);
|
||||
this->decoder = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Mpg123Decoder::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
double Mpg123Decoder::SetPosition(double second) {
|
||||
off_t seekToFileOffset = 0;
|
||||
off_t seekToSampleOffset = second * (double)this->sampleRate;
|
||||
off_t *indexOffset = 0;
|
||||
off_t indexSet = 0;
|
||||
size_t indexFill = 0;
|
||||
int err = mpg123_index(this->decoder, &indexOffset, &indexSet, &indexFill);
|
||||
|
||||
off_t seekedTo = 0;
|
||||
int feedMore = 20;
|
||||
while((seekedTo = mpg123_feedseek(
|
||||
this->decoder,
|
||||
seekToSampleOffset,
|
||||
SEEK_SET,
|
||||
&seekToFileOffset)) == MPG123_NEED_MORE && feedMore > 0)
|
||||
{
|
||||
if (!this->Feed()) {
|
||||
feedMore = 0;
|
||||
}
|
||||
|
||||
feedMore--;
|
||||
}
|
||||
|
||||
if (seekedTo >= 0) {
|
||||
if (this->fileStream->SetPosition(seekToFileOffset)) {
|
||||
return second;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Mpg123Decoder::GetBuffer(IBuffer *buffer) {
|
||||
buffer->SetChannels(this->channels);
|
||||
buffer->SetSampleRate(this->sampleRate);
|
||||
buffer->SetSamples(STREAM_FEED_SIZE / this->channels);
|
||||
|
||||
unsigned char* targetBuffer = (unsigned char*) (buffer->BufferPointer());
|
||||
|
||||
bool done = false;
|
||||
|
||||
size_t bytesWritten = 0;
|
||||
|
||||
do {
|
||||
int readResult = mpg123_read(
|
||||
this->decoder,
|
||||
targetBuffer,
|
||||
buffer->Bytes(),
|
||||
&bytesWritten);
|
||||
|
||||
switch (readResult) {
|
||||
case MPG123_OK:
|
||||
break;
|
||||
|
||||
case MPG123_DONE:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case MPG123_NEED_MORE:
|
||||
if (!this->Feed()) {
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case MPG123_ERR:
|
||||
return false;
|
||||
break;
|
||||
|
||||
case MPG123_NEW_FORMAT: {
|
||||
#if DEBUG > 0
|
||||
int encoding = 0;
|
||||
|
||||
mpg123_getformat(
|
||||
this->decoder,
|
||||
&this->sampleRate,
|
||||
&this->channels,
|
||||
&encoding);
|
||||
|
||||
std::cerr << "output format:"
|
||||
"\n type: " << encoding <<
|
||||
"\n rate " << this->sampleRate <<
|
||||
"\n channels " << this->channels << "\n";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} while (bytesWritten == 0 && !done);
|
||||
|
||||
buffer->SetSamples(bytesWritten / this->sampleSizeBytes);
|
||||
|
||||
return (bytesWritten > 0);
|
||||
}
|
||||
|
||||
bool Mpg123Decoder::Feed() {
|
||||
if (this->fileStream) {
|
||||
unsigned char buffer[STREAM_FEED_SIZE];
|
||||
|
||||
long long bytesRead =
|
||||
this->fileStream->Read(&buffer, STREAM_FEED_SIZE);
|
||||
|
||||
if (bytesRead) {
|
||||
if (mpg123_feed(this->decoder, buffer, bytesRead) == MPG123_OK) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mpg123Decoder::Open(IDataStream *fileStream){
|
||||
if (this->decoder && fileStream) {
|
||||
this->fileStream = fileStream;
|
||||
|
||||
if (mpg123_open_feed(this->decoder) == MPG123_OK) {
|
||||
int result = mpg123_param(
|
||||
this->decoder,
|
||||
MPG123_ADD_FLAGS,
|
||||
MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS | MPG123_QUIET,
|
||||
0);
|
||||
|
||||
mpg123_set_filesize(this->decoder, this->fileStream->Length());
|
||||
|
||||
/* reset output table */
|
||||
mpg123_format_none(this->decoder);
|
||||
|
||||
/* force the output encoding to float32. note that this needs to
|
||||
be done before any data is fed to the decoder! */
|
||||
mpg123_format(
|
||||
this->decoder,
|
||||
this->sampleRate,
|
||||
MPG123_STEREO,
|
||||
MPG123_ENC_FLOAT_32);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -30,45 +30,45 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "Mpg123DecoderFactory.h"
|
||||
#include "Mpg123Decoder.h"
|
||||
#include "mpg123.h"
|
||||
|
||||
using namespace musik::core::audio;
|
||||
|
||||
Mpg123DecoderFactory::Mpg123DecoderFactory() {
|
||||
mpg123_init();
|
||||
}
|
||||
|
||||
Mpg123DecoderFactory::~Mpg123DecoderFactory() {
|
||||
mpg123_exit();
|
||||
}
|
||||
|
||||
void Mpg123DecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* Mpg123DecoderFactory::CreateDecoder() {
|
||||
return new Mpg123Decoder();
|
||||
}
|
||||
|
||||
bool Mpg123DecoderFactory::CanHandle(const char* type) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
if (musik::sdk::endsWith(str, ".mp3") ||
|
||||
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;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "Mpg123DecoderFactory.h"
|
||||
#include "Mpg123Decoder.h"
|
||||
#include "mpg123.h"
|
||||
|
||||
using namespace musik::core::audio;
|
||||
|
||||
Mpg123DecoderFactory::Mpg123DecoderFactory() {
|
||||
mpg123_init();
|
||||
}
|
||||
|
||||
Mpg123DecoderFactory::~Mpg123DecoderFactory() {
|
||||
mpg123_exit();
|
||||
}
|
||||
|
||||
void Mpg123DecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* Mpg123DecoderFactory::CreateDecoder() {
|
||||
return new Mpg123Decoder();
|
||||
}
|
||||
|
||||
bool Mpg123DecoderFactory::CanHandle(const char* type) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
if (musik::sdk::endsWith(str, ".mp3") ||
|
||||
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;
|
||||
}
|
||||
|
@ -30,35 +30,35 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include "Mpg123DecoderFactory.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class Mpg123Plugin : public musik::core::IPlugin {
|
||||
virtual void Destroy() { delete this; }
|
||||
virtual const char* Name() { return "mpg123 IDecoder"; }
|
||||
virtual const char* Version() { return "0.2"; }
|
||||
virtual const char* Author() { return "Daniel Önnerby, clangen"; }
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new Mpg123Plugin();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT musik::core::audio::IDecoderFactory* GetDecoderFactory() {
|
||||
return new Mpg123DecoderFactory();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include "Mpg123DecoderFactory.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class Mpg123Plugin : public musik::core::IPlugin {
|
||||
virtual void Destroy() { delete this; }
|
||||
virtual const char* Name() { return "mpg123 IDecoder"; }
|
||||
virtual const char* Version() { return "0.2"; }
|
||||
virtual const char* Author() { return "Daniel Önnerby, clangen"; }
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new Mpg123Plugin();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT musik::core::audio::IDecoderFactory* GetDecoderFactory() {
|
||||
return new Mpg123DecoderFactory();
|
||||
}
|
||||
|
@ -30,6 +30,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
@ -30,195 +30,195 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "OggDecoder.h"
|
||||
|
||||
#define OGG_MAX_SAMPLES 1024
|
||||
|
||||
OggDecoder::OggDecoder() {
|
||||
this->oggCallbacks.read_func = &OggRead;
|
||||
this->oggCallbacks.seek_func = &OggSeek;
|
||||
this->oggCallbacks.tell_func = &OggTell;
|
||||
this->oggCallbacks.close_func = &OggClose;
|
||||
}
|
||||
|
||||
size_t OggDecoder::OggRead(void *buffer, size_t nofParts, size_t partSize, void *datasource) {
|
||||
return (size_t)((OggDecoder*)datasource)->fileStream->Read(buffer,(long)(nofParts*partSize));
|
||||
}
|
||||
|
||||
int OggDecoder::OggSeek(void *datasource, ogg_int64_t offset, int whence) {
|
||||
OggDecoder* decoder = (OggDecoder*) datasource;
|
||||
|
||||
switch(whence) {
|
||||
case SEEK_CUR:
|
||||
{
|
||||
long currentPosition = decoder->fileStream->Position();
|
||||
if (decoder->fileStream->SetPosition(currentPosition + (long) offset)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SEEK_END:
|
||||
{
|
||||
long fileSize = decoder->fileStream->Length();
|
||||
if (decoder->fileStream->SetPosition(fileSize)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SEEK_SET:
|
||||
{
|
||||
if (decoder->fileStream->SetPosition((long) offset)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long OggDecoder::OggTell(void *datasource) {
|
||||
return ((OggDecoder*) datasource)->fileStream->Position();
|
||||
}
|
||||
|
||||
int OggDecoder::OggClose(void *datasource) {
|
||||
if(((OggDecoder*) datasource)->fileStream->Close()) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
OggDecoder::~OggDecoder() {
|
||||
}
|
||||
|
||||
bool OggDecoder::Open(musik::core::io::IDataStream *fileStream) {
|
||||
this->fileStream = fileStream;
|
||||
|
||||
if (ov_open_callbacks(this, &this->oggFile, NULL, 0, this->oggCallbacks) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OggDecoder::Destroy() {
|
||||
ov_clear(&this->oggFile);
|
||||
delete this;
|
||||
}
|
||||
|
||||
double OggDecoder::SetPosition(double second) {
|
||||
if (ov_seekable(&this->oggFile)) {
|
||||
if (!ov_time_seek(&this->oggFile, second)) {
|
||||
return ov_time_tell(&this->oggFile);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
bool OggDecoder::GetBuffer(IBuffer *buffer) {
|
||||
int bitstream;
|
||||
float **pcm;
|
||||
|
||||
unsigned long samplesRead = ov_read_float(
|
||||
&this->oggFile,
|
||||
&pcm,
|
||||
OGG_MAX_SAMPLES,
|
||||
&bitstream);
|
||||
|
||||
if (samplesRead == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vorbis_info *info = ov_info(&this->oggFile, -1);
|
||||
buffer->SetChannels(info->channels);
|
||||
buffer->SetSampleRate(info->rate);
|
||||
buffer->SetSamples(samplesRead);
|
||||
|
||||
/*
|
||||
The musik audio engine expects:
|
||||
|
||||
SPEAKER_FRONT_LEFT 0x1
|
||||
SPEAKER_FRONT_RIGHT 0x2
|
||||
SPEAKER_FRONT_CENTER 0x4
|
||||
SPEAKER_LOW_FREQUENCY 0x8
|
||||
SPEAKER_BACK_LEFT 0x10
|
||||
SPEAKER_BACK_RIGHT 0x20
|
||||
|
||||
OGG returns:
|
||||
|
||||
One channel: the stream is monophonic
|
||||
Two channels: the stream is stereo. channel order: left, right.
|
||||
Three channels: the stream is a 1d-surround encoding. channel order: left, center, right
|
||||
Four channels: the stream is quadraphonic surround. channel order: front left, front right, rear left, rear right
|
||||
Five channels: the stream is five-channel surround. channel order: front left, front center, front right, rear left, rear right
|
||||
Six channels: (used in Dolby Digital/AC3) the stream is 5,1 surround. channel order: front left, front center, front right, rear left, rear right, LFE (the sixth channel is entirely bass).
|
||||
|
||||
... so let's re-order when writing to our output buffer ...
|
||||
*/
|
||||
|
||||
float *pDataBuffer = buffer->BufferPointer();
|
||||
|
||||
if (info->channels == 2) {
|
||||
for (unsigned long x = 0; x < samplesRead; ++x) {
|
||||
pDataBuffer[0] = pcm[0][x];
|
||||
pDataBuffer[1] = pcm[1][x];
|
||||
pDataBuffer += 2;
|
||||
}
|
||||
}
|
||||
else if (info->channels == 3) {
|
||||
for (unsigned long x=0; x < samplesRead; ++x) {
|
||||
*pDataBuffer = pcm[0][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[2][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[1][x];
|
||||
++pDataBuffer;
|
||||
}
|
||||
}
|
||||
else if (info->channels == 5) {
|
||||
for (unsigned long x = 0; x < samplesRead; ++x) {
|
||||
*pDataBuffer = pcm[0][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[2][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[1][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[3][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[4][x];
|
||||
++pDataBuffer;
|
||||
}
|
||||
}
|
||||
else if (info->channels == 6) {
|
||||
for (unsigned long x = 0; x < samplesRead; ++x) {
|
||||
*pDataBuffer = pcm[0][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[2][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[1][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[5][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[3][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[4][x];
|
||||
++pDataBuffer;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (unsigned long x = 0; x < samplesRead; ++x) {
|
||||
for (int channel(0); channel < info->channels; ++channel) {
|
||||
*pDataBuffer = pcm[channel][x];
|
||||
++pDataBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "OggDecoder.h"
|
||||
|
||||
#define OGG_MAX_SAMPLES 1024
|
||||
|
||||
OggDecoder::OggDecoder() {
|
||||
this->oggCallbacks.read_func = &OggRead;
|
||||
this->oggCallbacks.seek_func = &OggSeek;
|
||||
this->oggCallbacks.tell_func = &OggTell;
|
||||
this->oggCallbacks.close_func = &OggClose;
|
||||
}
|
||||
|
||||
size_t OggDecoder::OggRead(void *buffer, size_t nofParts, size_t partSize, void *datasource) {
|
||||
return (size_t)((OggDecoder*)datasource)->fileStream->Read(buffer,(long)(nofParts*partSize));
|
||||
}
|
||||
|
||||
int OggDecoder::OggSeek(void *datasource, ogg_int64_t offset, int whence) {
|
||||
OggDecoder* decoder = (OggDecoder*) datasource;
|
||||
|
||||
switch(whence) {
|
||||
case SEEK_CUR:
|
||||
{
|
||||
long currentPosition = decoder->fileStream->Position();
|
||||
if (decoder->fileStream->SetPosition(currentPosition + (long) offset)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SEEK_END:
|
||||
{
|
||||
long fileSize = decoder->fileStream->Length();
|
||||
if (decoder->fileStream->SetPosition(fileSize)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SEEK_SET:
|
||||
{
|
||||
if (decoder->fileStream->SetPosition((long) offset)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long OggDecoder::OggTell(void *datasource) {
|
||||
return ((OggDecoder*) datasource)->fileStream->Position();
|
||||
}
|
||||
|
||||
int OggDecoder::OggClose(void *datasource) {
|
||||
if(((OggDecoder*) datasource)->fileStream->Close()) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
OggDecoder::~OggDecoder() {
|
||||
}
|
||||
|
||||
bool OggDecoder::Open(musik::core::io::IDataStream *fileStream) {
|
||||
this->fileStream = fileStream;
|
||||
|
||||
if (ov_open_callbacks(this, &this->oggFile, NULL, 0, this->oggCallbacks) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OggDecoder::Destroy() {
|
||||
ov_clear(&this->oggFile);
|
||||
delete this;
|
||||
}
|
||||
|
||||
double OggDecoder::SetPosition(double second) {
|
||||
if (ov_seekable(&this->oggFile)) {
|
||||
if (!ov_time_seek(&this->oggFile, second)) {
|
||||
return ov_time_tell(&this->oggFile);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
bool OggDecoder::GetBuffer(IBuffer *buffer) {
|
||||
int bitstream;
|
||||
float **pcm;
|
||||
|
||||
unsigned long samplesRead = ov_read_float(
|
||||
&this->oggFile,
|
||||
&pcm,
|
||||
OGG_MAX_SAMPLES,
|
||||
&bitstream);
|
||||
|
||||
if (samplesRead == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vorbis_info *info = ov_info(&this->oggFile, -1);
|
||||
buffer->SetChannels(info->channels);
|
||||
buffer->SetSampleRate(info->rate);
|
||||
buffer->SetSamples(samplesRead);
|
||||
|
||||
/*
|
||||
The musik audio engine expects:
|
||||
|
||||
SPEAKER_FRONT_LEFT 0x1
|
||||
SPEAKER_FRONT_RIGHT 0x2
|
||||
SPEAKER_FRONT_CENTER 0x4
|
||||
SPEAKER_LOW_FREQUENCY 0x8
|
||||
SPEAKER_BACK_LEFT 0x10
|
||||
SPEAKER_BACK_RIGHT 0x20
|
||||
|
||||
OGG returns:
|
||||
|
||||
One channel: the stream is monophonic
|
||||
Two channels: the stream is stereo. channel order: left, right.
|
||||
Three channels: the stream is a 1d-surround encoding. channel order: left, center, right
|
||||
Four channels: the stream is quadraphonic surround. channel order: front left, front right, rear left, rear right
|
||||
Five channels: the stream is five-channel surround. channel order: front left, front center, front right, rear left, rear right
|
||||
Six channels: (used in Dolby Digital/AC3) the stream is 5,1 surround. channel order: front left, front center, front right, rear left, rear right, LFE (the sixth channel is entirely bass).
|
||||
|
||||
... so let's re-order when writing to our output buffer ...
|
||||
*/
|
||||
|
||||
float *pDataBuffer = buffer->BufferPointer();
|
||||
|
||||
if (info->channels == 2) {
|
||||
for (unsigned long x = 0; x < samplesRead; ++x) {
|
||||
pDataBuffer[0] = pcm[0][x];
|
||||
pDataBuffer[1] = pcm[1][x];
|
||||
pDataBuffer += 2;
|
||||
}
|
||||
}
|
||||
else if (info->channels == 3) {
|
||||
for (unsigned long x=0; x < samplesRead; ++x) {
|
||||
*pDataBuffer = pcm[0][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[2][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[1][x];
|
||||
++pDataBuffer;
|
||||
}
|
||||
}
|
||||
else if (info->channels == 5) {
|
||||
for (unsigned long x = 0; x < samplesRead; ++x) {
|
||||
*pDataBuffer = pcm[0][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[2][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[1][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[3][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[4][x];
|
||||
++pDataBuffer;
|
||||
}
|
||||
}
|
||||
else if (info->channels == 6) {
|
||||
for (unsigned long x = 0; x < samplesRead; ++x) {
|
||||
*pDataBuffer = pcm[0][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[2][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[1][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[5][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[3][x];
|
||||
++pDataBuffer;
|
||||
*pDataBuffer = pcm[4][x];
|
||||
++pDataBuffer;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (unsigned long x = 0; x < samplesRead; ++x) {
|
||||
for (int channel(0); channel < info->channels; ++channel) {
|
||||
*pDataBuffer = pcm[channel][x];
|
||||
++pDataBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -30,34 +30,34 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <cctype>
|
||||
#include "OggDecoderFactory.h"
|
||||
#include "OggDecoder.h"
|
||||
|
||||
OggDecoderFactory::OggDecoderFactory() {
|
||||
}
|
||||
|
||||
OggDecoderFactory::~OggDecoderFactory() {
|
||||
}
|
||||
|
||||
void OggDecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* OggDecoderFactory::CreateDecoder() {
|
||||
return new OggDecoder();
|
||||
}
|
||||
|
||||
bool OggDecoderFactory::CanHandle(const char* type) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
return
|
||||
musik::sdk::endsWith(str, ".ogg") ||
|
||||
musik::sdk::endsWith(str, ".oga") ||
|
||||
str.find("audio/ogg") != std::string::npos ||
|
||||
str.find("audio/vorbis") != std::string::npos;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <cctype>
|
||||
#include "OggDecoderFactory.h"
|
||||
#include "OggDecoder.h"
|
||||
|
||||
OggDecoderFactory::OggDecoderFactory() {
|
||||
}
|
||||
|
||||
OggDecoderFactory::~OggDecoderFactory() {
|
||||
}
|
||||
|
||||
void OggDecoderFactory::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
IDecoder* OggDecoderFactory::CreateDecoder() {
|
||||
return new OggDecoder();
|
||||
}
|
||||
|
||||
bool OggDecoderFactory::CanHandle(const char* type) const {
|
||||
std::string str(type);
|
||||
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
||||
|
||||
return
|
||||
musik::sdk::endsWith(str, ".ogg") ||
|
||||
musik::sdk::endsWith(str, ".oga") ||
|
||||
str.find("audio/ogg") != std::string::npos ||
|
||||
str.find("audio/vorbis") != std::string::npos;
|
||||
}
|
||||
|
@ -30,36 +30,36 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include "OggDecoderFactory.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class OggDecoderPlugin : public musik::core::IPlugin {
|
||||
public:
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "Ogg IDecoder"; };
|
||||
const char* Version() { return "0.2"; };
|
||||
const char* Author() { return "Björn Olievier, clangen"; };
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new OggDecoderPlugin();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT IDecoderFactory* GetDecoderFactory() {
|
||||
return new OggDecoderFactory();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include "OggDecoderFactory.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class OggDecoderPlugin : public musik::core::IPlugin {
|
||||
public:
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "Ogg IDecoder"; };
|
||||
const char* Version() { return "0.2"; };
|
||||
const char* Author() { return "Björn Olievier, clangen"; };
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new OggDecoderPlugin();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT IDecoderFactory* GetDecoderFactory() {
|
||||
return new OggDecoderFactory();
|
||||
}
|
||||
|
@ -30,6 +30,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
@ -1,73 +1,73 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2009, Julian Cromarty
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <core/IPlugin.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "StdOut.h"
|
||||
|
||||
class StdOutPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; };
|
||||
|
||||
const utfchar* Name() { return TEXT("StdOut output plugin"); };
|
||||
const utfchar* Version() { return TEXT("0.1"); };
|
||||
const utfchar* Author() { return TEXT("Julian Cromarty"); };
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
musik::core::IPlugin* GetPlugin()
|
||||
{
|
||||
return new StdOutPlugin();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
extern "C" {
|
||||
musik::core::audio::IAudioOutputSupplier* CreateAudioOutputSupplier()
|
||||
{
|
||||
return new StdOutSupplier();
|
||||
}
|
||||
}*/
|
||||
|
||||
extern "C" {
|
||||
musik::core::audio::IOutput* GetAudioOutput()
|
||||
{
|
||||
return new StdOut();
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2009, Julian Cromarty
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <core/IPlugin.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "StdOut.h"
|
||||
|
||||
class StdOutPlugin : public musik::core::IPlugin
|
||||
{
|
||||
void Destroy() { delete this; };
|
||||
|
||||
const utfchar* Name() { return TEXT("StdOut output plugin"); };
|
||||
const utfchar* Version() { return TEXT("0.1"); };
|
||||
const utfchar* Author() { return TEXT("Julian Cromarty"); };
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
musik::core::IPlugin* GetPlugin()
|
||||
{
|
||||
return new StdOutPlugin();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
extern "C" {
|
||||
musik::core::audio::IAudioOutputSupplier* CreateAudioOutputSupplier()
|
||||
{
|
||||
return new StdOutSupplier();
|
||||
}
|
||||
}*/
|
||||
|
||||
extern "C" {
|
||||
musik::core::audio::IOutput* GetAudioOutput()
|
||||
{
|
||||
return new StdOut();
|
||||
}
|
||||
}
|
||||
|
@ -30,428 +30,428 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TaglibMetadataReader.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <taglib/toolkit/tlist.h>
|
||||
#include <taglib/toolkit/tfile.h>
|
||||
#include <taglib/tag.h>
|
||||
#include <taglib/fileref.h>
|
||||
#include <taglib/audioproperties.h>
|
||||
#include <taglib/mpeg/mpegfile.h>
|
||||
#include <taglib/mpeg/id3v1/id3v1tag.h>
|
||||
#include <taglib/mpeg/id3v1/id3v1genres.h>
|
||||
#include <taglib/mpeg/id3v2/id3v2tag.h>
|
||||
#include <taglib/mpeg/id3v2/id3v2header.h>
|
||||
#include <taglib/mpeg/id3v2/id3v2frame.h>
|
||||
#include <taglib/mpeg/id3v2/frames/attachedpictureframe.h>
|
||||
#include <taglib/mpeg/id3v2/frames/commentsframe.h>
|
||||
#include <taglib/mp4/mp4file.h>
|
||||
#include <taglib/ogg/oggfile.h>
|
||||
#include <taglib/toolkit/tpropertymap.h>
|
||||
#else
|
||||
#include <taglib/tlist.h>
|
||||
#include <taglib/tfile.h>
|
||||
#include <taglib/tag.h>
|
||||
#include <taglib/fileref.h>
|
||||
#include <taglib/audioproperties.h>
|
||||
#include <taglib/mpegfile.h>
|
||||
#include <taglib/id3v1tag.h>
|
||||
#include <taglib/id3v1genres.h>
|
||||
#include <taglib/id3v2tag.h>
|
||||
#include <taglib/id3v2header.h>
|
||||
#include <taglib/id3v2frame.h>
|
||||
#include <taglib/attachedpictureframe.h>
|
||||
#include <taglib/commentsframe.h>
|
||||
#include <taglib/mp4file.h>
|
||||
#include <taglib/oggfile.h>
|
||||
#include <taglib/tpropertymap.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
static inline std::wstring utf8to16(const char* utf8) {
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, 0, 0);
|
||||
wchar_t* buffer = new wchar_t[size];
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, buffer, size);
|
||||
std::wstring utf16fn(buffer);
|
||||
delete[] buffer;
|
||||
return utf16fn;
|
||||
}
|
||||
#endif
|
||||
|
||||
TaglibMetadataReader::TaglibMetadataReader() {
|
||||
}
|
||||
|
||||
TaglibMetadataReader::~TaglibMetadataReader() {
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool TaglibMetadataReader::CanRead(const char *extension){
|
||||
if (extension) {
|
||||
std::string ext(extension);
|
||||
boost::algorithm::to_lower(ext);
|
||||
|
||||
return
|
||||
ext.compare("mp3") == 0 ||
|
||||
ext.compare("ogg") == 0 ||
|
||||
ext.compare("aac") == 0 ||
|
||||
ext.compare("m4a") == 0 ||
|
||||
ext.compare("flac") == 0 ||
|
||||
ext.compare("ape") == 0 ||
|
||||
ext.compare("mpc") == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TaglibMetadataReader::Read(const char* uri, musik::core::IMetadataWriter *track) {
|
||||
std::string path(uri);
|
||||
std::string extension;
|
||||
|
||||
std::string::size_type lastDot = path.find_last_of(".");
|
||||
if (lastDot != std::string::npos) {
|
||||
extension = path.substr(lastDot + 1).c_str();
|
||||
}
|
||||
|
||||
if (extension.size()) {
|
||||
boost::algorithm::to_lower(extension);
|
||||
|
||||
if (extension == "mp3") {
|
||||
this->GetID3v2Tag(uri, track);
|
||||
}
|
||||
}
|
||||
|
||||
return this->GetGenericTag(uri, track);
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
bool TaglibMetadataReader::GetGenericTag(const char* uri, musik::core::IMetadataWriter *target) {
|
||||
#ifdef WIN32
|
||||
TagLib::FileRef file(utf8to16(uri).c_str());
|
||||
#else
|
||||
TagLib::FileRef file(uri);
|
||||
#endif
|
||||
|
||||
if (!file.isNull()) {
|
||||
TagLib::Tag *tag = file.tag();
|
||||
|
||||
if (tag) {
|
||||
if (!tag->title().isEmpty()) {
|
||||
this->SetTagValue("title", tag->title(), target);
|
||||
}
|
||||
else {
|
||||
this->SetTagValue("title", uri, target);
|
||||
}
|
||||
|
||||
this->SetTagValue("album",tag->album(), target);
|
||||
this->SetSlashSeparatedValues("artist",tag->artist() , target);
|
||||
this->SetTagValue("genre",tag->genre(), target);
|
||||
this->SetTagValue("comment",tag->comment(), target);
|
||||
|
||||
if (tag->track()) {
|
||||
this->SetTagValue("track", tag->track(), target);
|
||||
}
|
||||
|
||||
if (tag->year()) {
|
||||
this->SetTagValue("year", tag->year(), target);
|
||||
}
|
||||
|
||||
TagLib::PropertyMap map = tag->properties();
|
||||
if (map.contains("DISCNUMBER")) {
|
||||
TagLib::StringList value = map["DISCNUMBER"];
|
||||
if (value.size()) {
|
||||
this->SetTagValue("disc", value[0], target);
|
||||
}
|
||||
}
|
||||
|
||||
TagLib::AudioProperties *audio = file.audioProperties();
|
||||
this->SetAudioProperties(audio, target);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TaglibMetadataReader::GetID3v2Tag(const char* uri, musik::core::IMetadataWriter *track) {
|
||||
TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);
|
||||
|
||||
#ifdef WIN32
|
||||
TagLib::MPEG::File file(utf8to16(uri).c_str());
|
||||
#else
|
||||
TagLib::MPEG::File file(uri);
|
||||
#endif
|
||||
|
||||
TagLib::ID3v2::Tag *id3v2 = file.ID3v2Tag();
|
||||
|
||||
if (id3v2) {
|
||||
TagLib::AudioProperties *audio = file.audioProperties();
|
||||
TagLib::ID3v2::FrameListMap allTags = id3v2->frameListMap();
|
||||
|
||||
if (!id3v2->title().isEmpty()) {
|
||||
this->SetTagValue("title", id3v2->title(), track);
|
||||
}
|
||||
|
||||
this->SetTagValue("album", id3v2->album(), track);
|
||||
|
||||
/* year */
|
||||
|
||||
if (!allTags["TYER"].isEmpty()) { /* ID3v2.3*/
|
||||
this->SetTagValue("year", allTags["TYER"].front()->toString().substr(0, 4), track);
|
||||
}
|
||||
|
||||
if (!allTags["TDRC"].isEmpty()) { /* ID3v2.4*/
|
||||
this->SetTagValue("year", allTags["TDRC"].front()->toString().substr(0, 4), track);
|
||||
}
|
||||
|
||||
/* TRCK is the track number (or "trackNum/totalTracks") */
|
||||
|
||||
std::vector<std::string> splitTrack;
|
||||
if (!allTags["TRCK"].isEmpty()) {
|
||||
std::string tempTrack = allTags["TRCK"].front()->toString().toCString(true);
|
||||
boost::algorithm::split(splitTrack, tempTrack, boost::algorithm::is_any_of("/"));
|
||||
this->SetTagValue("track", splitTrack[0].c_str(), track);
|
||||
|
||||
if (splitTrack.size() > 1) {
|
||||
this->SetTagValue("totaltracks", splitTrack[1].c_str(), track);
|
||||
}
|
||||
}
|
||||
|
||||
this->SetTagValues("bpm", allTags["TBPM"], track);
|
||||
this->SetSlashSeparatedValues("composer", allTags["TCOM"], track);
|
||||
this->SetTagValues("copyright", allTags["TCOP"], track);
|
||||
this->SetTagValues("encoder", allTags["TENC"], track);
|
||||
this->SetTagValues("writer", allTags["TEXT"], track);
|
||||
this->SetTagValues("org.writer", allTags["TOLY"], track);
|
||||
this->SetSlashSeparatedValues("publisher", allTags["TPUB"], track);
|
||||
this->SetTagValues("mood", allTags["TMOO"], track);
|
||||
this->SetSlashSeparatedValues("org.artist", allTags["TOPE"], track);
|
||||
this->SetTagValues("language", allTags["TLAN"], track);
|
||||
this->SetTagValues("disc", allTags["TPOS"], track);
|
||||
this->SetTagValues("lyrics", allTags["USLT"], track);
|
||||
this->SetTagValues("disc", allTags["TPOS"], track);
|
||||
|
||||
/* genre. note that multiple genres may be present */
|
||||
|
||||
if (!allTags["TCON"].isEmpty()) {
|
||||
TagLib::ID3v2::FrameList genres = allTags["TCON"];
|
||||
|
||||
TagLib::ID3v2::FrameList::ConstIterator it = genres.begin();
|
||||
|
||||
for (; it != genres.end(); ++it) {
|
||||
TagLib::String genreString = (*it)->toString();
|
||||
|
||||
if (!genreString.isEmpty()) {
|
||||
/* note1: apparently genres will already be de-duped */
|
||||
int numberLength = 0;
|
||||
bool isNumber = true;
|
||||
|
||||
TagLib::String::ConstIterator charIt = genreString.begin();
|
||||
for (; isNumber && charIt != genreString.end(); ++charIt) {
|
||||
isNumber = (*charIt >= '0' && *charIt <= '9');
|
||||
|
||||
if (isNumber) {
|
||||
++numberLength;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNumber) { /* old ID3v1 tags had numbers for genres. */
|
||||
int genreNumber = genreString.toInt();
|
||||
if (genreNumber >= 0 && genreNumber <= 255) {
|
||||
genreString = TagLib::ID3v1::genre(genreNumber);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (numberLength > 0) { /* genre may start with a number. */
|
||||
if (genreString.substr(numberLength, 1) == " ") {
|
||||
int genreNumber = genreString.substr(0, numberLength).toInt();
|
||||
if (genreNumber >= 0 && genreNumber <= 255) {
|
||||
this->SetTagValue("genre", TagLib::ID3v1::genre(genreNumber), track);
|
||||
}
|
||||
|
||||
/* strip the number */
|
||||
genreString = genreString.substr(numberLength + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!genreString.isEmpty()) {
|
||||
this->SetTagValue("genre", genreString, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* artists */
|
||||
|
||||
this->SetSlashSeparatedValues("artist" ,allTags["TPE1"], track);
|
||||
this->SetSlashSeparatedValues("album_artist", allTags["TPE2"], track);
|
||||
this->SetSlashSeparatedValues("conductor", allTags["TPE3"], track);
|
||||
this->SetSlashSeparatedValues("interpreted", allTags["TPE4"], track);
|
||||
|
||||
/* audio properties include things like bitrate, channels, and duration */
|
||||
|
||||
this->SetAudioProperties(audio, track);
|
||||
|
||||
/* comments, mood, and rating */
|
||||
|
||||
TagLib::ID3v2::FrameList comments = allTags["COMM"];
|
||||
|
||||
TagLib::ID3v2::FrameList::Iterator it = comments.begin();
|
||||
for ( ; it != comments.end(); ++it) {
|
||||
TagLib::ID3v2::CommentsFrame *comment
|
||||
= dynamic_cast<TagLib::ID3v2::CommentsFrame*> (*it);
|
||||
|
||||
TagLib::String temp = comment->description();
|
||||
std::string description(temp.begin(), temp.end());
|
||||
|
||||
if (description.empty()) {
|
||||
this->SetTagValue("comment", comment->toString(), track);
|
||||
}
|
||||
else if (description.compare("MusicMatch_Mood") == 0) {
|
||||
this->SetTagValue("mood", comment->toString(), track);
|
||||
}
|
||||
else if (description.compare("MusicMatch_Preference") == 0) {
|
||||
this->SetTagValue("textrating", comment->toString(), track);
|
||||
}
|
||||
}
|
||||
|
||||
/* thumbnail */
|
||||
|
||||
TagLib::ID3v2::FrameList pictures = allTags["APIC"];
|
||||
if(!pictures.isEmpty()) {
|
||||
/* there can be multiple pictures, apparently. let's just use
|
||||
the first one. */
|
||||
|
||||
TagLib::ID3v2::AttachedPictureFrame *picture =
|
||||
static_cast<TagLib::ID3v2::AttachedPictureFrame*>(pictures.front());
|
||||
|
||||
TagLib::ByteVector pictureData = picture->picture();
|
||||
long long size = pictureData.size();
|
||||
|
||||
if(size > 32) { /* noticed that some id3tags have like a 4-8 byte size with no thumbnail */
|
||||
track->SetThumbnail(pictureData.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetTagValue(
|
||||
const char* key,
|
||||
const TagLib::String tagString,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
std::string value(tagString.to8Bit(true));
|
||||
track->SetValue(key, value.c_str());
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetTagValue(
|
||||
const char* key,
|
||||
const char* string,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
std::string temp(string);
|
||||
track->SetValue(key, temp.c_str());
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetTagValue(
|
||||
const char* key,
|
||||
const int tagInt,
|
||||
musik::core::IMetadataWriter *target)
|
||||
{
|
||||
std::string temp = boost::str(boost::format("%1%") % tagInt);
|
||||
target->SetValue(key, temp.c_str());
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetTagValues(
|
||||
const char* key,
|
||||
const TagLib::ID3v2::FrameList &frame,
|
||||
musik::core::IMetadataWriter *target)
|
||||
{
|
||||
if (!frame.isEmpty()) {
|
||||
TagLib::ID3v2::FrameList::ConstIterator value = frame.begin();
|
||||
|
||||
for ( ; value != frame.end(); ++value) {
|
||||
TagLib::String tagString = (*value)->toString();
|
||||
if(!tagString.isEmpty()) {
|
||||
std::string value(tagString.to8Bit(true));
|
||||
target->SetValue(key,value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetSlashSeparatedValues(
|
||||
const char* key,
|
||||
TagLib::String tagString,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
if(!tagString.isEmpty()) {
|
||||
std::string value(tagString.to8Bit(true));
|
||||
std::vector<std::string> splitValues;
|
||||
|
||||
boost::algorithm::split(splitValues, value, boost::algorithm::is_any_of("/"));
|
||||
|
||||
std::vector<std::string>::iterator it = splitValues.begin();
|
||||
for( ; it != splitValues.end(); ++it) {
|
||||
track->SetValue(key, it->c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetSlashSeparatedValues(
|
||||
const char* key,
|
||||
const TagLib::ID3v2::FrameList &frame,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
if(!frame.isEmpty()) {
|
||||
TagLib::ID3v2::FrameList::ConstIterator value = frame.begin();
|
||||
for ( ; value != frame.end(); ++value) {
|
||||
TagLib::String tagString = (*value)->toString();
|
||||
this->SetSlashSeparatedValues(key, tagString, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetAudioProperties(
|
||||
TagLib::AudioProperties *audioProperties,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
/* FIXME: it's overkill to bring boost in just to convert ints to strings */
|
||||
|
||||
if (audioProperties) {
|
||||
std::string duration = boost::str(boost::format("%1%") % audioProperties->length());
|
||||
this->SetTagValue("duration", duration, track);
|
||||
|
||||
int bitrate = audioProperties->bitrate();
|
||||
|
||||
if(bitrate) {
|
||||
std::string temp(boost::str(boost::format("%1%") % bitrate));
|
||||
this->SetTagValue("bitrate", temp, track);
|
||||
}
|
||||
|
||||
int channels = audioProperties->channels();
|
||||
|
||||
if(channels) {
|
||||
std::string temp(boost::str(boost::format("%1%") % channels));
|
||||
this->SetTagValue("channels", temp, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TaglibMetadataReader.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <taglib/toolkit/tlist.h>
|
||||
#include <taglib/toolkit/tfile.h>
|
||||
#include <taglib/tag.h>
|
||||
#include <taglib/fileref.h>
|
||||
#include <taglib/audioproperties.h>
|
||||
#include <taglib/mpeg/mpegfile.h>
|
||||
#include <taglib/mpeg/id3v1/id3v1tag.h>
|
||||
#include <taglib/mpeg/id3v1/id3v1genres.h>
|
||||
#include <taglib/mpeg/id3v2/id3v2tag.h>
|
||||
#include <taglib/mpeg/id3v2/id3v2header.h>
|
||||
#include <taglib/mpeg/id3v2/id3v2frame.h>
|
||||
#include <taglib/mpeg/id3v2/frames/attachedpictureframe.h>
|
||||
#include <taglib/mpeg/id3v2/frames/commentsframe.h>
|
||||
#include <taglib/mp4/mp4file.h>
|
||||
#include <taglib/ogg/oggfile.h>
|
||||
#include <taglib/toolkit/tpropertymap.h>
|
||||
#else
|
||||
#include <taglib/tlist.h>
|
||||
#include <taglib/tfile.h>
|
||||
#include <taglib/tag.h>
|
||||
#include <taglib/fileref.h>
|
||||
#include <taglib/audioproperties.h>
|
||||
#include <taglib/mpegfile.h>
|
||||
#include <taglib/id3v1tag.h>
|
||||
#include <taglib/id3v1genres.h>
|
||||
#include <taglib/id3v2tag.h>
|
||||
#include <taglib/id3v2header.h>
|
||||
#include <taglib/id3v2frame.h>
|
||||
#include <taglib/attachedpictureframe.h>
|
||||
#include <taglib/commentsframe.h>
|
||||
#include <taglib/mp4file.h>
|
||||
#include <taglib/oggfile.h>
|
||||
#include <taglib/tpropertymap.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
static inline std::wstring utf8to16(const char* utf8) {
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, 0, 0);
|
||||
wchar_t* buffer = new wchar_t[size];
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, buffer, size);
|
||||
std::wstring utf16fn(buffer);
|
||||
delete[] buffer;
|
||||
return utf16fn;
|
||||
}
|
||||
#endif
|
||||
|
||||
TaglibMetadataReader::TaglibMetadataReader() {
|
||||
}
|
||||
|
||||
TaglibMetadataReader::~TaglibMetadataReader() {
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool TaglibMetadataReader::CanRead(const char *extension){
|
||||
if (extension) {
|
||||
std::string ext(extension);
|
||||
boost::algorithm::to_lower(ext);
|
||||
|
||||
return
|
||||
ext.compare("mp3") == 0 ||
|
||||
ext.compare("ogg") == 0 ||
|
||||
ext.compare("aac") == 0 ||
|
||||
ext.compare("m4a") == 0 ||
|
||||
ext.compare("flac") == 0 ||
|
||||
ext.compare("ape") == 0 ||
|
||||
ext.compare("mpc") == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TaglibMetadataReader::Read(const char* uri, musik::core::IMetadataWriter *track) {
|
||||
std::string path(uri);
|
||||
std::string extension;
|
||||
|
||||
std::string::size_type lastDot = path.find_last_of(".");
|
||||
if (lastDot != std::string::npos) {
|
||||
extension = path.substr(lastDot + 1).c_str();
|
||||
}
|
||||
|
||||
if (extension.size()) {
|
||||
boost::algorithm::to_lower(extension);
|
||||
|
||||
if (extension == "mp3") {
|
||||
this->GetID3v2Tag(uri, track);
|
||||
}
|
||||
}
|
||||
|
||||
return this->GetGenericTag(uri, track);
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
bool TaglibMetadataReader::GetGenericTag(const char* uri, musik::core::IMetadataWriter *target) {
|
||||
#ifdef WIN32
|
||||
TagLib::FileRef file(utf8to16(uri).c_str());
|
||||
#else
|
||||
TagLib::FileRef file(uri);
|
||||
#endif
|
||||
|
||||
if (!file.isNull()) {
|
||||
TagLib::Tag *tag = file.tag();
|
||||
|
||||
if (tag) {
|
||||
if (!tag->title().isEmpty()) {
|
||||
this->SetTagValue("title", tag->title(), target);
|
||||
}
|
||||
else {
|
||||
this->SetTagValue("title", uri, target);
|
||||
}
|
||||
|
||||
this->SetTagValue("album",tag->album(), target);
|
||||
this->SetSlashSeparatedValues("artist",tag->artist() , target);
|
||||
this->SetTagValue("genre",tag->genre(), target);
|
||||
this->SetTagValue("comment",tag->comment(), target);
|
||||
|
||||
if (tag->track()) {
|
||||
this->SetTagValue("track", tag->track(), target);
|
||||
}
|
||||
|
||||
if (tag->year()) {
|
||||
this->SetTagValue("year", tag->year(), target);
|
||||
}
|
||||
|
||||
TagLib::PropertyMap map = tag->properties();
|
||||
if (map.contains("DISCNUMBER")) {
|
||||
TagLib::StringList value = map["DISCNUMBER"];
|
||||
if (value.size()) {
|
||||
this->SetTagValue("disc", value[0], target);
|
||||
}
|
||||
}
|
||||
|
||||
TagLib::AudioProperties *audio = file.audioProperties();
|
||||
this->SetAudioProperties(audio, target);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TaglibMetadataReader::GetID3v2Tag(const char* uri, musik::core::IMetadataWriter *track) {
|
||||
TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);
|
||||
|
||||
#ifdef WIN32
|
||||
TagLib::MPEG::File file(utf8to16(uri).c_str());
|
||||
#else
|
||||
TagLib::MPEG::File file(uri);
|
||||
#endif
|
||||
|
||||
TagLib::ID3v2::Tag *id3v2 = file.ID3v2Tag();
|
||||
|
||||
if (id3v2) {
|
||||
TagLib::AudioProperties *audio = file.audioProperties();
|
||||
TagLib::ID3v2::FrameListMap allTags = id3v2->frameListMap();
|
||||
|
||||
if (!id3v2->title().isEmpty()) {
|
||||
this->SetTagValue("title", id3v2->title(), track);
|
||||
}
|
||||
|
||||
this->SetTagValue("album", id3v2->album(), track);
|
||||
|
||||
/* year */
|
||||
|
||||
if (!allTags["TYER"].isEmpty()) { /* ID3v2.3*/
|
||||
this->SetTagValue("year", allTags["TYER"].front()->toString().substr(0, 4), track);
|
||||
}
|
||||
|
||||
if (!allTags["TDRC"].isEmpty()) { /* ID3v2.4*/
|
||||
this->SetTagValue("year", allTags["TDRC"].front()->toString().substr(0, 4), track);
|
||||
}
|
||||
|
||||
/* TRCK is the track number (or "trackNum/totalTracks") */
|
||||
|
||||
std::vector<std::string> splitTrack;
|
||||
if (!allTags["TRCK"].isEmpty()) {
|
||||
std::string tempTrack = allTags["TRCK"].front()->toString().toCString(true);
|
||||
boost::algorithm::split(splitTrack, tempTrack, boost::algorithm::is_any_of("/"));
|
||||
this->SetTagValue("track", splitTrack[0].c_str(), track);
|
||||
|
||||
if (splitTrack.size() > 1) {
|
||||
this->SetTagValue("totaltracks", splitTrack[1].c_str(), track);
|
||||
}
|
||||
}
|
||||
|
||||
this->SetTagValues("bpm", allTags["TBPM"], track);
|
||||
this->SetSlashSeparatedValues("composer", allTags["TCOM"], track);
|
||||
this->SetTagValues("copyright", allTags["TCOP"], track);
|
||||
this->SetTagValues("encoder", allTags["TENC"], track);
|
||||
this->SetTagValues("writer", allTags["TEXT"], track);
|
||||
this->SetTagValues("org.writer", allTags["TOLY"], track);
|
||||
this->SetSlashSeparatedValues("publisher", allTags["TPUB"], track);
|
||||
this->SetTagValues("mood", allTags["TMOO"], track);
|
||||
this->SetSlashSeparatedValues("org.artist", allTags["TOPE"], track);
|
||||
this->SetTagValues("language", allTags["TLAN"], track);
|
||||
this->SetTagValues("disc", allTags["TPOS"], track);
|
||||
this->SetTagValues("lyrics", allTags["USLT"], track);
|
||||
this->SetTagValues("disc", allTags["TPOS"], track);
|
||||
|
||||
/* genre. note that multiple genres may be present */
|
||||
|
||||
if (!allTags["TCON"].isEmpty()) {
|
||||
TagLib::ID3v2::FrameList genres = allTags["TCON"];
|
||||
|
||||
TagLib::ID3v2::FrameList::ConstIterator it = genres.begin();
|
||||
|
||||
for (; it != genres.end(); ++it) {
|
||||
TagLib::String genreString = (*it)->toString();
|
||||
|
||||
if (!genreString.isEmpty()) {
|
||||
/* note1: apparently genres will already be de-duped */
|
||||
int numberLength = 0;
|
||||
bool isNumber = true;
|
||||
|
||||
TagLib::String::ConstIterator charIt = genreString.begin();
|
||||
for (; isNumber && charIt != genreString.end(); ++charIt) {
|
||||
isNumber = (*charIt >= '0' && *charIt <= '9');
|
||||
|
||||
if (isNumber) {
|
||||
++numberLength;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNumber) { /* old ID3v1 tags had numbers for genres. */
|
||||
int genreNumber = genreString.toInt();
|
||||
if (genreNumber >= 0 && genreNumber <= 255) {
|
||||
genreString = TagLib::ID3v1::genre(genreNumber);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (numberLength > 0) { /* genre may start with a number. */
|
||||
if (genreString.substr(numberLength, 1) == " ") {
|
||||
int genreNumber = genreString.substr(0, numberLength).toInt();
|
||||
if (genreNumber >= 0 && genreNumber <= 255) {
|
||||
this->SetTagValue("genre", TagLib::ID3v1::genre(genreNumber), track);
|
||||
}
|
||||
|
||||
/* strip the number */
|
||||
genreString = genreString.substr(numberLength + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!genreString.isEmpty()) {
|
||||
this->SetTagValue("genre", genreString, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* artists */
|
||||
|
||||
this->SetSlashSeparatedValues("artist" ,allTags["TPE1"], track);
|
||||
this->SetSlashSeparatedValues("album_artist", allTags["TPE2"], track);
|
||||
this->SetSlashSeparatedValues("conductor", allTags["TPE3"], track);
|
||||
this->SetSlashSeparatedValues("interpreted", allTags["TPE4"], track);
|
||||
|
||||
/* audio properties include things like bitrate, channels, and duration */
|
||||
|
||||
this->SetAudioProperties(audio, track);
|
||||
|
||||
/* comments, mood, and rating */
|
||||
|
||||
TagLib::ID3v2::FrameList comments = allTags["COMM"];
|
||||
|
||||
TagLib::ID3v2::FrameList::Iterator it = comments.begin();
|
||||
for ( ; it != comments.end(); ++it) {
|
||||
TagLib::ID3v2::CommentsFrame *comment
|
||||
= dynamic_cast<TagLib::ID3v2::CommentsFrame*> (*it);
|
||||
|
||||
TagLib::String temp = comment->description();
|
||||
std::string description(temp.begin(), temp.end());
|
||||
|
||||
if (description.empty()) {
|
||||
this->SetTagValue("comment", comment->toString(), track);
|
||||
}
|
||||
else if (description.compare("MusicMatch_Mood") == 0) {
|
||||
this->SetTagValue("mood", comment->toString(), track);
|
||||
}
|
||||
else if (description.compare("MusicMatch_Preference") == 0) {
|
||||
this->SetTagValue("textrating", comment->toString(), track);
|
||||
}
|
||||
}
|
||||
|
||||
/* thumbnail */
|
||||
|
||||
TagLib::ID3v2::FrameList pictures = allTags["APIC"];
|
||||
if(!pictures.isEmpty()) {
|
||||
/* there can be multiple pictures, apparently. let's just use
|
||||
the first one. */
|
||||
|
||||
TagLib::ID3v2::AttachedPictureFrame *picture =
|
||||
static_cast<TagLib::ID3v2::AttachedPictureFrame*>(pictures.front());
|
||||
|
||||
TagLib::ByteVector pictureData = picture->picture();
|
||||
long long size = pictureData.size();
|
||||
|
||||
if(size > 32) { /* noticed that some id3tags have like a 4-8 byte size with no thumbnail */
|
||||
track->SetThumbnail(pictureData.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetTagValue(
|
||||
const char* key,
|
||||
const TagLib::String tagString,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
std::string value(tagString.to8Bit(true));
|
||||
track->SetValue(key, value.c_str());
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetTagValue(
|
||||
const char* key,
|
||||
const char* string,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
std::string temp(string);
|
||||
track->SetValue(key, temp.c_str());
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetTagValue(
|
||||
const char* key,
|
||||
const int tagInt,
|
||||
musik::core::IMetadataWriter *target)
|
||||
{
|
||||
std::string temp = boost::str(boost::format("%1%") % tagInt);
|
||||
target->SetValue(key, temp.c_str());
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetTagValues(
|
||||
const char* key,
|
||||
const TagLib::ID3v2::FrameList &frame,
|
||||
musik::core::IMetadataWriter *target)
|
||||
{
|
||||
if (!frame.isEmpty()) {
|
||||
TagLib::ID3v2::FrameList::ConstIterator value = frame.begin();
|
||||
|
||||
for ( ; value != frame.end(); ++value) {
|
||||
TagLib::String tagString = (*value)->toString();
|
||||
if(!tagString.isEmpty()) {
|
||||
std::string value(tagString.to8Bit(true));
|
||||
target->SetValue(key,value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetSlashSeparatedValues(
|
||||
const char* key,
|
||||
TagLib::String tagString,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
if(!tagString.isEmpty()) {
|
||||
std::string value(tagString.to8Bit(true));
|
||||
std::vector<std::string> splitValues;
|
||||
|
||||
boost::algorithm::split(splitValues, value, boost::algorithm::is_any_of("/"));
|
||||
|
||||
std::vector<std::string>::iterator it = splitValues.begin();
|
||||
for( ; it != splitValues.end(); ++it) {
|
||||
track->SetValue(key, it->c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetSlashSeparatedValues(
|
||||
const char* key,
|
||||
const TagLib::ID3v2::FrameList &frame,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
if(!frame.isEmpty()) {
|
||||
TagLib::ID3v2::FrameList::ConstIterator value = frame.begin();
|
||||
for ( ; value != frame.end(); ++value) {
|
||||
TagLib::String tagString = (*value)->toString();
|
||||
this->SetSlashSeparatedValues(key, tagString, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaglibMetadataReader::SetAudioProperties(
|
||||
TagLib::AudioProperties *audioProperties,
|
||||
musik::core::IMetadataWriter *track)
|
||||
{
|
||||
/* FIXME: it's overkill to bring boost in just to convert ints to strings */
|
||||
|
||||
if (audioProperties) {
|
||||
std::string duration = boost::str(boost::format("%1%") % audioProperties->length());
|
||||
this->SetTagValue("duration", duration, track);
|
||||
|
||||
int bitrate = audioProperties->bitrate();
|
||||
|
||||
if(bitrate) {
|
||||
std::string temp(boost::str(boost::format("%1%") % bitrate));
|
||||
this->SetTagValue("bitrate", temp, track);
|
||||
}
|
||||
|
||||
int channels = audioProperties->channels();
|
||||
|
||||
if(channels) {
|
||||
std::string temp(boost::str(boost::format("%1%") % channels));
|
||||
this->SetTagValue("channels", temp, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
@ -30,36 +30,36 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "TaglibMetadataReader.h"
|
||||
#include "core/sdk/IPlugin.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
class TaglibPlugin : public musik::core::IPlugin {
|
||||
public:
|
||||
virtual void Destroy() { delete this; }
|
||||
virtual const char* Name() { return "Taglib 1.11 IMetadataReader"; }
|
||||
virtual const char* Version() { return "0.2"; }
|
||||
virtual const char* Author() { return "Daniel Önnerby, clangen"; }
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::metadata::IMetadataReader* GetMetadataReader() {
|
||||
return new TaglibMetadataReader();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new TaglibPlugin();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "TaglibMetadataReader.h"
|
||||
#include "core/sdk/IPlugin.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
class TaglibPlugin : public musik::core::IPlugin {
|
||||
public:
|
||||
virtual void Destroy() { delete this; }
|
||||
virtual const char* Name() { return "Taglib 1.11 IMetadataReader"; }
|
||||
virtual const char* Version() { return "0.2"; }
|
||||
virtual const char* Author() { return "Daniel Önnerby, clangen"; }
|
||||
};
|
||||
|
||||
extern "C" DLLEXPORT musik::core::metadata::IMetadataReader* GetMetadataReader() {
|
||||
return new TaglibMetadataReader();
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT musik::core::IPlugin* GetPlugin() {
|
||||
return new TaglibPlugin();
|
||||
}
|
||||
|
@ -30,300 +30,300 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "WaveOut.h"
|
||||
|
||||
#define MAX_VOLUME 0xFFFF
|
||||
#define MAX_BUFFERS_PER_OUTPUT 8
|
||||
|
||||
static void notifyBufferProcessed(WaveOutBuffer *buffer) {
|
||||
/* let the provider know the output device is done with the buffer; the
|
||||
Player ensures buffers are locked down and not freed/reused until it
|
||||
gets confirmation it's been played (or the user stops playback) */
|
||||
IBufferProvider* provider = buffer->GetBufferProvider();
|
||||
provider->OnBufferProcessed(buffer->GetWrappedBuffer());
|
||||
}
|
||||
|
||||
WaveOut::WaveOut()
|
||||
: waveHandle(NULL)
|
||||
, currentVolume(1.0)
|
||||
, threadId(0)
|
||||
, threadHandle(NULL)
|
||||
, playing(false)
|
||||
{
|
||||
}
|
||||
|
||||
WaveOut::~WaveOut() {
|
||||
}
|
||||
|
||||
void WaveOut::Destroy() {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
|
||||
/* reset playback immediately. this will invalidate all pending
|
||||
buffers */
|
||||
if (this->waveHandle != NULL) {
|
||||
waveOutReset(this->waveHandle);
|
||||
}
|
||||
|
||||
/* stop the thread so nothing else is processed */
|
||||
this->StopWaveOutThread();
|
||||
|
||||
/* close it down after the threadproc has finished */
|
||||
if (this->waveHandle != NULL) {
|
||||
waveOutClose(this->waveHandle);
|
||||
this->waveHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
this->ClearBufferQueue();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void WaveOut::Pause() {
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
waveOutPause(this->waveHandle);
|
||||
this->playing = false;
|
||||
}
|
||||
|
||||
void WaveOut::Resume() {
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
|
||||
if (!this->playing) {
|
||||
waveOutRestart(this->waveHandle);
|
||||
}
|
||||
|
||||
this->playing = true;
|
||||
}
|
||||
|
||||
void WaveOut::SetVolume(double volume) {
|
||||
if (this->waveHandle) {
|
||||
DWORD newVolume = (DWORD) (volume * MAX_VOLUME);
|
||||
DWORD leftAndRight = (newVolume << 16) | newVolume;
|
||||
waveOutSetVolume(this->waveHandle, leftAndRight);
|
||||
}
|
||||
|
||||
this->currentVolume = volume;
|
||||
}
|
||||
|
||||
void WaveOut::Stop() {
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
|
||||
if (this->waveHandle != NULL) {
|
||||
waveOutReset(this->waveHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOut::ClearBufferQueue() {
|
||||
std::list<WaveOutBufferPtr> remove;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->bufferQueueMutex);
|
||||
std::swap(this->queuedBuffers, remove);
|
||||
}
|
||||
|
||||
/* notify and free any pending buffers, the Player in the core
|
||||
will be waiting for all pending buffers to be processed. */
|
||||
if (remove.size() > 0) {
|
||||
BufferList::iterator it = remove.begin();
|
||||
for (; it != remove.end(); it++) {
|
||||
notifyBufferProcessed((*it).get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOut::OnBufferWrittenToOutput(WaveOutBuffer *buffer) {
|
||||
WaveOutBufferPtr erased;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->bufferQueueMutex);
|
||||
|
||||
/* removed the buffer. it should be at the front of the queue. */
|
||||
BufferList::iterator it = this->queuedBuffers.begin();
|
||||
for (; it != this->queuedBuffers.end(); it++) {
|
||||
if (it->get() == buffer) {
|
||||
erased = *it;
|
||||
this->queuedBuffers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (erased) {
|
||||
notifyBufferProcessed(erased.get());
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOut::StartWaveOutThread() {
|
||||
this->StopWaveOutThread();
|
||||
|
||||
this->threadHandle = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
&WaveOut::WaveCallbackThreadProc,
|
||||
this,
|
||||
NULL,
|
||||
&this->threadId);
|
||||
}
|
||||
|
||||
void WaveOut::StopWaveOutThread() {
|
||||
if (this->threadHandle != NULL) {
|
||||
PostThreadMessage(this->threadId, WM_QUIT, 0, 0);
|
||||
WaitForSingleObject(this->threadHandle, INFINITE);
|
||||
this->threadHandle = NULL;
|
||||
this->threadId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool WaveOut::Play(IBuffer *buffer, IBufferProvider *provider) {
|
||||
boost::recursive_mutex::scoped_lock lock(this->bufferQueueMutex);
|
||||
|
||||
/* if we have a different format, return false and wait for the pending
|
||||
buffers to be written to the output device. */
|
||||
if (!this->queuedBuffers.empty()) {
|
||||
bool formatChanged =
|
||||
this->currentChannels != buffer->Channels() ||
|
||||
this->currentSampleRate != buffer->SampleRate();
|
||||
|
||||
if (formatChanged) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t buffersForOutput = 0;
|
||||
auto it = this->queuedBuffers.begin();
|
||||
while (it != this->queuedBuffers.end()) {
|
||||
if ((*it)->GetBufferProvider() == provider) {
|
||||
++buffersForOutput;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
if (MAX_BUFFERS_PER_OUTPUT > buffersForOutput) {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
|
||||
if (!this->playing) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ensure the output device itself (the WAVEOUT) is configured correctly
|
||||
for the new buffer */
|
||||
this->SetFormat(buffer);
|
||||
|
||||
/* add the raw buffer to a WaveOutBuffer; this will ensure a correct WAVEHDR
|
||||
is configured for the WAVEOUT device */
|
||||
WaveOutBufferPtr waveBuffer(new WaveOutBuffer(this, buffer, provider));
|
||||
|
||||
if (waveBuffer->WriteToOutput()) {
|
||||
this->queuedBuffers.push_back(waveBuffer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WaveOut::SetFormat(IBuffer *buffer) {
|
||||
if (this->currentChannels != buffer->Channels() ||
|
||||
this->currentSampleRate != buffer->SampleRate() ||
|
||||
this->waveHandle == NULL)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->outputDeviceMutex);
|
||||
|
||||
this->currentChannels = buffer->Channels();
|
||||
this->currentSampleRate = buffer->SampleRate();
|
||||
|
||||
this->Stop();
|
||||
this->StartWaveOutThread();
|
||||
|
||||
/* reset, and configure speaker output */
|
||||
ZeroMemory(&this->waveFormat, sizeof(this->waveFormat));
|
||||
|
||||
DWORD speakerConfig = 0;
|
||||
|
||||
switch (buffer->Channels()) {
|
||||
case 1:
|
||||
speakerConfig = KSAUDIO_SPEAKER_MONO;
|
||||
break;
|
||||
case 2:
|
||||
speakerConfig = KSAUDIO_SPEAKER_STEREO;
|
||||
break;
|
||||
case 4:
|
||||
speakerConfig = KSAUDIO_SPEAKER_QUAD;
|
||||
break;
|
||||
case 5:
|
||||
speakerConfig = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
|
||||
break;
|
||||
case 6:
|
||||
speakerConfig = KSAUDIO_SPEAKER_5POINT1;
|
||||
break;
|
||||
}
|
||||
|
||||
this->waveFormat.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
this->waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
this->waveFormat.Format.nChannels = (WORD) buffer->Channels();
|
||||
this->waveFormat.Format.wBitsPerSample = sizeof(float) * 8;
|
||||
this->waveFormat.Format.nSamplesPerSec = (DWORD) buffer->SampleRate();
|
||||
|
||||
int bytesPerSample = this->waveFormat.Format.wBitsPerSample / 8;
|
||||
this->waveFormat.Format.nBlockAlign = bytesPerSample * this->waveFormat.Format.nChannels;
|
||||
|
||||
this->waveFormat.Format.nAvgBytesPerSec =
|
||||
this->waveFormat.Format.nBlockAlign * this->waveFormat.Format.nSamplesPerSec;
|
||||
|
||||
/* NOTE: wValidBitsPerSample/wReserved/wSamplesPerBlock are a union */
|
||||
this->waveFormat.Samples.wValidBitsPerSample = this->waveFormat.Format.wBitsPerSample;
|
||||
this->waveFormat.dwChannelMask = speakerConfig;
|
||||
this->waveFormat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
|
||||
/* create the output device. note that we use a thread instead of a simple callback
|
||||
here. that's because processing a buffer after calling waveOutReset() can lead to
|
||||
crashes; so we can use a thread and ensure it's shut down before resetting the
|
||||
output device, making it impossible to reach this condition. */
|
||||
int openResult = waveOutOpen(
|
||||
&this->waveHandle,
|
||||
WAVE_MAPPER,
|
||||
(WAVEFORMATEX*) &this->waveFormat,
|
||||
this->threadId,
|
||||
(DWORD_PTR) this,
|
||||
CALLBACK_THREAD);
|
||||
|
||||
if (openResult != MMSYSERR_NOERROR) {
|
||||
throw;
|
||||
}
|
||||
|
||||
this->SetVolume(this->currentVolume);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI WaveOut::WaveCallbackThreadProc(LPVOID params) {
|
||||
WaveOut* waveOut = (WaveOut*) params;
|
||||
|
||||
MSG msg;
|
||||
|
||||
/* create message queue implicitly. */
|
||||
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
|
||||
|
||||
bool stop = false;
|
||||
while (!stop && GetMessage(&msg, NULL, 0, 0)) {
|
||||
switch (msg.message) {
|
||||
case WOM_DONE: {
|
||||
LPWAVEHDR waveoutHeader = (LPWAVEHDR) msg.lParam;
|
||||
WaveOutBuffer* buffer = (WaveOutBuffer*) waveoutHeader->dwUser;
|
||||
waveOut->OnBufferWrittenToOutput(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_QUIT: {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "WaveOut.h"
|
||||
|
||||
#define MAX_VOLUME 0xFFFF
|
||||
#define MAX_BUFFERS_PER_OUTPUT 8
|
||||
|
||||
static void notifyBufferProcessed(WaveOutBuffer *buffer) {
|
||||
/* let the provider know the output device is done with the buffer; the
|
||||
Player ensures buffers are locked down and not freed/reused until it
|
||||
gets confirmation it's been played (or the user stops playback) */
|
||||
IBufferProvider* provider = buffer->GetBufferProvider();
|
||||
provider->OnBufferProcessed(buffer->GetWrappedBuffer());
|
||||
}
|
||||
|
||||
WaveOut::WaveOut()
|
||||
: waveHandle(NULL)
|
||||
, currentVolume(1.0)
|
||||
, threadId(0)
|
||||
, threadHandle(NULL)
|
||||
, playing(false)
|
||||
{
|
||||
}
|
||||
|
||||
WaveOut::~WaveOut() {
|
||||
}
|
||||
|
||||
void WaveOut::Destroy() {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
|
||||
/* reset playback immediately. this will invalidate all pending
|
||||
buffers */
|
||||
if (this->waveHandle != NULL) {
|
||||
waveOutReset(this->waveHandle);
|
||||
}
|
||||
|
||||
/* stop the thread so nothing else is processed */
|
||||
this->StopWaveOutThread();
|
||||
|
||||
/* close it down after the threadproc has finished */
|
||||
if (this->waveHandle != NULL) {
|
||||
waveOutClose(this->waveHandle);
|
||||
this->waveHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
this->ClearBufferQueue();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void WaveOut::Pause() {
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
waveOutPause(this->waveHandle);
|
||||
this->playing = false;
|
||||
}
|
||||
|
||||
void WaveOut::Resume() {
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
|
||||
if (!this->playing) {
|
||||
waveOutRestart(this->waveHandle);
|
||||
}
|
||||
|
||||
this->playing = true;
|
||||
}
|
||||
|
||||
void WaveOut::SetVolume(double volume) {
|
||||
if (this->waveHandle) {
|
||||
DWORD newVolume = (DWORD) (volume * MAX_VOLUME);
|
||||
DWORD leftAndRight = (newVolume << 16) | newVolume;
|
||||
waveOutSetVolume(this->waveHandle, leftAndRight);
|
||||
}
|
||||
|
||||
this->currentVolume = volume;
|
||||
}
|
||||
|
||||
void WaveOut::Stop() {
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
|
||||
if (this->waveHandle != NULL) {
|
||||
waveOutReset(this->waveHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOut::ClearBufferQueue() {
|
||||
std::list<WaveOutBufferPtr> remove;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->bufferQueueMutex);
|
||||
std::swap(this->queuedBuffers, remove);
|
||||
}
|
||||
|
||||
/* notify and free any pending buffers, the Player in the core
|
||||
will be waiting for all pending buffers to be processed. */
|
||||
if (remove.size() > 0) {
|
||||
BufferList::iterator it = remove.begin();
|
||||
for (; it != remove.end(); it++) {
|
||||
notifyBufferProcessed((*it).get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOut::OnBufferWrittenToOutput(WaveOutBuffer *buffer) {
|
||||
WaveOutBufferPtr erased;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->bufferQueueMutex);
|
||||
|
||||
/* removed the buffer. it should be at the front of the queue. */
|
||||
BufferList::iterator it = this->queuedBuffers.begin();
|
||||
for (; it != this->queuedBuffers.end(); it++) {
|
||||
if (it->get() == buffer) {
|
||||
erased = *it;
|
||||
this->queuedBuffers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (erased) {
|
||||
notifyBufferProcessed(erased.get());
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOut::StartWaveOutThread() {
|
||||
this->StopWaveOutThread();
|
||||
|
||||
this->threadHandle = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
&WaveOut::WaveCallbackThreadProc,
|
||||
this,
|
||||
NULL,
|
||||
&this->threadId);
|
||||
}
|
||||
|
||||
void WaveOut::StopWaveOutThread() {
|
||||
if (this->threadHandle != NULL) {
|
||||
PostThreadMessage(this->threadId, WM_QUIT, 0, 0);
|
||||
WaitForSingleObject(this->threadHandle, INFINITE);
|
||||
this->threadHandle = NULL;
|
||||
this->threadId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool WaveOut::Play(IBuffer *buffer, IBufferProvider *provider) {
|
||||
boost::recursive_mutex::scoped_lock lock(this->bufferQueueMutex);
|
||||
|
||||
/* if we have a different format, return false and wait for the pending
|
||||
buffers to be written to the output device. */
|
||||
if (!this->queuedBuffers.empty()) {
|
||||
bool formatChanged =
|
||||
this->currentChannels != buffer->Channels() ||
|
||||
this->currentSampleRate != buffer->SampleRate();
|
||||
|
||||
if (formatChanged) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t buffersForOutput = 0;
|
||||
auto it = this->queuedBuffers.begin();
|
||||
while (it != this->queuedBuffers.end()) {
|
||||
if ((*it)->GetBufferProvider() == provider) {
|
||||
++buffersForOutput;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
if (MAX_BUFFERS_PER_OUTPUT > buffersForOutput) {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock2(this->outputDeviceMutex);
|
||||
|
||||
if (!this->playing) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ensure the output device itself (the WAVEOUT) is configured correctly
|
||||
for the new buffer */
|
||||
this->SetFormat(buffer);
|
||||
|
||||
/* add the raw buffer to a WaveOutBuffer; this will ensure a correct WAVEHDR
|
||||
is configured for the WAVEOUT device */
|
||||
WaveOutBufferPtr waveBuffer(new WaveOutBuffer(this, buffer, provider));
|
||||
|
||||
if (waveBuffer->WriteToOutput()) {
|
||||
this->queuedBuffers.push_back(waveBuffer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WaveOut::SetFormat(IBuffer *buffer) {
|
||||
if (this->currentChannels != buffer->Channels() ||
|
||||
this->currentSampleRate != buffer->SampleRate() ||
|
||||
this->waveHandle == NULL)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->outputDeviceMutex);
|
||||
|
||||
this->currentChannels = buffer->Channels();
|
||||
this->currentSampleRate = buffer->SampleRate();
|
||||
|
||||
this->Stop();
|
||||
this->StartWaveOutThread();
|
||||
|
||||
/* reset, and configure speaker output */
|
||||
ZeroMemory(&this->waveFormat, sizeof(this->waveFormat));
|
||||
|
||||
DWORD speakerConfig = 0;
|
||||
|
||||
switch (buffer->Channels()) {
|
||||
case 1:
|
||||
speakerConfig = KSAUDIO_SPEAKER_MONO;
|
||||
break;
|
||||
case 2:
|
||||
speakerConfig = KSAUDIO_SPEAKER_STEREO;
|
||||
break;
|
||||
case 4:
|
||||
speakerConfig = KSAUDIO_SPEAKER_QUAD;
|
||||
break;
|
||||
case 5:
|
||||
speakerConfig = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
|
||||
break;
|
||||
case 6:
|
||||
speakerConfig = KSAUDIO_SPEAKER_5POINT1;
|
||||
break;
|
||||
}
|
||||
|
||||
this->waveFormat.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
this->waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
this->waveFormat.Format.nChannels = (WORD) buffer->Channels();
|
||||
this->waveFormat.Format.wBitsPerSample = sizeof(float) * 8;
|
||||
this->waveFormat.Format.nSamplesPerSec = (DWORD) buffer->SampleRate();
|
||||
|
||||
int bytesPerSample = this->waveFormat.Format.wBitsPerSample / 8;
|
||||
this->waveFormat.Format.nBlockAlign = bytesPerSample * this->waveFormat.Format.nChannels;
|
||||
|
||||
this->waveFormat.Format.nAvgBytesPerSec =
|
||||
this->waveFormat.Format.nBlockAlign * this->waveFormat.Format.nSamplesPerSec;
|
||||
|
||||
/* NOTE: wValidBitsPerSample/wReserved/wSamplesPerBlock are a union */
|
||||
this->waveFormat.Samples.wValidBitsPerSample = this->waveFormat.Format.wBitsPerSample;
|
||||
this->waveFormat.dwChannelMask = speakerConfig;
|
||||
this->waveFormat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
|
||||
/* create the output device. note that we use a thread instead of a simple callback
|
||||
here. that's because processing a buffer after calling waveOutReset() can lead to
|
||||
crashes; so we can use a thread and ensure it's shut down before resetting the
|
||||
output device, making it impossible to reach this condition. */
|
||||
int openResult = waveOutOpen(
|
||||
&this->waveHandle,
|
||||
WAVE_MAPPER,
|
||||
(WAVEFORMATEX*) &this->waveFormat,
|
||||
this->threadId,
|
||||
(DWORD_PTR) this,
|
||||
CALLBACK_THREAD);
|
||||
|
||||
if (openResult != MMSYSERR_NOERROR) {
|
||||
throw;
|
||||
}
|
||||
|
||||
this->SetVolume(this->currentVolume);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI WaveOut::WaveCallbackThreadProc(LPVOID params) {
|
||||
WaveOut* waveOut = (WaveOut*) params;
|
||||
|
||||
MSG msg;
|
||||
|
||||
/* create message queue implicitly. */
|
||||
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
|
||||
|
||||
bool stop = false;
|
||||
while (!stop && GetMessage(&msg, NULL, 0, 0)) {
|
||||
switch (msg.message) {
|
||||
case WOM_DONE: {
|
||||
LPWAVEHDR waveoutHeader = (LPWAVEHDR) msg.lParam;
|
||||
WaveOutBuffer* buffer = (WaveOutBuffer*) waveoutHeader->dwUser;
|
||||
waveOut->OnBufferWrittenToOutput(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_QUIT: {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -30,68 +30,68 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "WaveOutBuffer.h"
|
||||
#include "WaveOut.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
WaveOutBuffer::WaveOutBuffer(WaveOut *waveOut, IBuffer *buffer, IBufferProvider *provider)
|
||||
: waveOut(waveOut)
|
||||
, buffer(buffer)
|
||||
, provider(provider)
|
||||
, destroyed(false)
|
||||
{
|
||||
this->Initialize();
|
||||
}
|
||||
|
||||
void WaveOutBuffer::Initialize() {
|
||||
this->header.dwBufferLength = this->buffer->Samples() * this->buffer->Channels() * sizeof(float);
|
||||
this->header.lpData = (LPSTR) this->buffer->BufferPointer();
|
||||
this->header.dwUser = (DWORD_PTR) this;
|
||||
this->header.dwBytesRecorded = 0;
|
||||
this->header.dwFlags = 0;
|
||||
this->header.dwLoops = 0;
|
||||
this->header.lpNext = NULL;
|
||||
this->header.reserved = NULL;
|
||||
|
||||
MMRESULT result = waveOutPrepareHeader(this->waveOut->waveHandle, &this->header, sizeof(WAVEHDR));
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOutBuffer::Destroy() {
|
||||
if (!this->destroyed) {
|
||||
if (this->waveOut->waveHandle && this->header.dwFlags & WHDR_PREPARED) {
|
||||
waveOutUnprepareHeader(this->waveOut->waveHandle, &this->header, sizeof(WAVEHDR));
|
||||
this->header.dwFlags = WHDR_DONE;
|
||||
}
|
||||
|
||||
this->destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
IBufferProvider* WaveOutBuffer::GetBufferProvider() const {
|
||||
return this->provider;
|
||||
}
|
||||
|
||||
IBuffer* WaveOutBuffer::GetWrappedBuffer() const {
|
||||
return this->buffer;
|
||||
}
|
||||
|
||||
WaveOutBuffer::~WaveOutBuffer() {
|
||||
this->Destroy();
|
||||
}
|
||||
|
||||
bool WaveOutBuffer::WriteToOutput() {
|
||||
MMRESULT result = waveOutWrite(this->waveOut->waveHandle, &this->header, sizeof(WAVEHDR));
|
||||
|
||||
if (result == MMSYSERR_NOERROR) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "WaveOutBuffer.h"
|
||||
#include "WaveOut.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
WaveOutBuffer::WaveOutBuffer(WaveOut *waveOut, IBuffer *buffer, IBufferProvider *provider)
|
||||
: waveOut(waveOut)
|
||||
, buffer(buffer)
|
||||
, provider(provider)
|
||||
, destroyed(false)
|
||||
{
|
||||
this->Initialize();
|
||||
}
|
||||
|
||||
void WaveOutBuffer::Initialize() {
|
||||
this->header.dwBufferLength = this->buffer->Samples() * this->buffer->Channels() * sizeof(float);
|
||||
this->header.lpData = (LPSTR) this->buffer->BufferPointer();
|
||||
this->header.dwUser = (DWORD_PTR) this;
|
||||
this->header.dwBytesRecorded = 0;
|
||||
this->header.dwFlags = 0;
|
||||
this->header.dwLoops = 0;
|
||||
this->header.lpNext = NULL;
|
||||
this->header.reserved = NULL;
|
||||
|
||||
MMRESULT result = waveOutPrepareHeader(this->waveOut->waveHandle, &this->header, sizeof(WAVEHDR));
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOutBuffer::Destroy() {
|
||||
if (!this->destroyed) {
|
||||
if (this->waveOut->waveHandle && this->header.dwFlags & WHDR_PREPARED) {
|
||||
waveOutUnprepareHeader(this->waveOut->waveHandle, &this->header, sizeof(WAVEHDR));
|
||||
this->header.dwFlags = WHDR_DONE;
|
||||
}
|
||||
|
||||
this->destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
IBufferProvider* WaveOutBuffer::GetBufferProvider() const {
|
||||
return this->provider;
|
||||
}
|
||||
|
||||
IBuffer* WaveOutBuffer::GetWrappedBuffer() const {
|
||||
return this->buffer;
|
||||
}
|
||||
|
||||
WaveOutBuffer::~WaveOutBuffer() {
|
||||
this->Destroy();
|
||||
}
|
||||
|
||||
bool WaveOutBuffer::WriteToOutput() {
|
||||
MMRESULT result = waveOutWrite(this->waveOut->waveHandle, &this->header, sizeof(WAVEHDR));
|
||||
|
||||
if (result == MMSYSERR_NOERROR) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
@ -30,28 +30,28 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include "WaveOut.h"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
|
||||
class WaveOutPlugin : public musik::core::IPlugin {
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "WaveOut IOutput"; };
|
||||
const char* Version() { return "0.2"; };
|
||||
const char* Author() { return "Björn Olievier, clangen"; };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin() {
|
||||
return new WaveOutPlugin();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::audio::IOutput* GetAudioOutput() {
|
||||
return new WaveOut();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include "WaveOut.h"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
return true;
|
||||
}
|
||||
|
||||
class WaveOutPlugin : public musik::core::IPlugin {
|
||||
void Destroy() { delete this; };
|
||||
const char* Name() { return "WaveOut IOutput"; };
|
||||
const char* Version() { return "0.2"; };
|
||||
const char* Author() { return "Björn Olievier, clangen"; };
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin() {
|
||||
return new WaveOutPlugin();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) musik::core::audio::IOutput* GetAudioOutput() {
|
||||
return new WaveOut();
|
||||
}
|
||||
|
@ -1,324 +1,324 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/audio/Player.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <algorithm>
|
||||
|
||||
#define MAX_PREBUFFER_QUEUE_COUNT 8
|
||||
|
||||
using namespace musik::core::audio;
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
static std::string TAG = "Player";
|
||||
|
||||
PlayerPtr Player::Create(const std::string &url, OutputPtr output) {
|
||||
return PlayerPtr(new Player(url, output));
|
||||
}
|
||||
|
||||
OutputPtr Player::CreateDefaultOutput() {
|
||||
/* if no output is specified, find all output plugins, and select the first one. */
|
||||
typedef std::vector<OutputPtr> OutputVector;
|
||||
|
||||
OutputVector outputs = musik::core::PluginFactory::Instance().QueryInterface<
|
||||
IOutput, musik::core::PluginFactory::DestroyDeleter<IOutput> >("GetAudioOutput");
|
||||
|
||||
if (!outputs.empty()) {
|
||||
musik::debug::info(TAG, "found an IOutput device!");
|
||||
return outputs.front();
|
||||
}
|
||||
|
||||
return OutputPtr();
|
||||
}
|
||||
|
||||
Player::Player(const std::string &url, OutputPtr output)
|
||||
: state(Player::Precache)
|
||||
, url(url)
|
||||
, currentPosition(0)
|
||||
, output(output)
|
||||
, notifiedStarted(false)
|
||||
, setPosition(-1) {
|
||||
musik::debug::info(TAG, "new instance created");
|
||||
|
||||
/* we allow callers to specify an output device; but if they don't,
|
||||
we will create and manage our own. */
|
||||
if (!this->output) {
|
||||
throw std::runtime_error("output cannot be null!");
|
||||
}
|
||||
|
||||
/* each player instance is driven by a background thread. start it. */
|
||||
this->thread.reset(new boost::thread(boost::bind(&Player::ThreadLoop, this)));
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->state = Player::Quit;
|
||||
this->prebufferQueue.clear();
|
||||
this->writeToOutputCondition.notify_all();
|
||||
}
|
||||
|
||||
this->thread->join();
|
||||
}
|
||||
|
||||
void Player::Play() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->state = Player::Playing;
|
||||
this->writeToOutputCondition.notify_all();
|
||||
}
|
||||
|
||||
void Player::Stop() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->state = Player::Quit;
|
||||
this->writeToOutputCondition.notify_all();
|
||||
}
|
||||
|
||||
double Player::Position() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
return this->currentPosition;
|
||||
}
|
||||
|
||||
void Player::SetPosition(double seconds) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->setPosition = std::max(0.0, seconds);
|
||||
}
|
||||
|
||||
int Player::State() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void Player::ThreadLoop() {
|
||||
/* create and open the stream */
|
||||
this->stream = Stream::Create();
|
||||
if (this->stream->OpenStream(this->url)) {
|
||||
/* precache until buffers are full */
|
||||
bool keepPrecaching = true;
|
||||
while (this->State() == Precache && keepPrecaching) {
|
||||
keepPrecaching = this->PreBuffer();
|
||||
}
|
||||
|
||||
/* wait until we enter the Playing or Quit state; we may still
|
||||
be in the Precache state. */
|
||||
while (this->state == Precache) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->writeToOutputCondition.wait(lock);
|
||||
}
|
||||
|
||||
/* we're ready to go.... */
|
||||
bool finished = false;
|
||||
BufferPtr buffer;
|
||||
|
||||
while (!finished && !this->Exited()) {
|
||||
/* see if we've been asked to seek since the last sample was
|
||||
played. if we have, clear our output buffer and seek the
|
||||
stream. */
|
||||
if (this->setPosition != -1) {
|
||||
this->output->Stop(); /* flush all buffers */
|
||||
this->output->Resume(); /* start it back up */
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
while (this->lockedBuffers.size() > 0) {
|
||||
writeToOutputCondition.wait(this->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
this->stream->SetPosition(this->setPosition);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->setPosition = -1;
|
||||
this->prebufferQueue.clear();
|
||||
}
|
||||
|
||||
buffer.reset();
|
||||
}
|
||||
|
||||
/* let's see if we can find some samples to play */
|
||||
if (!buffer) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
/* the buffer queue may already have some available if it was prefetched. */
|
||||
if (!this->prebufferQueue.empty()) {
|
||||
buffer = this->prebufferQueue.front();
|
||||
this->prebufferQueue.pop_front();
|
||||
}
|
||||
/* otherwise, we need to grab a buffer from the stream and add it to the queue */
|
||||
else {
|
||||
buffer = this->stream->GetNextProcessedOutputBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have a decoded, processed buffer available. let's try to send it to
|
||||
the output device. */
|
||||
if (buffer) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
if (this->output->Play(buffer.get(), this)) {
|
||||
/* success! the buffer was accepted by the output.*/
|
||||
/* lock it down so it's not destroyed until the output device lets us
|
||||
know it's done with it. */
|
||||
this->lockedBuffers.push_back(buffer);
|
||||
|
||||
if (this->lockedBuffers.size() == 1) {
|
||||
this->currentPosition = buffer->Position();
|
||||
}
|
||||
|
||||
buffer.reset(); /* important! we're done with this one locally. */
|
||||
}
|
||||
else {
|
||||
/* the output device queue is full. we should block and wait until
|
||||
the output lets us know that it needs more data */
|
||||
writeToOutputCondition.wait(this->mutex);
|
||||
}
|
||||
}
|
||||
/* if we're unable to obtain a buffer, it means we're out of data and the
|
||||
player is finished. terminate the thread. */
|
||||
else {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* if the Quit flag isn't set, that means the stream has ended "naturally", i.e.
|
||||
it wasn't stopped by the user. raise the "almost ended" flag. */
|
||||
if (!this->Exited()) {
|
||||
this->PlaybackAlmostEnded(this);
|
||||
}
|
||||
}
|
||||
|
||||
/* if the stream failed to open... */
|
||||
else {
|
||||
this->PlaybackError(this);
|
||||
}
|
||||
|
||||
this->state = Player::Quit;
|
||||
|
||||
/* wait until all remaining buffers have been written, set final state... */
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
while (this->lockedBuffers.size() > 0) {
|
||||
writeToOutputCondition.wait(this->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
this->PlaybackFinished(this);
|
||||
}
|
||||
|
||||
void Player::ReleaseAllBuffers() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->lockedBuffers.empty();
|
||||
}
|
||||
|
||||
bool Player::PreBuffer() {
|
||||
/* don't prebuffer if the buffer is already full */
|
||||
if (this->prebufferQueue.size() < MAX_PREBUFFER_QUEUE_COUNT) {
|
||||
BufferPtr newBuffer = this->stream->GetNextProcessedOutputBuffer();
|
||||
|
||||
if (newBuffer) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->prebufferQueue.push_back(newBuffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Player::Exited() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
return (this->state == Player::Quit);
|
||||
}
|
||||
|
||||
void Player::OnBufferProcessed(IBuffer *buffer) {
|
||||
bool started = false;
|
||||
bool found = false;
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
/* removes the specified buffer from the list of locked buffers, and also
|
||||
lets the stream know it can be recycled. */
|
||||
BufferList::iterator it = this->lockedBuffers.begin();
|
||||
while (it != this->lockedBuffers.end() && !found) {
|
||||
if (it->get() == buffer) {
|
||||
found = true;
|
||||
|
||||
if (this->stream) {
|
||||
this->stream->OnBufferProcessedByPlayer(*it);
|
||||
}
|
||||
|
||||
it = this->lockedBuffers.erase(it);
|
||||
|
||||
/* this sets the current time in the stream. it does this by grabbing
|
||||
the time at the next buffer in the queue */
|
||||
if (!this->lockedBuffers.empty()) {
|
||||
this->currentPosition = this->lockedBuffers.front()->Position();
|
||||
}
|
||||
else {
|
||||
/* if the queue is drained, use the position from the buffer
|
||||
that was just processed */
|
||||
this->currentPosition = ((Buffer*) buffer)->Position();
|
||||
}
|
||||
|
||||
/* if the output device's internal buffers are full, it will stop
|
||||
accepting new samples. now that a buffer has been processed, we can
|
||||
try to enqueue another sample. the thread loop blocks on this condition */
|
||||
this->writeToOutputCondition.notify_all();
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->notifiedStarted) {
|
||||
this->notifiedStarted = true;
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* we notify our listeners that we've started playing only after the first
|
||||
buffer has been consumed. this is because sometimes we precache buffers
|
||||
and send them to the output before they are actually processed by the
|
||||
output device */
|
||||
if (started) {
|
||||
this->PlaybackStarted(this);
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/audio/Player.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <algorithm>
|
||||
|
||||
#define MAX_PREBUFFER_QUEUE_COUNT 8
|
||||
|
||||
using namespace musik::core::audio;
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
static std::string TAG = "Player";
|
||||
|
||||
PlayerPtr Player::Create(const std::string &url, OutputPtr output) {
|
||||
return PlayerPtr(new Player(url, output));
|
||||
}
|
||||
|
||||
OutputPtr Player::CreateDefaultOutput() {
|
||||
/* if no output is specified, find all output plugins, and select the first one. */
|
||||
typedef std::vector<OutputPtr> OutputVector;
|
||||
|
||||
OutputVector outputs = musik::core::PluginFactory::Instance().QueryInterface<
|
||||
IOutput, musik::core::PluginFactory::DestroyDeleter<IOutput> >("GetAudioOutput");
|
||||
|
||||
if (!outputs.empty()) {
|
||||
musik::debug::info(TAG, "found an IOutput device!");
|
||||
return outputs.front();
|
||||
}
|
||||
|
||||
return OutputPtr();
|
||||
}
|
||||
|
||||
Player::Player(const std::string &url, OutputPtr output)
|
||||
: state(Player::Precache)
|
||||
, url(url)
|
||||
, currentPosition(0)
|
||||
, output(output)
|
||||
, notifiedStarted(false)
|
||||
, setPosition(-1) {
|
||||
musik::debug::info(TAG, "new instance created");
|
||||
|
||||
/* we allow callers to specify an output device; but if they don't,
|
||||
we will create and manage our own. */
|
||||
if (!this->output) {
|
||||
throw std::runtime_error("output cannot be null!");
|
||||
}
|
||||
|
||||
/* each player instance is driven by a background thread. start it. */
|
||||
this->thread.reset(new boost::thread(boost::bind(&Player::ThreadLoop, this)));
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->state = Player::Quit;
|
||||
this->prebufferQueue.clear();
|
||||
this->writeToOutputCondition.notify_all();
|
||||
}
|
||||
|
||||
this->thread->join();
|
||||
}
|
||||
|
||||
void Player::Play() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->state = Player::Playing;
|
||||
this->writeToOutputCondition.notify_all();
|
||||
}
|
||||
|
||||
void Player::Stop() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->state = Player::Quit;
|
||||
this->writeToOutputCondition.notify_all();
|
||||
}
|
||||
|
||||
double Player::Position() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
return this->currentPosition;
|
||||
}
|
||||
|
||||
void Player::SetPosition(double seconds) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->setPosition = std::max(0.0, seconds);
|
||||
}
|
||||
|
||||
int Player::State() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void Player::ThreadLoop() {
|
||||
/* create and open the stream */
|
||||
this->stream = Stream::Create();
|
||||
if (this->stream->OpenStream(this->url)) {
|
||||
/* precache until buffers are full */
|
||||
bool keepPrecaching = true;
|
||||
while (this->State() == Precache && keepPrecaching) {
|
||||
keepPrecaching = this->PreBuffer();
|
||||
}
|
||||
|
||||
/* wait until we enter the Playing or Quit state; we may still
|
||||
be in the Precache state. */
|
||||
while (this->state == Precache) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->writeToOutputCondition.wait(lock);
|
||||
}
|
||||
|
||||
/* we're ready to go.... */
|
||||
bool finished = false;
|
||||
BufferPtr buffer;
|
||||
|
||||
while (!finished && !this->Exited()) {
|
||||
/* see if we've been asked to seek since the last sample was
|
||||
played. if we have, clear our output buffer and seek the
|
||||
stream. */
|
||||
if (this->setPosition != -1) {
|
||||
this->output->Stop(); /* flush all buffers */
|
||||
this->output->Resume(); /* start it back up */
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
while (this->lockedBuffers.size() > 0) {
|
||||
writeToOutputCondition.wait(this->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
this->stream->SetPosition(this->setPosition);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->setPosition = -1;
|
||||
this->prebufferQueue.clear();
|
||||
}
|
||||
|
||||
buffer.reset();
|
||||
}
|
||||
|
||||
/* let's see if we can find some samples to play */
|
||||
if (!buffer) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
/* the buffer queue may already have some available if it was prefetched. */
|
||||
if (!this->prebufferQueue.empty()) {
|
||||
buffer = this->prebufferQueue.front();
|
||||
this->prebufferQueue.pop_front();
|
||||
}
|
||||
/* otherwise, we need to grab a buffer from the stream and add it to the queue */
|
||||
else {
|
||||
buffer = this->stream->GetNextProcessedOutputBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have a decoded, processed buffer available. let's try to send it to
|
||||
the output device. */
|
||||
if (buffer) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
if (this->output->Play(buffer.get(), this)) {
|
||||
/* success! the buffer was accepted by the output.*/
|
||||
/* lock it down so it's not destroyed until the output device lets us
|
||||
know it's done with it. */
|
||||
this->lockedBuffers.push_back(buffer);
|
||||
|
||||
if (this->lockedBuffers.size() == 1) {
|
||||
this->currentPosition = buffer->Position();
|
||||
}
|
||||
|
||||
buffer.reset(); /* important! we're done with this one locally. */
|
||||
}
|
||||
else {
|
||||
/* the output device queue is full. we should block and wait until
|
||||
the output lets us know that it needs more data */
|
||||
writeToOutputCondition.wait(this->mutex);
|
||||
}
|
||||
}
|
||||
/* if we're unable to obtain a buffer, it means we're out of data and the
|
||||
player is finished. terminate the thread. */
|
||||
else {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* if the Quit flag isn't set, that means the stream has ended "naturally", i.e.
|
||||
it wasn't stopped by the user. raise the "almost ended" flag. */
|
||||
if (!this->Exited()) {
|
||||
this->PlaybackAlmostEnded(this);
|
||||
}
|
||||
}
|
||||
|
||||
/* if the stream failed to open... */
|
||||
else {
|
||||
this->PlaybackError(this);
|
||||
}
|
||||
|
||||
this->state = Player::Quit;
|
||||
|
||||
/* wait until all remaining buffers have been written, set final state... */
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
while (this->lockedBuffers.size() > 0) {
|
||||
writeToOutputCondition.wait(this->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
this->PlaybackFinished(this);
|
||||
}
|
||||
|
||||
void Player::ReleaseAllBuffers() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->lockedBuffers.empty();
|
||||
}
|
||||
|
||||
bool Player::PreBuffer() {
|
||||
/* don't prebuffer if the buffer is already full */
|
||||
if (this->prebufferQueue.size() < MAX_PREBUFFER_QUEUE_COUNT) {
|
||||
BufferPtr newBuffer = this->stream->GetNextProcessedOutputBuffer();
|
||||
|
||||
if (newBuffer) {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->prebufferQueue.push_back(newBuffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Player::Exited() {
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
return (this->state == Player::Quit);
|
||||
}
|
||||
|
||||
void Player::OnBufferProcessed(IBuffer *buffer) {
|
||||
bool started = false;
|
||||
bool found = false;
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
/* removes the specified buffer from the list of locked buffers, and also
|
||||
lets the stream know it can be recycled. */
|
||||
BufferList::iterator it = this->lockedBuffers.begin();
|
||||
while (it != this->lockedBuffers.end() && !found) {
|
||||
if (it->get() == buffer) {
|
||||
found = true;
|
||||
|
||||
if (this->stream) {
|
||||
this->stream->OnBufferProcessedByPlayer(*it);
|
||||
}
|
||||
|
||||
it = this->lockedBuffers.erase(it);
|
||||
|
||||
/* this sets the current time in the stream. it does this by grabbing
|
||||
the time at the next buffer in the queue */
|
||||
if (!this->lockedBuffers.empty()) {
|
||||
this->currentPosition = this->lockedBuffers.front()->Position();
|
||||
}
|
||||
else {
|
||||
/* if the queue is drained, use the position from the buffer
|
||||
that was just processed */
|
||||
this->currentPosition = ((Buffer*) buffer)->Position();
|
||||
}
|
||||
|
||||
/* if the output device's internal buffers are full, it will stop
|
||||
accepting new samples. now that a buffer has been processed, we can
|
||||
try to enqueue another sample. the thread loop blocks on this condition */
|
||||
this->writeToOutputCondition.notify_all();
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->notifiedStarted) {
|
||||
this->notifiedStarted = true;
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* we notify our listeners that we've started playing only after the first
|
||||
buffer has been consumed. this is because sometimes we precache buffers
|
||||
and send them to the output before they are actually processed by the
|
||||
output device */
|
||||
if (started) {
|
||||
this->PlaybackStarted(this);
|
||||
}
|
||||
}
|
||||
|
@ -1,233 +1,233 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/audio/Stream.h>
|
||||
#include <core/sdk/IDecoderFactory.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
|
||||
using namespace musik::core::audio;
|
||||
using musik::core::PluginFactory;
|
||||
|
||||
static std::string TAG = "Stream";
|
||||
|
||||
Stream::Stream(unsigned int options)
|
||||
: preferedBufferSampleSize(4096)
|
||||
, options(options)
|
||||
, decoderSampleRate(0)
|
||||
, decoderSamplePosition(0)
|
||||
{
|
||||
if ((this->options & NoDSP) == 0) {
|
||||
typedef PluginFactory::DestroyDeleter<IDSP> Deleter;
|
||||
this->dsps = PluginFactory::Instance().QueryInterface<IDSP, Deleter>("GetDSP");
|
||||
}
|
||||
|
||||
this->LoadDecoderPlugins();
|
||||
}
|
||||
|
||||
Stream::~Stream() {
|
||||
}
|
||||
|
||||
StreamPtr Stream::Create(unsigned int options) {
|
||||
return StreamPtr(new Stream(options));
|
||||
}
|
||||
|
||||
double Stream::SetPosition(double requestedSeconds) {
|
||||
double actualSeconds = this->decoder->SetPosition(requestedSeconds);
|
||||
|
||||
if (actualSeconds != -1) {
|
||||
double rate = (double) this->decoderSampleRate;
|
||||
this->decoderSamplePosition = (uint64)(actualSeconds * rate);
|
||||
}
|
||||
|
||||
return actualSeconds;
|
||||
}
|
||||
|
||||
bool Stream::OpenStream(std::string uri) {
|
||||
musik::debug::info(TAG, "opening " + uri);
|
||||
|
||||
/* use our file stream abstraction to open the data at the
|
||||
specified URI */
|
||||
this->dataStream = musik::core::io::DataStreamFactory::OpenUri(uri.c_str());
|
||||
|
||||
if (!this->dataStream) {
|
||||
musik::debug::err(TAG, "failed to open " + uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* find a DecoderFactory we can use for this type of data*/
|
||||
DecoderFactoryList::iterator factories = this->decoderFactories.begin();
|
||||
DecoderFactoryList::iterator end = this->decoderFactories.end();
|
||||
DecoderFactoryPtr decoderFactory;
|
||||
|
||||
for ( ; factories != end && !decoderFactory; ++factories) {
|
||||
if ((*factories)->CanHandle(this->dataStream->Type())) {
|
||||
decoderFactory = (*factories);
|
||||
}
|
||||
}
|
||||
|
||||
if (!decoderFactory) {
|
||||
/* nothing can decode this type of file */
|
||||
musik::debug::err(TAG, "nothing could open " + uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
IDecoder *decoder = decoderFactory->CreateDecoder();
|
||||
if (!decoder) {
|
||||
/* shouldn't ever happen, the factory said it can handle this file */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ask the decoder to open the data stream. if it returns true we're
|
||||
good to start pulling data out of it! */
|
||||
typedef PluginFactory::DestroyDeleter<IDecoder> Deleter;
|
||||
|
||||
this->decoder.reset(decoder, Deleter());
|
||||
if (!this->decoder->Open(this->dataStream.get())) {
|
||||
musik::debug::err(TAG, "open ok, but decode failed " + uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
musik::debug::info(TAG, "about ready to play: " + uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Stream::OnBufferProcessedByPlayer(BufferPtr buffer) {
|
||||
this->RecycleBuffer(buffer);
|
||||
}
|
||||
|
||||
BufferPtr Stream::GetNextBufferFromDecoder() {
|
||||
/* get a spare buffer, then ask the decoder for some data */
|
||||
BufferPtr buffer = this->GetEmptyBuffer();
|
||||
if (!this->decoder->GetBuffer(buffer.get())) {
|
||||
return BufferPtr(); /* return NULL */
|
||||
}
|
||||
|
||||
/* remember the sample rate so we can calculate the current time-position */
|
||||
if (!this->decoderSampleRate) {
|
||||
this->decoderSampleRate = buffer->SampleRate();
|
||||
}
|
||||
|
||||
/* offset, in samples */
|
||||
this->decoderSamplePosition += buffer->Samples();
|
||||
|
||||
/* calculate the position (seconds) in the buffer */
|
||||
buffer->SetPosition(
|
||||
((double) this->decoderSamplePosition) /
|
||||
((double) this->decoderSampleRate));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
BufferPtr Stream::GetNextProcessedOutputBuffer() {
|
||||
/* ask the decoder for the next buffer */
|
||||
BufferPtr currentBuffer = this->GetNextBufferFromDecoder();
|
||||
|
||||
if(currentBuffer) {
|
||||
/* try to fill the buffer to its optimal size; if the decoder didn't return
|
||||
a full buffer, ask it for some more data. */
|
||||
bool moreBuffers = true;
|
||||
while (currentBuffer->Samples() < this->preferedBufferSampleSize && moreBuffers) {
|
||||
BufferPtr bufferToAppend = this->GetNextBufferFromDecoder();
|
||||
if (bufferToAppend) {
|
||||
currentBuffer->Append(bufferToAppend);
|
||||
this->RecycleBuffer(bufferToAppend);
|
||||
}
|
||||
else {
|
||||
moreBuffers = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* let DSP plugins process the buffer */
|
||||
if (this->dsps.size() > 0) {
|
||||
BufferPtr oldBuffer = this->GetEmptyBuffer();
|
||||
|
||||
for (Dsps::iterator dsp = this->dsps.begin(); dsp != this->dsps.end(); ++dsp) {
|
||||
oldBuffer->CopyFormat(currentBuffer);
|
||||
currentBuffer->SetPosition(oldBuffer->Position());
|
||||
|
||||
if ((*dsp)->Process(currentBuffer.get(), oldBuffer.get())) {
|
||||
currentBuffer.swap(oldBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
this->RecycleBuffer(oldBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
return currentBuffer;
|
||||
}
|
||||
|
||||
/* returns a previously used buffer, if one is available. otherwise, a
|
||||
new one will be allocated. */
|
||||
BufferPtr Stream::GetEmptyBuffer() {
|
||||
BufferPtr buffer;
|
||||
if (!this->recycledBuffers.empty()) {
|
||||
buffer = this->recycledBuffers.front();
|
||||
this->recycledBuffers.pop_front();
|
||||
}
|
||||
else {
|
||||
buffer = Buffer::Create();
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* marks a used buffer as recycled so it can be re-used later. */
|
||||
void Stream::RecycleBuffer(BufferPtr oldBuffer) {
|
||||
this->recycledBuffers.push_back(oldBuffer);
|
||||
}
|
||||
|
||||
double Stream::DecoderProgress() {
|
||||
if (this->dataStream) {
|
||||
long fileSize = this->dataStream->Length();
|
||||
long filePosition = this->dataStream->Position();
|
||||
|
||||
if (fileSize && filePosition) {
|
||||
return ((double) filePosition) / ((double) fileSize);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Stream::LoadDecoderPlugins() {
|
||||
PluginFactory::DestroyDeleter<IDecoderFactory> typedef Deleter;
|
||||
|
||||
this->decoderFactories = PluginFactory::Instance()
|
||||
.QueryInterface<IDecoderFactory, Deleter>("GetDecoderFactory");
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2016 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/audio/Stream.h>
|
||||
#include <core/sdk/IDecoderFactory.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
|
||||
using namespace musik::core::audio;
|
||||
using musik::core::PluginFactory;
|
||||
|
||||
static std::string TAG = "Stream";
|
||||
|
||||
Stream::Stream(unsigned int options)
|
||||
: preferedBufferSampleSize(4096)
|
||||
, options(options)
|
||||
, decoderSampleRate(0)
|
||||
, decoderSamplePosition(0)
|
||||
{
|
||||
if ((this->options & NoDSP) == 0) {
|
||||
typedef PluginFactory::DestroyDeleter<IDSP> Deleter;
|
||||
this->dsps = PluginFactory::Instance().QueryInterface<IDSP, Deleter>("GetDSP");
|
||||
}
|
||||
|
||||
this->LoadDecoderPlugins();
|
||||
}
|
||||
|
||||
Stream::~Stream() {
|
||||
}
|
||||
|
||||
StreamPtr Stream::Create(unsigned int options) {
|
||||
return StreamPtr(new Stream(options));
|
||||
}
|
||||
|
||||
double Stream::SetPosition(double requestedSeconds) {
|
||||
double actualSeconds = this->decoder->SetPosition(requestedSeconds);
|
||||
|
||||
if (actualSeconds != -1) {
|
||||
double rate = (double) this->decoderSampleRate;
|
||||
this->decoderSamplePosition = (uint64)(actualSeconds * rate);
|
||||
}
|
||||
|
||||
return actualSeconds;
|
||||
}
|
||||
|
||||
bool Stream::OpenStream(std::string uri) {
|
||||
musik::debug::info(TAG, "opening " + uri);
|
||||
|
||||
/* use our file stream abstraction to open the data at the
|
||||
specified URI */
|
||||
this->dataStream = musik::core::io::DataStreamFactory::OpenUri(uri.c_str());
|
||||
|
||||
if (!this->dataStream) {
|
||||
musik::debug::err(TAG, "failed to open " + uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* find a DecoderFactory we can use for this type of data*/
|
||||
DecoderFactoryList::iterator factories = this->decoderFactories.begin();
|
||||
DecoderFactoryList::iterator end = this->decoderFactories.end();
|
||||
DecoderFactoryPtr decoderFactory;
|
||||
|
||||
for ( ; factories != end && !decoderFactory; ++factories) {
|
||||
if ((*factories)->CanHandle(this->dataStream->Type())) {
|
||||
decoderFactory = (*factories);
|
||||
}
|
||||
}
|
||||
|
||||
if (!decoderFactory) {
|
||||
/* nothing can decode this type of file */
|
||||
musik::debug::err(TAG, "nothing could open " + uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
IDecoder *decoder = decoderFactory->CreateDecoder();
|
||||
if (!decoder) {
|
||||
/* shouldn't ever happen, the factory said it can handle this file */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ask the decoder to open the data stream. if it returns true we're
|
||||
good to start pulling data out of it! */
|
||||
typedef PluginFactory::DestroyDeleter<IDecoder> Deleter;
|
||||
|
||||
this->decoder.reset(decoder, Deleter());
|
||||
if (!this->decoder->Open(this->dataStream.get())) {
|
||||
musik::debug::err(TAG, "open ok, but decode failed " + uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
musik::debug::info(TAG, "about ready to play: " + uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Stream::OnBufferProcessedByPlayer(BufferPtr buffer) {
|
||||
this->RecycleBuffer(buffer);
|
||||
}
|
||||
|
||||
BufferPtr Stream::GetNextBufferFromDecoder() {
|
||||
/* get a spare buffer, then ask the decoder for some data */
|
||||
BufferPtr buffer = this->GetEmptyBuffer();
|
||||
if (!this->decoder->GetBuffer(buffer.get())) {
|
||||
return BufferPtr(); /* return NULL */
|
||||
}
|
||||
|
||||
/* remember the sample rate so we can calculate the current time-position */
|
||||
if (!this->decoderSampleRate) {
|
||||
this->decoderSampleRate = buffer->SampleRate();
|
||||
}
|
||||
|
||||
/* offset, in samples */
|
||||
this->decoderSamplePosition += buffer->Samples();
|
||||
|
||||
/* calculate the position (seconds) in the buffer */
|
||||
buffer->SetPosition(
|
||||
((double) this->decoderSamplePosition) /
|
||||
((double) this->decoderSampleRate));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
BufferPtr Stream::GetNextProcessedOutputBuffer() {
|
||||
/* ask the decoder for the next buffer */
|
||||
BufferPtr currentBuffer = this->GetNextBufferFromDecoder();
|
||||
|
||||
if(currentBuffer) {
|
||||
/* try to fill the buffer to its optimal size; if the decoder didn't return
|
||||
a full buffer, ask it for some more data. */
|
||||
bool moreBuffers = true;
|
||||
while (currentBuffer->Samples() < this->preferedBufferSampleSize && moreBuffers) {
|
||||
BufferPtr bufferToAppend = this->GetNextBufferFromDecoder();
|
||||
if (bufferToAppend) {
|
||||
currentBuffer->Append(bufferToAppend);
|
||||
this->RecycleBuffer(bufferToAppend);
|
||||
}
|
||||
else {
|
||||
moreBuffers = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* let DSP plugins process the buffer */
|
||||
if (this->dsps.size() > 0) {
|
||||
BufferPtr oldBuffer = this->GetEmptyBuffer();
|
||||
|
||||
for (Dsps::iterator dsp = this->dsps.begin(); dsp != this->dsps.end(); ++dsp) {
|
||||
oldBuffer->CopyFormat(currentBuffer);
|
||||
currentBuffer->SetPosition(oldBuffer->Position());
|
||||
|
||||
if ((*dsp)->Process(currentBuffer.get(), oldBuffer.get())) {
|
||||
currentBuffer.swap(oldBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
this->RecycleBuffer(oldBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
return currentBuffer;
|
||||
}
|
||||
|
||||
/* returns a previously used buffer, if one is available. otherwise, a
|
||||
new one will be allocated. */
|
||||
BufferPtr Stream::GetEmptyBuffer() {
|
||||
BufferPtr buffer;
|
||||
if (!this->recycledBuffers.empty()) {
|
||||
buffer = this->recycledBuffers.front();
|
||||
this->recycledBuffers.pop_front();
|
||||
}
|
||||
else {
|
||||
buffer = Buffer::Create();
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* marks a used buffer as recycled so it can be re-used later. */
|
||||
void Stream::RecycleBuffer(BufferPtr oldBuffer) {
|
||||
this->recycledBuffers.push_back(oldBuffer);
|
||||
}
|
||||
|
||||
double Stream::DecoderProgress() {
|
||||
if (this->dataStream) {
|
||||
long fileSize = this->dataStream->Length();
|
||||
long filePosition = this->dataStream->Position();
|
||||
|
||||
if (fileSize && filePosition) {
|
||||
return ((double) filePosition) / ((double) fileSize);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Stream::LoadDecoderPlugins() {
|
||||
PluginFactory::DestroyDeleter<IDecoderFactory> typedef Deleter;
|
||||
|
||||
this->decoderFactories = PluginFactory::Instance()
|
||||
.QueryInterface<IDecoderFactory, Deleter>("GetDecoderFactory");
|
||||
}
|
||||
|
@ -30,45 +30,45 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/db/CachedStatement.h>
|
||||
#include <core/db/Statement.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <sqlite/sqlite3.h>
|
||||
|
||||
using namespace musik::core::db;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
///
|
||||
///\param sql
|
||||
///SQL
|
||||
///
|
||||
///\param connection
|
||||
///Connection to execute the statement on
|
||||
//////////////////////////////////////////
|
||||
CachedStatement::CachedStatement(const char* sql,Connection &connection) : Statement(connection){
|
||||
this->sqlStatement.assign(sql);
|
||||
this->stmt = this->connection->GetCachedStatement(sql);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destructor
|
||||
///
|
||||
///Will return the cached statement to the Connection
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::Connection::ReturnCachedStatement
|
||||
//////////////////////////////////////////
|
||||
CachedStatement::~CachedStatement(){
|
||||
sqlite3_reset(this->stmt);
|
||||
sqlite3_clear_bindings(this->stmt);
|
||||
this->connection->ReturnCachedStatement(this->sqlStatement.c_str(),this->stmt);
|
||||
this->stmt=NULL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/db/CachedStatement.h>
|
||||
#include <core/db/Statement.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <sqlite/sqlite3.h>
|
||||
|
||||
using namespace musik::core::db;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
///
|
||||
///\param sql
|
||||
///SQL
|
||||
///
|
||||
///\param connection
|
||||
///Connection to execute the statement on
|
||||
//////////////////////////////////////////
|
||||
CachedStatement::CachedStatement(const char* sql,Connection &connection) : Statement(connection){
|
||||
this->sqlStatement.assign(sql);
|
||||
this->stmt = this->connection->GetCachedStatement(sql);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destructor
|
||||
///
|
||||
///Will return the cached statement to the Connection
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::Connection::ReturnCachedStatement
|
||||
//////////////////////////////////////////
|
||||
CachedStatement::~CachedStatement(){
|
||||
sqlite3_reset(this->stmt);
|
||||
sqlite3_clear_bindings(this->stmt);
|
||||
this->connection->ReturnCachedStatement(this->sqlStatement.c_str(),this->stmt);
|
||||
this->stmt=NULL;
|
||||
}
|
||||
|
||||
|
@ -30,346 +30,346 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/db/Connection.h>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <sqlite/sqlite3.h>
|
||||
|
||||
using namespace musik::core::db;
|
||||
|
||||
boost::mutex Connection::globalMutex;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
//////////////////////////////////////////
|
||||
Connection::Connection() : connection(NULL),transactionCounter(0) {
|
||||
this->Maintenance(true);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destructor
|
||||
///
|
||||
///Will automatically close the connection if it's not closed before
|
||||
//////////////////////////////////////////
|
||||
Connection::~Connection(){
|
||||
this->Close();
|
||||
this->Maintenance(false);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Open a connection to the database
|
||||
///
|
||||
///\param database
|
||||
///Connection string. In SQLite this is the filename
|
||||
///
|
||||
///\param options
|
||||
///Bit options. Unused at the moment
|
||||
///
|
||||
///\param cache
|
||||
///Cachesize in KB
|
||||
///
|
||||
///\returns
|
||||
///Error code returned by SQLite
|
||||
//////////////////////////////////////////
|
||||
int Connection::Open(const char *database,unsigned int options,unsigned int cache){
|
||||
// sqlite3_enable_shared_cache(1);
|
||||
|
||||
int error;
|
||||
#ifdef UTF_WIDECHAR
|
||||
error = sqlite3_open16(database,&this->connection);
|
||||
#else
|
||||
error = sqlite3_open(database,&this->connection);
|
||||
#endif
|
||||
|
||||
if(error==SQLITE_OK){
|
||||
this->Initialize(cache);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Open a connection to the database
|
||||
///
|
||||
///\param database
|
||||
///Connection string. In SQLite this is the filename
|
||||
///
|
||||
///\param options
|
||||
///Bit options. Unused at the moment
|
||||
///
|
||||
///\param cache
|
||||
///Cachesize in KB
|
||||
///
|
||||
///\returns
|
||||
///Error code returned by SQLite
|
||||
//////////////////////////////////////////
|
||||
int Connection::Open(const std::string &database,unsigned int options,unsigned int cache){
|
||||
int error;
|
||||
#ifdef WIN32
|
||||
std::wstring wdatabase = u8to16(database);
|
||||
error = sqlite3_open16(wdatabase.c_str(),&this->connection);
|
||||
#else
|
||||
error = sqlite3_open(database.c_str(),&this->connection);
|
||||
#endif
|
||||
|
||||
if(error==SQLITE_OK){
|
||||
this->Initialize(cache);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Close connection to the database
|
||||
///
|
||||
///\returns
|
||||
///Errorcode ( musik::core::db::ReturnCode )
|
||||
//////////////////////////////////////////
|
||||
int Connection::Close(){
|
||||
|
||||
// Clear the cache
|
||||
for(StatementCache::iterator stmt=this->cachedStatements.begin();stmt!=this->cachedStatements.end();++stmt){
|
||||
sqlite3_finalize(stmt->second);
|
||||
}
|
||||
this->cachedStatements.clear();
|
||||
|
||||
|
||||
if(sqlite3_close(this->connection)==SQLITE_OK){
|
||||
this->connection = 0;
|
||||
return musik::core::db::Okay;
|
||||
}
|
||||
return musik::core::db::Error;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Execute a SQL string
|
||||
///
|
||||
///\param sql
|
||||
///SQL to execute
|
||||
///
|
||||
///\returns
|
||||
///Errorcode musik::core::db::ReturnCode
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::ReturnCode
|
||||
//////////////////////////////////////////
|
||||
int Connection::Execute(const char* sql){
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
|
||||
// Prepaire seems to give errors when interrupted
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
if(sqlite3_prepare_v2(this->connection,sql,-1,&stmt,NULL)!=SQLITE_OK){
|
||||
sqlite3_finalize(stmt);
|
||||
return db::Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the statement
|
||||
int error = this->StepStatement(stmt);
|
||||
if(error!=SQLITE_OK && error!=SQLITE_DONE){
|
||||
sqlite3_finalize(stmt);
|
||||
return db::Error;
|
||||
}
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return musik::core::db::Okay;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Execute a SQL string
|
||||
///
|
||||
///\param sql
|
||||
///SQL to execute
|
||||
///
|
||||
///\returns
|
||||
///Errorcode musik::core::db::ReturnCode
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::ReturnCode
|
||||
//////////////////////////////////////////
|
||||
int Connection::Execute(const wchar_t* sql){
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
int err = sqlite3_prepare16_v2(this->connection,sql,-1,&stmt,NULL);
|
||||
if(err!=SQLITE_OK){
|
||||
sqlite3_finalize(stmt);
|
||||
return db::Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the statement
|
||||
int error = this->StepStatement(stmt);
|
||||
if(error!=SQLITE_OK && error!=SQLITE_DONE){
|
||||
sqlite3_finalize(stmt);
|
||||
return db::Error;
|
||||
}
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return musik::core::db::Okay;
|
||||
}
|
||||
|
||||
void Connection::Analyze(){
|
||||
boost::mutex::scoped_lock lock(Connection::globalMutex);
|
||||
// this->Execute("ANALYZE");
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the last inserted row ID
|
||||
///
|
||||
///\returns
|
||||
///Last inserted row ID
|
||||
///
|
||||
///\see
|
||||
///http://www.sqlite.org/c3ref/last_insert_rowid.html
|
||||
//////////////////////////////////////////
|
||||
int Connection::LastInsertedId(){
|
||||
return (int)sqlite3_last_insert_rowid(this->connection);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Initializes the database.
|
||||
///
|
||||
///\param cache
|
||||
///Size of the cache to use in kilobytes
|
||||
///
|
||||
///This will set all the initial PRAGMAS
|
||||
//////////////////////////////////////////
|
||||
void Connection::Initialize(unsigned int cache){
|
||||
// sqlite3_enable_shared_cache(1);
|
||||
sqlite3_busy_timeout(this->connection,10000);
|
||||
|
||||
sqlite3_exec(this->connection,"PRAGMA synchronous=OFF",NULL,NULL,NULL); // Not a critical DB. Sync set to OFF
|
||||
sqlite3_exec(this->connection,"PRAGMA page_size=4096",NULL,NULL,NULL); // According to windows standard page size
|
||||
sqlite3_exec(this->connection,"PRAGMA auto_vacuum=0",NULL,NULL,NULL); // No autovaccum.
|
||||
|
||||
if(cache!=0){
|
||||
// Divide by 4 to since the page_size is 4096
|
||||
// Total cache is the same as page_size*cache_size
|
||||
cache = cache/4;
|
||||
|
||||
std::string cacheSize("PRAGMA cache_size=" + boost::lexical_cast<std::string>(cache));
|
||||
sqlite3_exec(this->connection,cacheSize.c_str(),NULL,NULL,NULL); // size * 1.5kb = 6Mb cache
|
||||
}
|
||||
|
||||
|
||||
sqlite3_exec(this->connection,"PRAGMA case_sensitive_like=0",NULL,NULL,NULL); // More speed if case insensitive
|
||||
sqlite3_exec(this->connection,"PRAGMA count_changes=0",NULL,NULL,NULL); // If set it counts changes on SQL UPDATE. More speed when not.
|
||||
sqlite3_exec(this->connection,"PRAGMA legacy_file_format=OFF",NULL,NULL,NULL); // No reason to be backwards compatible :)
|
||||
sqlite3_exec(this->connection,"PRAGMA temp_store=MEMORY",NULL,NULL,NULL); // MEMORY, not file. More speed.
|
||||
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Internal method used by the CachedStatement to locate if a statement already exists
|
||||
///
|
||||
///\param sql
|
||||
///SQL to check for
|
||||
///
|
||||
///\returns
|
||||
///The cached or newly created statement
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::CachedStatment
|
||||
//////////////////////////////////////////
|
||||
sqlite3_stmt *Connection::GetCachedStatement(const char* sql){
|
||||
sqlite3_stmt *newStmt(NULL);
|
||||
|
||||
StatementCache::iterator stmt = this->cachedStatements.find(sql);
|
||||
if(stmt==this->cachedStatements.end()){
|
||||
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
int err = sqlite3_prepare_v2(this->connection,sql,-1,&newStmt,NULL);
|
||||
if(err!=SQLITE_OK){
|
||||
return NULL;
|
||||
}
|
||||
return newStmt;
|
||||
}
|
||||
|
||||
newStmt = stmt->second;
|
||||
this->cachedStatements.erase(stmt);
|
||||
return newStmt;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Used by CachedStatement when destructed to return it's statement.
|
||||
///
|
||||
///\param sql
|
||||
///SQL string
|
||||
///
|
||||
///\param stmt
|
||||
///Statement to return
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::CachedStatment
|
||||
//////////////////////////////////////////
|
||||
void Connection::ReturnCachedStatement(const char* sql,sqlite3_stmt *stmt){
|
||||
StatementCache::iterator cacheStmt = this->cachedStatements.find(sql);
|
||||
if(cacheStmt==this->cachedStatements.end()){
|
||||
// Insert the stmt in cache
|
||||
this->cachedStatements[sql] = stmt;
|
||||
}else{
|
||||
// Stmt already exists. Finalize it
|
||||
DB_ASSERT(sqlite3_finalize(stmt));
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Interrupts the current running statement(s)
|
||||
//////////////////////////////////////////
|
||||
void Connection::Interrupt(){
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
sqlite3_interrupt(this->connection);
|
||||
}
|
||||
|
||||
void Connection::Maintenance(bool init){
|
||||
|
||||
// Need to be locked throuout all Connections
|
||||
boost::mutex::scoped_lock lock(Connection::globalMutex);
|
||||
|
||||
static int counter(0);
|
||||
|
||||
if(init){
|
||||
if(counter==0){
|
||||
sqlite3_initialize();
|
||||
}
|
||||
++counter;
|
||||
}else{
|
||||
--counter;
|
||||
if(counter==0){
|
||||
sqlite3_shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Connection::StepStatement(sqlite3_stmt *stmt) {
|
||||
return sqlite3_step(stmt);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/db/Connection.h>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <sqlite/sqlite3.h>
|
||||
|
||||
using namespace musik::core::db;
|
||||
|
||||
boost::mutex Connection::globalMutex;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
//////////////////////////////////////////
|
||||
Connection::Connection() : connection(NULL),transactionCounter(0) {
|
||||
this->Maintenance(true);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destructor
|
||||
///
|
||||
///Will automatically close the connection if it's not closed before
|
||||
//////////////////////////////////////////
|
||||
Connection::~Connection(){
|
||||
this->Close();
|
||||
this->Maintenance(false);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Open a connection to the database
|
||||
///
|
||||
///\param database
|
||||
///Connection string. In SQLite this is the filename
|
||||
///
|
||||
///\param options
|
||||
///Bit options. Unused at the moment
|
||||
///
|
||||
///\param cache
|
||||
///Cachesize in KB
|
||||
///
|
||||
///\returns
|
||||
///Error code returned by SQLite
|
||||
//////////////////////////////////////////
|
||||
int Connection::Open(const char *database,unsigned int options,unsigned int cache){
|
||||
// sqlite3_enable_shared_cache(1);
|
||||
|
||||
int error;
|
||||
#ifdef UTF_WIDECHAR
|
||||
error = sqlite3_open16(database,&this->connection);
|
||||
#else
|
||||
error = sqlite3_open(database,&this->connection);
|
||||
#endif
|
||||
|
||||
if(error==SQLITE_OK){
|
||||
this->Initialize(cache);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Open a connection to the database
|
||||
///
|
||||
///\param database
|
||||
///Connection string. In SQLite this is the filename
|
||||
///
|
||||
///\param options
|
||||
///Bit options. Unused at the moment
|
||||
///
|
||||
///\param cache
|
||||
///Cachesize in KB
|
||||
///
|
||||
///\returns
|
||||
///Error code returned by SQLite
|
||||
//////////////////////////////////////////
|
||||
int Connection::Open(const std::string &database,unsigned int options,unsigned int cache){
|
||||
int error;
|
||||
#ifdef WIN32
|
||||
std::wstring wdatabase = u8to16(database);
|
||||
error = sqlite3_open16(wdatabase.c_str(),&this->connection);
|
||||
#else
|
||||
error = sqlite3_open(database.c_str(),&this->connection);
|
||||
#endif
|
||||
|
||||
if(error==SQLITE_OK){
|
||||
this->Initialize(cache);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Close connection to the database
|
||||
///
|
||||
///\returns
|
||||
///Errorcode ( musik::core::db::ReturnCode )
|
||||
//////////////////////////////////////////
|
||||
int Connection::Close(){
|
||||
|
||||
// Clear the cache
|
||||
for(StatementCache::iterator stmt=this->cachedStatements.begin();stmt!=this->cachedStatements.end();++stmt){
|
||||
sqlite3_finalize(stmt->second);
|
||||
}
|
||||
this->cachedStatements.clear();
|
||||
|
||||
|
||||
if(sqlite3_close(this->connection)==SQLITE_OK){
|
||||
this->connection = 0;
|
||||
return musik::core::db::Okay;
|
||||
}
|
||||
return musik::core::db::Error;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Execute a SQL string
|
||||
///
|
||||
///\param sql
|
||||
///SQL to execute
|
||||
///
|
||||
///\returns
|
||||
///Errorcode musik::core::db::ReturnCode
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::ReturnCode
|
||||
//////////////////////////////////////////
|
||||
int Connection::Execute(const char* sql){
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
|
||||
// Prepaire seems to give errors when interrupted
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
if(sqlite3_prepare_v2(this->connection,sql,-1,&stmt,NULL)!=SQLITE_OK){
|
||||
sqlite3_finalize(stmt);
|
||||
return db::Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the statement
|
||||
int error = this->StepStatement(stmt);
|
||||
if(error!=SQLITE_OK && error!=SQLITE_DONE){
|
||||
sqlite3_finalize(stmt);
|
||||
return db::Error;
|
||||
}
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return musik::core::db::Okay;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Execute a SQL string
|
||||
///
|
||||
///\param sql
|
||||
///SQL to execute
|
||||
///
|
||||
///\returns
|
||||
///Errorcode musik::core::db::ReturnCode
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::ReturnCode
|
||||
//////////////////////////////////////////
|
||||
int Connection::Execute(const wchar_t* sql){
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
int err = sqlite3_prepare16_v2(this->connection,sql,-1,&stmt,NULL);
|
||||
if(err!=SQLITE_OK){
|
||||
sqlite3_finalize(stmt);
|
||||
return db::Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the statement
|
||||
int error = this->StepStatement(stmt);
|
||||
if(error!=SQLITE_OK && error!=SQLITE_DONE){
|
||||
sqlite3_finalize(stmt);
|
||||
return db::Error;
|
||||
}
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return musik::core::db::Okay;
|
||||
}
|
||||
|
||||
void Connection::Analyze(){
|
||||
boost::mutex::scoped_lock lock(Connection::globalMutex);
|
||||
// this->Execute("ANALYZE");
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the last inserted row ID
|
||||
///
|
||||
///\returns
|
||||
///Last inserted row ID
|
||||
///
|
||||
///\see
|
||||
///http://www.sqlite.org/c3ref/last_insert_rowid.html
|
||||
//////////////////////////////////////////
|
||||
int Connection::LastInsertedId(){
|
||||
return (int)sqlite3_last_insert_rowid(this->connection);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Initializes the database.
|
||||
///
|
||||
///\param cache
|
||||
///Size of the cache to use in kilobytes
|
||||
///
|
||||
///This will set all the initial PRAGMAS
|
||||
//////////////////////////////////////////
|
||||
void Connection::Initialize(unsigned int cache){
|
||||
// sqlite3_enable_shared_cache(1);
|
||||
sqlite3_busy_timeout(this->connection,10000);
|
||||
|
||||
sqlite3_exec(this->connection,"PRAGMA synchronous=OFF",NULL,NULL,NULL); // Not a critical DB. Sync set to OFF
|
||||
sqlite3_exec(this->connection,"PRAGMA page_size=4096",NULL,NULL,NULL); // According to windows standard page size
|
||||
sqlite3_exec(this->connection,"PRAGMA auto_vacuum=0",NULL,NULL,NULL); // No autovaccum.
|
||||
|
||||
if(cache!=0){
|
||||
// Divide by 4 to since the page_size is 4096
|
||||
// Total cache is the same as page_size*cache_size
|
||||
cache = cache/4;
|
||||
|
||||
std::string cacheSize("PRAGMA cache_size=" + boost::lexical_cast<std::string>(cache));
|
||||
sqlite3_exec(this->connection,cacheSize.c_str(),NULL,NULL,NULL); // size * 1.5kb = 6Mb cache
|
||||
}
|
||||
|
||||
|
||||
sqlite3_exec(this->connection,"PRAGMA case_sensitive_like=0",NULL,NULL,NULL); // More speed if case insensitive
|
||||
sqlite3_exec(this->connection,"PRAGMA count_changes=0",NULL,NULL,NULL); // If set it counts changes on SQL UPDATE. More speed when not.
|
||||
sqlite3_exec(this->connection,"PRAGMA legacy_file_format=OFF",NULL,NULL,NULL); // No reason to be backwards compatible :)
|
||||
sqlite3_exec(this->connection,"PRAGMA temp_store=MEMORY",NULL,NULL,NULL); // MEMORY, not file. More speed.
|
||||
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Internal method used by the CachedStatement to locate if a statement already exists
|
||||
///
|
||||
///\param sql
|
||||
///SQL to check for
|
||||
///
|
||||
///\returns
|
||||
///The cached or newly created statement
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::CachedStatment
|
||||
//////////////////////////////////////////
|
||||
sqlite3_stmt *Connection::GetCachedStatement(const char* sql){
|
||||
sqlite3_stmt *newStmt(NULL);
|
||||
|
||||
StatementCache::iterator stmt = this->cachedStatements.find(sql);
|
||||
if(stmt==this->cachedStatements.end()){
|
||||
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
int err = sqlite3_prepare_v2(this->connection,sql,-1,&newStmt,NULL);
|
||||
if(err!=SQLITE_OK){
|
||||
return NULL;
|
||||
}
|
||||
return newStmt;
|
||||
}
|
||||
|
||||
newStmt = stmt->second;
|
||||
this->cachedStatements.erase(stmt);
|
||||
return newStmt;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Used by CachedStatement when destructed to return it's statement.
|
||||
///
|
||||
///\param sql
|
||||
///SQL string
|
||||
///
|
||||
///\param stmt
|
||||
///Statement to return
|
||||
///
|
||||
///\see
|
||||
///musik::core::db::CachedStatment
|
||||
//////////////////////////////////////////
|
||||
void Connection::ReturnCachedStatement(const char* sql,sqlite3_stmt *stmt){
|
||||
StatementCache::iterator cacheStmt = this->cachedStatements.find(sql);
|
||||
if(cacheStmt==this->cachedStatements.end()){
|
||||
// Insert the stmt in cache
|
||||
this->cachedStatements[sql] = stmt;
|
||||
}else{
|
||||
// Stmt already exists. Finalize it
|
||||
DB_ASSERT(sqlite3_finalize(stmt));
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Interrupts the current running statement(s)
|
||||
//////////////////////////////////////////
|
||||
void Connection::Interrupt(){
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
sqlite3_interrupt(this->connection);
|
||||
}
|
||||
|
||||
void Connection::Maintenance(bool init){
|
||||
|
||||
// Need to be locked throuout all Connections
|
||||
boost::mutex::scoped_lock lock(Connection::globalMutex);
|
||||
|
||||
static int counter(0);
|
||||
|
||||
if(init){
|
||||
if(counter==0){
|
||||
sqlite3_initialize();
|
||||
}
|
||||
++counter;
|
||||
}else{
|
||||
--counter;
|
||||
if(counter==0){
|
||||
sqlite3_shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Connection::StepStatement(sqlite3_stmt *stmt) {
|
||||
return sqlite3_step(stmt);
|
||||
}
|
||||
|
@ -30,80 +30,80 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/db/ScopedTransaction.h>
|
||||
#include <core/db/Connection.h>
|
||||
|
||||
using namespace musik::core::db;
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
///
|
||||
///\param connection
|
||||
///Connection to run transaction on
|
||||
//////////////////////////////////////////
|
||||
ScopedTransaction::ScopedTransaction(Connection &connection) : canceled(false){
|
||||
this->connection = &connection;
|
||||
this->Begin();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destructor will end the transaction if it's the last nested transaction
|
||||
//////////////////////////////////////////
|
||||
ScopedTransaction::~ScopedTransaction(){
|
||||
this->End();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///If canceled, this all statements in the transaction scope will be canceled
|
||||
//////////////////////////////////////////
|
||||
void ScopedTransaction::Cancel(){
|
||||
this->canceled = true;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Sometimes it's a good option to be able to commit a transaction and restart it all over.
|
||||
//////////////////////////////////////////
|
||||
void ScopedTransaction::CommitAndRestart(){
|
||||
this->End();
|
||||
this->Begin();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Runs the acctual BEGIN TRANSACTION on the database
|
||||
//////////////////////////////////////////
|
||||
void ScopedTransaction::Begin(){
|
||||
if(this->connection->transactionCounter==0){
|
||||
this->connection->Execute("BEGIN TRANSACTION");
|
||||
}
|
||||
++this->connection->transactionCounter;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Runs the COMMIT or ROLLBACK on the database
|
||||
//////////////////////////////////////////
|
||||
void ScopedTransaction::End(){
|
||||
--this->connection->transactionCounter;
|
||||
if(this->connection->transactionCounter==0){
|
||||
if(this->canceled){
|
||||
this->connection->Execute("ROLLBACK TRANSACTION");
|
||||
}else{
|
||||
this->connection->Execute("COMMIT TRANSACTION");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/db/ScopedTransaction.h>
|
||||
#include <core/db/Connection.h>
|
||||
|
||||
using namespace musik::core::db;
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
///
|
||||
///\param connection
|
||||
///Connection to run transaction on
|
||||
//////////////////////////////////////////
|
||||
ScopedTransaction::ScopedTransaction(Connection &connection) : canceled(false){
|
||||
this->connection = &connection;
|
||||
this->Begin();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destructor will end the transaction if it's the last nested transaction
|
||||
//////////////////////////////////////////
|
||||
ScopedTransaction::~ScopedTransaction(){
|
||||
this->End();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///If canceled, this all statements in the transaction scope will be canceled
|
||||
//////////////////////////////////////////
|
||||
void ScopedTransaction::Cancel(){
|
||||
this->canceled = true;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Sometimes it's a good option to be able to commit a transaction and restart it all over.
|
||||
//////////////////////////////////////////
|
||||
void ScopedTransaction::CommitAndRestart(){
|
||||
this->End();
|
||||
this->Begin();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Runs the acctual BEGIN TRANSACTION on the database
|
||||
//////////////////////////////////////////
|
||||
void ScopedTransaction::Begin(){
|
||||
if(this->connection->transactionCounter==0){
|
||||
this->connection->Execute("BEGIN TRANSACTION");
|
||||
}
|
||||
++this->connection->transactionCounter;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Runs the COMMIT or ROLLBACK on the database
|
||||
//////////////////////////////////////////
|
||||
void ScopedTransaction::End(){
|
||||
--this->connection->transactionCounter;
|
||||
if(this->connection->transactionCounter==0){
|
||||
if(this->canceled){
|
||||
this->connection->Execute("ROLLBACK TRANSACTION");
|
||||
}else{
|
||||
this->connection->Execute("COMMIT TRANSACTION");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,239 +30,239 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/db/Statement.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <sqlite/sqlite3.h>
|
||||
|
||||
using namespace musik::core::db;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
///
|
||||
///\param sql
|
||||
///SQL to be precomiled
|
||||
///
|
||||
///\param connection
|
||||
///database Connection
|
||||
//////////////////////////////////////////
|
||||
Statement::Statement(const char* sql,Connection &connection)
|
||||
: connection(&connection)
|
||||
, stmt(NULL)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(connection.mutex);
|
||||
|
||||
int err = sqlite3_prepare_v2(
|
||||
this->connection->connection, sql, -1, &this->stmt, NULL);
|
||||
|
||||
if (err!=SQLITE_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor used by the CachedStatement
|
||||
//////////////////////////////////////////
|
||||
Statement::Statement(Connection &connection) : connection(&connection),stmt(NULL) {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destructor that will finalize the statement
|
||||
//////////////////////////////////////////
|
||||
Statement::~Statement(){
|
||||
int err=sqlite3_finalize(this->stmt);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Reset a statment to be able to re-execute it later
|
||||
//////////////////////////////////////////
|
||||
void Statement::Reset(){
|
||||
int err = sqlite3_reset(this->stmt);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Unbinds all previously binded parameters
|
||||
//////////////////////////////////////////
|
||||
void Statement::UnBindAll(){
|
||||
DB_ASSERT(sqlite3_clear_bindings(this->stmt));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Execute/Step through the statment
|
||||
///
|
||||
///\returns
|
||||
///musik::core::db::ReturnCode
|
||||
//////////////////////////////////////////
|
||||
int Statement::Step(){
|
||||
return this->connection->StepStatement(this->stmt);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a integer to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindInt
|
||||
///Integer to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindInt(int position,int bindInt){
|
||||
DB_ASSERT(sqlite3_bind_int(this->stmt, position + 1, bindInt));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a 64bit integer to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindInt
|
||||
///Integer to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindInt(int position, uint64 bindInt){
|
||||
DB_ASSERT(sqlite3_bind_int64(this->stmt, position + 1, bindInt));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a text to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindText
|
||||
///Text to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindText(int position, const char* bindText) {
|
||||
DB_ASSERT(sqlite3_bind_text(
|
||||
this->stmt,
|
||||
position + 1,
|
||||
bindText,
|
||||
-1,
|
||||
SQLITE_STATIC));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a text to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindText
|
||||
///Text to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindText(int position ,const std::string &bindText) {
|
||||
DB_ASSERT(sqlite3_bind_text(
|
||||
this->stmt, position + 1,
|
||||
bindText.c_str(),
|
||||
-1,
|
||||
SQLITE_TRANSIENT));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a text to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindText
|
||||
///Text to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindTextW(int position,const wchar_t* bindText){
|
||||
DB_ASSERT(sqlite3_bind_text16(
|
||||
this->stmt,
|
||||
position + 1,
|
||||
bindText,
|
||||
-1,
|
||||
SQLITE_STATIC));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a text to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindText
|
||||
///Text to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindTextW(int position,const std::wstring &bindText){
|
||||
DB_ASSERT(sqlite3_bind_text16(
|
||||
this->stmt,
|
||||
position + 1,
|
||||
bindText.c_str(),
|
||||
-1,
|
||||
SQLITE_TRANSIENT));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the results of a column if Step() return a musik::core::db::Row
|
||||
///
|
||||
///\param column
|
||||
///Column to get (0 is the first)
|
||||
///
|
||||
///\returns
|
||||
///Column data
|
||||
//////////////////////////////////////////
|
||||
int Statement::ColumnInt(int column){
|
||||
return sqlite3_column_int(this->stmt,column);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the results of a column if Step() return a musik::core::db::Row
|
||||
///
|
||||
///\param column
|
||||
///Column to get (0 is the first)
|
||||
///
|
||||
///\returns
|
||||
///Column data
|
||||
//////////////////////////////////////////
|
||||
uint64 Statement::ColumnInt64(int column){
|
||||
return sqlite3_column_int64(this->stmt,column);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the results of a column if Step() return a musik::core::db::Row
|
||||
///
|
||||
///\param column
|
||||
///Column to get (0 is the first)
|
||||
///
|
||||
///\returns
|
||||
///Column data
|
||||
//////////////////////////////////////////
|
||||
const char* Statement::ColumnText(int column){
|
||||
const char* text = (char*) sqlite3_column_text(this->stmt, column);
|
||||
return text ? text : "";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the results of a column if Step() return a musik::core::db::Row
|
||||
///
|
||||
///\param column
|
||||
///Column to get (0 is the first)
|
||||
///
|
||||
///\returns
|
||||
///Column data
|
||||
//////////////////////////////////////////
|
||||
const wchar_t* Statement::ColumnTextW(int column){
|
||||
const wchar_t* text = (wchar_t*) sqlite3_column_text16(this->stmt,column);
|
||||
return text ? text : L"";
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/db/Statement.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <sqlite/sqlite3.h>
|
||||
|
||||
using namespace musik::core::db;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
///
|
||||
///\param sql
|
||||
///SQL to be precomiled
|
||||
///
|
||||
///\param connection
|
||||
///database Connection
|
||||
//////////////////////////////////////////
|
||||
Statement::Statement(const char* sql,Connection &connection)
|
||||
: connection(&connection)
|
||||
, stmt(NULL)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(connection.mutex);
|
||||
|
||||
int err = sqlite3_prepare_v2(
|
||||
this->connection->connection, sql, -1, &this->stmt, NULL);
|
||||
|
||||
if (err!=SQLITE_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor used by the CachedStatement
|
||||
//////////////////////////////////////////
|
||||
Statement::Statement(Connection &connection) : connection(&connection),stmt(NULL) {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destructor that will finalize the statement
|
||||
//////////////////////////////////////////
|
||||
Statement::~Statement(){
|
||||
int err=sqlite3_finalize(this->stmt);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Reset a statment to be able to re-execute it later
|
||||
//////////////////////////////////////////
|
||||
void Statement::Reset(){
|
||||
int err = sqlite3_reset(this->stmt);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Unbinds all previously binded parameters
|
||||
//////////////////////////////////////////
|
||||
void Statement::UnBindAll(){
|
||||
DB_ASSERT(sqlite3_clear_bindings(this->stmt));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Execute/Step through the statment
|
||||
///
|
||||
///\returns
|
||||
///musik::core::db::ReturnCode
|
||||
//////////////////////////////////////////
|
||||
int Statement::Step(){
|
||||
return this->connection->StepStatement(this->stmt);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a integer to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindInt
|
||||
///Integer to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindInt(int position,int bindInt){
|
||||
DB_ASSERT(sqlite3_bind_int(this->stmt, position + 1, bindInt));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a 64bit integer to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindInt
|
||||
///Integer to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindInt(int position, uint64 bindInt){
|
||||
DB_ASSERT(sqlite3_bind_int64(this->stmt, position + 1, bindInt));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a text to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindText
|
||||
///Text to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindText(int position, const char* bindText) {
|
||||
DB_ASSERT(sqlite3_bind_text(
|
||||
this->stmt,
|
||||
position + 1,
|
||||
bindText,
|
||||
-1,
|
||||
SQLITE_STATIC));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a text to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindText
|
||||
///Text to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindText(int position ,const std::string &bindText) {
|
||||
DB_ASSERT(sqlite3_bind_text(
|
||||
this->stmt, position + 1,
|
||||
bindText.c_str(),
|
||||
-1,
|
||||
SQLITE_TRANSIENT));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a text to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindText
|
||||
///Text to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindTextW(int position,const wchar_t* bindText){
|
||||
DB_ASSERT(sqlite3_bind_text16(
|
||||
this->stmt,
|
||||
position + 1,
|
||||
bindText,
|
||||
-1,
|
||||
SQLITE_STATIC));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Bind a text to a statment parameter
|
||||
///
|
||||
///\param position
|
||||
///Position of the parameter (0 is the first)
|
||||
///
|
||||
///\param bindText
|
||||
///Text to bind
|
||||
//////////////////////////////////////////
|
||||
void Statement::BindTextW(int position,const std::wstring &bindText){
|
||||
DB_ASSERT(sqlite3_bind_text16(
|
||||
this->stmt,
|
||||
position + 1,
|
||||
bindText.c_str(),
|
||||
-1,
|
||||
SQLITE_TRANSIENT));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the results of a column if Step() return a musik::core::db::Row
|
||||
///
|
||||
///\param column
|
||||
///Column to get (0 is the first)
|
||||
///
|
||||
///\returns
|
||||
///Column data
|
||||
//////////////////////////////////////////
|
||||
int Statement::ColumnInt(int column){
|
||||
return sqlite3_column_int(this->stmt,column);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the results of a column if Step() return a musik::core::db::Row
|
||||
///
|
||||
///\param column
|
||||
///Column to get (0 is the first)
|
||||
///
|
||||
///\returns
|
||||
///Column data
|
||||
//////////////////////////////////////////
|
||||
uint64 Statement::ColumnInt64(int column){
|
||||
return sqlite3_column_int64(this->stmt,column);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the results of a column if Step() return a musik::core::db::Row
|
||||
///
|
||||
///\param column
|
||||
///Column to get (0 is the first)
|
||||
///
|
||||
///\returns
|
||||
///Column data
|
||||
//////////////////////////////////////////
|
||||
const char* Statement::ColumnText(int column){
|
||||
const char* text = (char*) sqlite3_column_text(this->stmt, column);
|
||||
return text ? text : "";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the results of a column if Step() return a musik::core::db::Row
|
||||
///
|
||||
///\param column
|
||||
///Column to get (0 is the first)
|
||||
///
|
||||
///\returns
|
||||
///Column data
|
||||
//////////////////////////////////////////
|
||||
const wchar_t* Statement::ColumnTextW(int column){
|
||||
const wchar_t* text = (wchar_t*) sqlite3_column_text16(this->stmt,column);
|
||||
return text ? text : L"";
|
||||
}
|
||||
|
@ -30,87 +30,87 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/io/DataStreamFactory.h>
|
||||
#include <core/config.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <core/io/LocalFileStream.h>
|
||||
|
||||
using namespace musik::core::io;
|
||||
|
||||
DataStreamFactory::DataStreamFactory() {
|
||||
typedef IDataStreamFactory PluginType;
|
||||
typedef musik::core::PluginFactory::DestroyDeleter<PluginType> Deleter;
|
||||
|
||||
this->dataStreamFactories = musik::core::PluginFactory::Instance()
|
||||
.QueryInterface<PluginType, Deleter>("GetDataStreamFactory");
|
||||
}
|
||||
|
||||
DataStreamFactory* DataStreamFactory::Instance() {
|
||||
static DataStreamFactory* instance = NULL;
|
||||
|
||||
if (instance == NULL) {
|
||||
instance = new DataStreamFactory();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
DataStreamFactory::DataStreamPtr DataStreamFactory::OpenUri(const char *uri) {
|
||||
typedef musik::core::PluginFactory::DestroyDeleter<IDataStream> StreamDeleter;
|
||||
|
||||
if (uri) {
|
||||
DataStreamFactoryVector::iterator it =
|
||||
DataStreamFactory::Instance()->dataStreamFactories.begin();
|
||||
|
||||
/* plugins get the first crack at the uri */
|
||||
for( ; it != DataStreamFactory::Instance()->dataStreamFactories.end(); it++) {
|
||||
if ((*it)->CanRead(uri)) {
|
||||
IDataStream* dataStream = (*it)->Open(uri);
|
||||
|
||||
if (dataStream) {
|
||||
return DataStreamPtr(dataStream, StreamDeleter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no plugins accepted it? try to open as a local file */
|
||||
DataStreamPtr regularFile(new LocalFileStream(), StreamDeleter());
|
||||
|
||||
if (regularFile->Open(uri)) {
|
||||
return regularFile;
|
||||
}
|
||||
}
|
||||
|
||||
return DataStreamPtr();
|
||||
}
|
||||
|
||||
bool DataStreamFactory::IsLocalFileStream(const char *uri) {
|
||||
typedef musik::core::PluginFactory::DestroyDeleter<IDataStream> StreamDeleter;
|
||||
|
||||
if (uri) {
|
||||
/* see if a plugin can handle this. if it can, then it's not
|
||||
considered to be a local file stream */
|
||||
DataStreamFactoryVector::iterator it =
|
||||
DataStreamFactory::Instance()->dataStreamFactories.begin();
|
||||
|
||||
for( ; it != DataStreamFactory::Instance()->dataStreamFactories.end(); ++it) {
|
||||
if ((*it)->CanRead(uri)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* now test local filesystem */
|
||||
boost::filesystem::path filename(uri);
|
||||
try {
|
||||
return boost::filesystem::exists(filename);
|
||||
}
|
||||
catch(...) {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/io/DataStreamFactory.h>
|
||||
#include <core/config.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <core/io/LocalFileStream.h>
|
||||
|
||||
using namespace musik::core::io;
|
||||
|
||||
DataStreamFactory::DataStreamFactory() {
|
||||
typedef IDataStreamFactory PluginType;
|
||||
typedef musik::core::PluginFactory::DestroyDeleter<PluginType> Deleter;
|
||||
|
||||
this->dataStreamFactories = musik::core::PluginFactory::Instance()
|
||||
.QueryInterface<PluginType, Deleter>("GetDataStreamFactory");
|
||||
}
|
||||
|
||||
DataStreamFactory* DataStreamFactory::Instance() {
|
||||
static DataStreamFactory* instance = NULL;
|
||||
|
||||
if (instance == NULL) {
|
||||
instance = new DataStreamFactory();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
DataStreamFactory::DataStreamPtr DataStreamFactory::OpenUri(const char *uri) {
|
||||
typedef musik::core::PluginFactory::DestroyDeleter<IDataStream> StreamDeleter;
|
||||
|
||||
if (uri) {
|
||||
DataStreamFactoryVector::iterator it =
|
||||
DataStreamFactory::Instance()->dataStreamFactories.begin();
|
||||
|
||||
/* plugins get the first crack at the uri */
|
||||
for( ; it != DataStreamFactory::Instance()->dataStreamFactories.end(); it++) {
|
||||
if ((*it)->CanRead(uri)) {
|
||||
IDataStream* dataStream = (*it)->Open(uri);
|
||||
|
||||
if (dataStream) {
|
||||
return DataStreamPtr(dataStream, StreamDeleter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no plugins accepted it? try to open as a local file */
|
||||
DataStreamPtr regularFile(new LocalFileStream(), StreamDeleter());
|
||||
|
||||
if (regularFile->Open(uri)) {
|
||||
return regularFile;
|
||||
}
|
||||
}
|
||||
|
||||
return DataStreamPtr();
|
||||
}
|
||||
|
||||
bool DataStreamFactory::IsLocalFileStream(const char *uri) {
|
||||
typedef musik::core::PluginFactory::DestroyDeleter<IDataStream> StreamDeleter;
|
||||
|
||||
if (uri) {
|
||||
/* see if a plugin can handle this. if it can, then it's not
|
||||
considered to be a local file stream */
|
||||
DataStreamFactoryVector::iterator it =
|
||||
DataStreamFactory::Instance()->dataStreamFactories.begin();
|
||||
|
||||
for( ; it != DataStreamFactory::Instance()->dataStreamFactories.end(); ++it) {
|
||||
if ((*it)->CanRead(uri)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* now test local filesystem */
|
||||
boost::filesystem::path filename(uri);
|
||||
try {
|
||||
return boost::filesystem::exists(filename);
|
||||
}
|
||||
catch(...) {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -30,107 +30,107 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/io/LocalFileStream.h>
|
||||
#include <core/config.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/config.h>
|
||||
|
||||
static const std::string TAG = "LocalFileStream";
|
||||
|
||||
using namespace musik::core::io;
|
||||
|
||||
LocalFileStream::LocalFileStream()
|
||||
: file(NULL)
|
||||
, filesize(-1) {
|
||||
}
|
||||
|
||||
LocalFileStream::~LocalFileStream() {
|
||||
try {
|
||||
this->Close();
|
||||
}
|
||||
catch (...) {
|
||||
musik::debug::err(TAG, "error closing file");
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalFileStream::Open(const char *filename, unsigned int options) {
|
||||
try {
|
||||
std::string fn(filename);
|
||||
debug::info(TAG, "opening file: " + std::string(filename));
|
||||
|
||||
boost::filesystem::path file(filename);
|
||||
|
||||
if (!boost::filesystem::exists(file)) {
|
||||
debug::err(TAG, "open failed " + fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!boost::filesystem::is_regular(file)) {
|
||||
debug::err(TAG, "not a regular file" + fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->filesize = (long)boost::filesystem::file_size(file);
|
||||
this->extension = file.extension().string();
|
||||
#ifdef WIN32
|
||||
std::wstring u16fn = u8to16(fn);
|
||||
this->file = _wfopen(u16fn.c_str(), L"rb");
|
||||
#else
|
||||
this->file = fopen(filename, "rb");
|
||||
#endif
|
||||
|
||||
if (this->file != NULL) {
|
||||
debug::info(TAG, "opened successfully");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch(...) {
|
||||
}
|
||||
|
||||
debug::err(TAG, "open failed " + std::string(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LocalFileStream::Close() {
|
||||
if (this->file) {
|
||||
if (fclose(this->file) == 0) {
|
||||
this->file = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocalFileStream::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
PositionType LocalFileStream::Read(void* buffer, PositionType readBytes) {
|
||||
return (PositionType) fread(buffer, 1, readBytes, this->file);
|
||||
}
|
||||
|
||||
bool LocalFileStream::SetPosition(PositionType position) {
|
||||
return fseek(this->file, position, SEEK_SET) == 0;
|
||||
}
|
||||
|
||||
PositionType LocalFileStream::Position() {
|
||||
return ftell(this->file);
|
||||
}
|
||||
|
||||
bool LocalFileStream::Eof() {
|
||||
return feof(this->file) != 0;
|
||||
}
|
||||
|
||||
long LocalFileStream::Length() {
|
||||
return this->filesize;
|
||||
}
|
||||
|
||||
const char* LocalFileStream::Type() {
|
||||
return this->extension.c_str();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/io/LocalFileStream.h>
|
||||
#include <core/config.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/config.h>
|
||||
|
||||
static const std::string TAG = "LocalFileStream";
|
||||
|
||||
using namespace musik::core::io;
|
||||
|
||||
LocalFileStream::LocalFileStream()
|
||||
: file(NULL)
|
||||
, filesize(-1) {
|
||||
}
|
||||
|
||||
LocalFileStream::~LocalFileStream() {
|
||||
try {
|
||||
this->Close();
|
||||
}
|
||||
catch (...) {
|
||||
musik::debug::err(TAG, "error closing file");
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalFileStream::Open(const char *filename, unsigned int options) {
|
||||
try {
|
||||
std::string fn(filename);
|
||||
debug::info(TAG, "opening file: " + std::string(filename));
|
||||
|
||||
boost::filesystem::path file(filename);
|
||||
|
||||
if (!boost::filesystem::exists(file)) {
|
||||
debug::err(TAG, "open failed " + fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!boost::filesystem::is_regular(file)) {
|
||||
debug::err(TAG, "not a regular file" + fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->filesize = (long)boost::filesystem::file_size(file);
|
||||
this->extension = file.extension().string();
|
||||
#ifdef WIN32
|
||||
std::wstring u16fn = u8to16(fn);
|
||||
this->file = _wfopen(u16fn.c_str(), L"rb");
|
||||
#else
|
||||
this->file = fopen(filename, "rb");
|
||||
#endif
|
||||
|
||||
if (this->file != NULL) {
|
||||
debug::info(TAG, "opened successfully");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch(...) {
|
||||
}
|
||||
|
||||
debug::err(TAG, "open failed " + std::string(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LocalFileStream::Close() {
|
||||
if (this->file) {
|
||||
if (fclose(this->file) == 0) {
|
||||
this->file = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocalFileStream::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
PositionType LocalFileStream::Read(void* buffer, PositionType readBytes) {
|
||||
return (PositionType) fread(buffer, 1, readBytes, this->file);
|
||||
}
|
||||
|
||||
bool LocalFileStream::SetPosition(PositionType position) {
|
||||
return fseek(this->file, position, SEEK_SET) == 0;
|
||||
}
|
||||
|
||||
PositionType LocalFileStream::Position() {
|
||||
return ftell(this->file);
|
||||
}
|
||||
|
||||
bool LocalFileStream::Eof() {
|
||||
return feof(this->file) != 0;
|
||||
}
|
||||
|
||||
long LocalFileStream::Length() {
|
||||
return this->filesize;
|
||||
}
|
||||
|
||||
const char* LocalFileStream::Type() {
|
||||
return this->extension.c_str();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,142 +30,142 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/library/LibraryFactory.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/support/Preferences.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
LibraryFactory& LibraryFactory::Instance() {
|
||||
typedef std::shared_ptr<LibraryFactory> InstanceType;
|
||||
static InstanceType sInstance(new LibraryFactory());
|
||||
return *sInstance;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
//////////////////////////////////////////
|
||||
LibraryFactory::LibraryFactory() {
|
||||
// Connect to the settings.db
|
||||
std::string dataDir = GetDataDirectory();
|
||||
std::string dbFile = GetDataDirectory() + "settings.db";
|
||||
musik::core::db::Connection db;
|
||||
db.Open(dbFile.c_str(), 0, 128);
|
||||
|
||||
Preferences::CreateDB(db);
|
||||
|
||||
// Get the libraries
|
||||
db::Statement stmtGetLibs("SELECT id, name, type FROM libraries ORDER BY id", db);
|
||||
|
||||
while(stmtGetLibs.Step() == db::Row) {
|
||||
int id = stmtGetLibs.ColumnInt(0);
|
||||
std::string name = stmtGetLibs.ColumnText(1);
|
||||
int type = stmtGetLibs.ColumnInt(2);
|
||||
this->AddLibrary(id, type, name);
|
||||
}
|
||||
|
||||
// If there are no libraries, add a LocalLibrary
|
||||
if (this->libraries.empty()) {
|
||||
this->CreateLibrary("Local Library", LocalLibrary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LibraryFactory::~LibraryFactory() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Add a new library to the LibraryFactory
|
||||
///
|
||||
///\param name
|
||||
///Identifier of library. Need to be a unique name.
|
||||
///
|
||||
///\param type
|
||||
///Type of library. See LibraryFactory::Types
|
||||
///
|
||||
///\param sendEvent
|
||||
///Send the LibrariesUpdated when library has been added?
|
||||
///
|
||||
///\param startup
|
||||
///Start the library when added
|
||||
///
|
||||
///\returns
|
||||
///LibraryPtr of the added library. (NULL pointer on failure)
|
||||
//////////////////////////////////////////
|
||||
LibraryPtr LibraryFactory::AddLibrary(int id, int type, const std::string& name)
|
||||
{
|
||||
LibraryPtr library = library::LocalLibrary::Create(name, id);
|
||||
|
||||
if (library) {
|
||||
this->libraries.push_back(library);
|
||||
this->libraryMap[id] = library;
|
||||
this->LibrariesUpdated();
|
||||
}
|
||||
|
||||
return library;
|
||||
}
|
||||
|
||||
void LibraryFactory::Shutdown() {
|
||||
Instance().libraries.clear();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Create a new Library
|
||||
///
|
||||
///\param name
|
||||
///Identifier of library. Need to be a unique name.
|
||||
///
|
||||
///\param type
|
||||
///Type of library. See LibraryFactory::Types
|
||||
///
|
||||
///\param startup
|
||||
///Start the library when added
|
||||
///
|
||||
///\returns
|
||||
///LibraryPtr of the added library. (NULL pointer on failure)
|
||||
//////////////////////////////////////////
|
||||
LibraryPtr LibraryFactory::CreateLibrary(const std::string& name, int type) {
|
||||
// Connect to the settings.db
|
||||
std::string dataDir = GetDataDirectory();
|
||||
std::string dbFile = GetDataDirectory() + "settings.db";
|
||||
musik::core::db::Connection db;
|
||||
db.Open(dbFile.c_str(), 0, 128);
|
||||
|
||||
db::Statement stmtInsert("INSERT OR FAIL INTO libraries (name,type) VALUES (?,?)", db);
|
||||
stmtInsert.BindText(0, name);
|
||||
stmtInsert.BindInt(1, type);
|
||||
|
||||
if (stmtInsert.Step() == db::Done) {
|
||||
return this->AddLibrary(db.LastInsertedId(), type, name);
|
||||
}
|
||||
|
||||
return LibraryPtr();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the vector with all current libraries
|
||||
//////////////////////////////////////////
|
||||
LibraryFactory::LibraryVector& LibraryFactory::Libraries(){
|
||||
return LibraryFactory::Instance().libraries;
|
||||
}
|
||||
|
||||
LibraryPtr LibraryFactory::GetLibrary(int identifier){
|
||||
if (identifier) {
|
||||
LibraryMap::iterator lib = this->libraryMap.find(identifier);
|
||||
if (lib != this->libraryMap.end()) {
|
||||
return lib->second;
|
||||
}
|
||||
}
|
||||
return LibraryPtr();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/library/LibraryFactory.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/support/Preferences.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
LibraryFactory& LibraryFactory::Instance() {
|
||||
typedef std::shared_ptr<LibraryFactory> InstanceType;
|
||||
static InstanceType sInstance(new LibraryFactory());
|
||||
return *sInstance;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Constructor
|
||||
//////////////////////////////////////////
|
||||
LibraryFactory::LibraryFactory() {
|
||||
// Connect to the settings.db
|
||||
std::string dataDir = GetDataDirectory();
|
||||
std::string dbFile = GetDataDirectory() + "settings.db";
|
||||
musik::core::db::Connection db;
|
||||
db.Open(dbFile.c_str(), 0, 128);
|
||||
|
||||
Preferences::CreateDB(db);
|
||||
|
||||
// Get the libraries
|
||||
db::Statement stmtGetLibs("SELECT id, name, type FROM libraries ORDER BY id", db);
|
||||
|
||||
while(stmtGetLibs.Step() == db::Row) {
|
||||
int id = stmtGetLibs.ColumnInt(0);
|
||||
std::string name = stmtGetLibs.ColumnText(1);
|
||||
int type = stmtGetLibs.ColumnInt(2);
|
||||
this->AddLibrary(id, type, name);
|
||||
}
|
||||
|
||||
// If there are no libraries, add a LocalLibrary
|
||||
if (this->libraries.empty()) {
|
||||
this->CreateLibrary("Local Library", LocalLibrary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LibraryFactory::~LibraryFactory() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Add a new library to the LibraryFactory
|
||||
///
|
||||
///\param name
|
||||
///Identifier of library. Need to be a unique name.
|
||||
///
|
||||
///\param type
|
||||
///Type of library. See LibraryFactory::Types
|
||||
///
|
||||
///\param sendEvent
|
||||
///Send the LibrariesUpdated when library has been added?
|
||||
///
|
||||
///\param startup
|
||||
///Start the library when added
|
||||
///
|
||||
///\returns
|
||||
///LibraryPtr of the added library. (NULL pointer on failure)
|
||||
//////////////////////////////////////////
|
||||
LibraryPtr LibraryFactory::AddLibrary(int id, int type, const std::string& name)
|
||||
{
|
||||
LibraryPtr library = library::LocalLibrary::Create(name, id);
|
||||
|
||||
if (library) {
|
||||
this->libraries.push_back(library);
|
||||
this->libraryMap[id] = library;
|
||||
this->LibrariesUpdated();
|
||||
}
|
||||
|
||||
return library;
|
||||
}
|
||||
|
||||
void LibraryFactory::Shutdown() {
|
||||
Instance().libraries.clear();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Create a new Library
|
||||
///
|
||||
///\param name
|
||||
///Identifier of library. Need to be a unique name.
|
||||
///
|
||||
///\param type
|
||||
///Type of library. See LibraryFactory::Types
|
||||
///
|
||||
///\param startup
|
||||
///Start the library when added
|
||||
///
|
||||
///\returns
|
||||
///LibraryPtr of the added library. (NULL pointer on failure)
|
||||
//////////////////////////////////////////
|
||||
LibraryPtr LibraryFactory::CreateLibrary(const std::string& name, int type) {
|
||||
// Connect to the settings.db
|
||||
std::string dataDir = GetDataDirectory();
|
||||
std::string dbFile = GetDataDirectory() + "settings.db";
|
||||
musik::core::db::Connection db;
|
||||
db.Open(dbFile.c_str(), 0, 128);
|
||||
|
||||
db::Statement stmtInsert("INSERT OR FAIL INTO libraries (name,type) VALUES (?,?)", db);
|
||||
stmtInsert.BindText(0, name);
|
||||
stmtInsert.BindInt(1, type);
|
||||
|
||||
if (stmtInsert.Step() == db::Done) {
|
||||
return this->AddLibrary(db.LastInsertedId(), type, name);
|
||||
}
|
||||
|
||||
return LibraryPtr();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the vector with all current libraries
|
||||
//////////////////////////////////////////
|
||||
LibraryFactory::LibraryVector& LibraryFactory::Libraries(){
|
||||
return LibraryFactory::Instance().libraries;
|
||||
}
|
||||
|
||||
LibraryPtr LibraryFactory::GetLibrary(int identifier){
|
||||
if (identifier) {
|
||||
LibraryMap::iterator lib = this->libraryMap.find(identifier);
|
||||
if (lib != this->libraryMap.end()) {
|
||||
return lib->second;
|
||||
}
|
||||
}
|
||||
return LibraryPtr();
|
||||
}
|
||||
|
||||
|
@ -30,358 +30,358 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/library/LocalLibrary.h>
|
||||
#include <core/config.h>
|
||||
#include <core/library/query/QueryBase.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/support/Preferences.h>
|
||||
#include <core/library/Indexer.h>
|
||||
#include <core/debug.h>
|
||||
|
||||
static const std::string TAG = "LocalLibrary";
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::library;
|
||||
|
||||
#define VERBOSE_LOGGING 0
|
||||
|
||||
LibraryPtr LocalLibrary::Create(std::string name, int id) {
|
||||
LibraryPtr lib(new LocalLibrary(name, id));
|
||||
return lib;
|
||||
}
|
||||
|
||||
LocalLibrary::LocalLibrary(std::string name,int id)
|
||||
: name(name)
|
||||
, id(id)
|
||||
, exit(false) {
|
||||
this->identifier = boost::lexical_cast<std::string>(id);
|
||||
|
||||
Preferences prefs("Library");
|
||||
|
||||
this->db.Open(
|
||||
this->GetDatabaseFilename().c_str(),
|
||||
0,
|
||||
prefs.GetInt("DatabaseCache",
|
||||
4096));
|
||||
|
||||
LocalLibrary::CreateDatabase(this->db);
|
||||
|
||||
this->indexer = new core::Indexer(
|
||||
this->GetLibraryDirectory(),
|
||||
this->GetDatabaseFilename());
|
||||
|
||||
this->thread = new boost::thread(boost::bind(&LocalLibrary::ThreadProc, this));
|
||||
}
|
||||
|
||||
LocalLibrary::~LocalLibrary() {
|
||||
this->Exit();
|
||||
this->thread->join();
|
||||
this->threads.join_all();
|
||||
delete this->thread;
|
||||
delete this->indexer;
|
||||
}
|
||||
|
||||
int LocalLibrary::Id() {
|
||||
return this->id;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Name of the library
|
||||
//////////////////////////////////////////
|
||||
const std::string& LocalLibrary::Name() {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the directory-location of the library where you may store extra files.
|
||||
///
|
||||
///\returns
|
||||
///String with the path
|
||||
///
|
||||
///The library directory is a directory where you may store
|
||||
///the librarys database and other files like thumbnail cache.
|
||||
///In a win32 environment this path will be located in the users
|
||||
///$APPDATA/mC2/"identifier"/
|
||||
///where the identifier is set in the library itself.
|
||||
///
|
||||
///\remarks
|
||||
///If the directory does not exist, this method will create it.
|
||||
//////////////////////////////////////////
|
||||
std::string LocalLibrary::GetLibraryDirectory() {
|
||||
std::string directory(musik::core::GetDataDirectory());
|
||||
|
||||
if (!this->identifier.empty()) {
|
||||
directory.append(this->identifier + "/" );
|
||||
}
|
||||
|
||||
boost::filesystem::path dir(directory);
|
||||
if(!boost::filesystem::exists(dir)){
|
||||
boost::filesystem::create_directories(dir);
|
||||
}
|
||||
|
||||
directory = dir.string();
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
std::string LocalLibrary::GetDatabaseFilename() {
|
||||
return this->GetLibraryDirectory() + "musik.db";
|
||||
}
|
||||
|
||||
int LocalLibrary::Enqueue(QueryPtr query, unsigned int options) {
|
||||
boost::recursive_mutex::scoped_lock l(this->mutex);
|
||||
|
||||
queryQueue.push_back(query);
|
||||
queueCondition.notify_all();
|
||||
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, "query '" + query->Name() + "' enqueued");
|
||||
}
|
||||
|
||||
return query->GetId();
|
||||
}
|
||||
|
||||
bool LocalLibrary::Exited() {
|
||||
boost::recursive_mutex::scoped_lock lock(this->mutex);
|
||||
return this->exit;
|
||||
}
|
||||
|
||||
void LocalLibrary::Exit() {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->mutex);
|
||||
this->exit = true;
|
||||
}
|
||||
|
||||
/* kick sleeping threads back to the top of the loop */
|
||||
this->queueCondition.notify_all();
|
||||
}
|
||||
|
||||
QueryPtr LocalLibrary::GetNextQuery() {
|
||||
if (queryQueue.size()) {
|
||||
QueryPtr front = queryQueue.front();
|
||||
queryQueue.pop_front();
|
||||
return front;
|
||||
}
|
||||
|
||||
return QueryPtr();
|
||||
}
|
||||
|
||||
void LocalLibrary::ThreadProc() {
|
||||
while (!this->Exited()) {
|
||||
QueryPtr query;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->mutex);
|
||||
query = GetNextQuery();
|
||||
|
||||
while (!query && !this->Exited()) {
|
||||
this->queueCondition.wait(lock);
|
||||
query = GetNextQuery();
|
||||
}
|
||||
}
|
||||
|
||||
if (query) {
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, "query '" + query->Name() + "' running");
|
||||
}
|
||||
|
||||
query->Run(this->db);
|
||||
this->QueryCompleted(query);
|
||||
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, boost::str(boost::format(
|
||||
"query '%1%' finished with status=%2%")
|
||||
% query->Name()
|
||||
% query->GetStatus()));
|
||||
}
|
||||
|
||||
query.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
musik::core::IIndexer* LocalLibrary::Indexer() {
|
||||
return this->indexer;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Helper method to determin what metakeys are "static"
|
||||
//////////////////////////////////////////
|
||||
bool LocalLibrary::IsStaticMetaKey(std::string &metakey){
|
||||
static std::set<std::string> staticMetaKeys;
|
||||
|
||||
if (staticMetaKeys.empty()) {
|
||||
staticMetaKeys.insert("track");
|
||||
staticMetaKeys.insert("disc");
|
||||
staticMetaKeys.insert("bpm");
|
||||
staticMetaKeys.insert("duration");
|
||||
staticMetaKeys.insert("filesize");
|
||||
staticMetaKeys.insert("year");
|
||||
staticMetaKeys.insert("title");
|
||||
staticMetaKeys.insert("filename");
|
||||
staticMetaKeys.insert("filetime");
|
||||
}
|
||||
|
||||
return staticMetaKeys.find(metakey) != staticMetaKeys.end();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Helper method to determine what metakeys that have a special many to one relation
|
||||
//////////////////////////////////////////
|
||||
bool LocalLibrary::IsSpecialMTOMetaKey(std::string &metakey){
|
||||
static std::set<std::string> specialMTOMetaKeys;
|
||||
|
||||
if (specialMTOMetaKeys.empty()) {
|
||||
specialMTOMetaKeys.insert("album");
|
||||
specialMTOMetaKeys.insert("visual_genre");
|
||||
specialMTOMetaKeys.insert("visual_artist");
|
||||
}
|
||||
|
||||
return specialMTOMetaKeys.find(metakey)!=specialMTOMetaKeys.end();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Helper method to determine what metakeys that have a special many to meny relation
|
||||
//////////////////////////////////////////
|
||||
bool LocalLibrary::IsSpecialMTMMetaKey(std::string &metakey) {
|
||||
static std::set<std::string> specialMTMMetaKeys;
|
||||
|
||||
if (specialMTMMetaKeys.empty()) {
|
||||
specialMTMMetaKeys.insert("artist");
|
||||
specialMTMMetaKeys.insert("genre");
|
||||
}
|
||||
|
||||
return specialMTMMetaKeys.find(metakey) != specialMTMMetaKeys.end();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Create all tables, indexes, etc in the database.
|
||||
///
|
||||
///This will assume that the database has been initialized.
|
||||
//////////////////////////////////////////
|
||||
void LocalLibrary::CreateDatabase(db::Connection &db){
|
||||
// Create the tracks-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS tracks ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"track INTEGER DEFAULT 0,"
|
||||
"disc TEXT DEFAULT '1',"
|
||||
"bpm REAL DEFAULT 0,"
|
||||
"duration INTEGER DEFAULT 0,"
|
||||
"filesize INTEGER DEFAULT 0,"
|
||||
"year INTEGER DEFAULT 0,"
|
||||
"visual_genre_id INTEGER DEFAULT 0,"
|
||||
"visual_artist_id INTEGER DEFAULT 0,"
|
||||
"album_artist_id INTEGER DEFAULT 0,"
|
||||
"path_id INTEGER,"
|
||||
"album_id INTEGER DEFAULT 0,"
|
||||
"title TEXT default '',"
|
||||
"filename TEXT default '',"
|
||||
"filetime INTEGER DEFAULT 0,"
|
||||
"thumbnail_id INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the genres-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS genres ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"aggregated INTEGER DEFAULT 0,"
|
||||
"sort_order INTEGER DEFAULT 0)");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS track_genres ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"genre_id INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the artists-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS artists ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"aggregated INTEGER DEFAULT 0,"
|
||||
"sort_order INTEGER DEFAULT 0)");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS track_artists ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"artist_id INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the meta-tables
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS meta_keys ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT)");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS meta_values ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"meta_key_id INTEGER DEFAULT 0,"
|
||||
"sort_order INTEGER DEFAULT 0,"
|
||||
"content TEXT)");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS track_meta ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"meta_value_id INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the albums-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS albums ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"thumbnail_id INTEGER default 0,"
|
||||
"sort_order INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the paths-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS paths ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"path TEXT default ''"
|
||||
")");
|
||||
|
||||
// Create the thumbnails table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS thumbnails ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"filename TEXT default '',"
|
||||
"filesize INTEGER DEFAULT 0,"
|
||||
"checksum INTEGER DEFAULT 0"
|
||||
")");
|
||||
|
||||
// Create the playlists
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS playlists ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"user_id INTEGER default 0"
|
||||
")");
|
||||
|
||||
// Create the playlist_tracks table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS playlist_tracks ("
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"playlist_id INTEGER DEFAULT 0,"
|
||||
"sort_order INTEGER DEFAULT 0"
|
||||
")");
|
||||
|
||||
// INDEXES
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS users_index ON users (login)");
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS paths_index ON paths (path)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS genre_index ON genres (sort_order)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS artist_index ON artists (sort_order)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS album_index ON albums (sort_order)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS thumbnail_index ON thumbnails (filesize)");
|
||||
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index1 ON track_genres (track_id,genre_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index2 ON track_genres (genre_id,track_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackartist_index1 ON track_artists (track_id,artist_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackartist_index2 ON track_artists (artist_id,track_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackmeta_index1 ON track_meta (track_id,meta_value_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackmeta_index2 ON track_meta (meta_value_id,track_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS metakey_index1 ON meta_keys (name)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS metavalues_index1 ON meta_values (meta_key_id)");
|
||||
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS playlist_index ON playlist_tracks (playlist_id,sort_order)");
|
||||
|
||||
db.Analyze();
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/library/LocalLibrary.h>
|
||||
#include <core/config.h>
|
||||
#include <core/library/query/QueryBase.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/support/Preferences.h>
|
||||
#include <core/library/Indexer.h>
|
||||
#include <core/debug.h>
|
||||
|
||||
static const std::string TAG = "LocalLibrary";
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::library;
|
||||
|
||||
#define VERBOSE_LOGGING 0
|
||||
|
||||
LibraryPtr LocalLibrary::Create(std::string name, int id) {
|
||||
LibraryPtr lib(new LocalLibrary(name, id));
|
||||
return lib;
|
||||
}
|
||||
|
||||
LocalLibrary::LocalLibrary(std::string name,int id)
|
||||
: name(name)
|
||||
, id(id)
|
||||
, exit(false) {
|
||||
this->identifier = boost::lexical_cast<std::string>(id);
|
||||
|
||||
Preferences prefs("Library");
|
||||
|
||||
this->db.Open(
|
||||
this->GetDatabaseFilename().c_str(),
|
||||
0,
|
||||
prefs.GetInt("DatabaseCache",
|
||||
4096));
|
||||
|
||||
LocalLibrary::CreateDatabase(this->db);
|
||||
|
||||
this->indexer = new core::Indexer(
|
||||
this->GetLibraryDirectory(),
|
||||
this->GetDatabaseFilename());
|
||||
|
||||
this->thread = new boost::thread(boost::bind(&LocalLibrary::ThreadProc, this));
|
||||
}
|
||||
|
||||
LocalLibrary::~LocalLibrary() {
|
||||
this->Exit();
|
||||
this->thread->join();
|
||||
this->threads.join_all();
|
||||
delete this->thread;
|
||||
delete this->indexer;
|
||||
}
|
||||
|
||||
int LocalLibrary::Id() {
|
||||
return this->id;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Name of the library
|
||||
//////////////////////////////////////////
|
||||
const std::string& LocalLibrary::Name() {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the directory-location of the library where you may store extra files.
|
||||
///
|
||||
///\returns
|
||||
///String with the path
|
||||
///
|
||||
///The library directory is a directory where you may store
|
||||
///the librarys database and other files like thumbnail cache.
|
||||
///In a win32 environment this path will be located in the users
|
||||
///$APPDATA/mC2/"identifier"/
|
||||
///where the identifier is set in the library itself.
|
||||
///
|
||||
///\remarks
|
||||
///If the directory does not exist, this method will create it.
|
||||
//////////////////////////////////////////
|
||||
std::string LocalLibrary::GetLibraryDirectory() {
|
||||
std::string directory(musik::core::GetDataDirectory());
|
||||
|
||||
if (!this->identifier.empty()) {
|
||||
directory.append(this->identifier + "/" );
|
||||
}
|
||||
|
||||
boost::filesystem::path dir(directory);
|
||||
if(!boost::filesystem::exists(dir)){
|
||||
boost::filesystem::create_directories(dir);
|
||||
}
|
||||
|
||||
directory = dir.string();
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
std::string LocalLibrary::GetDatabaseFilename() {
|
||||
return this->GetLibraryDirectory() + "musik.db";
|
||||
}
|
||||
|
||||
int LocalLibrary::Enqueue(QueryPtr query, unsigned int options) {
|
||||
boost::recursive_mutex::scoped_lock l(this->mutex);
|
||||
|
||||
queryQueue.push_back(query);
|
||||
queueCondition.notify_all();
|
||||
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, "query '" + query->Name() + "' enqueued");
|
||||
}
|
||||
|
||||
return query->GetId();
|
||||
}
|
||||
|
||||
bool LocalLibrary::Exited() {
|
||||
boost::recursive_mutex::scoped_lock lock(this->mutex);
|
||||
return this->exit;
|
||||
}
|
||||
|
||||
void LocalLibrary::Exit() {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->mutex);
|
||||
this->exit = true;
|
||||
}
|
||||
|
||||
/* kick sleeping threads back to the top of the loop */
|
||||
this->queueCondition.notify_all();
|
||||
}
|
||||
|
||||
QueryPtr LocalLibrary::GetNextQuery() {
|
||||
if (queryQueue.size()) {
|
||||
QueryPtr front = queryQueue.front();
|
||||
queryQueue.pop_front();
|
||||
return front;
|
||||
}
|
||||
|
||||
return QueryPtr();
|
||||
}
|
||||
|
||||
void LocalLibrary::ThreadProc() {
|
||||
while (!this->Exited()) {
|
||||
QueryPtr query;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->mutex);
|
||||
query = GetNextQuery();
|
||||
|
||||
while (!query && !this->Exited()) {
|
||||
this->queueCondition.wait(lock);
|
||||
query = GetNextQuery();
|
||||
}
|
||||
}
|
||||
|
||||
if (query) {
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, "query '" + query->Name() + "' running");
|
||||
}
|
||||
|
||||
query->Run(this->db);
|
||||
this->QueryCompleted(query);
|
||||
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, boost::str(boost::format(
|
||||
"query '%1%' finished with status=%2%")
|
||||
% query->Name()
|
||||
% query->GetStatus()));
|
||||
}
|
||||
|
||||
query.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
musik::core::IIndexer* LocalLibrary::Indexer() {
|
||||
return this->indexer;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Helper method to determin what metakeys are "static"
|
||||
//////////////////////////////////////////
|
||||
bool LocalLibrary::IsStaticMetaKey(std::string &metakey){
|
||||
static std::set<std::string> staticMetaKeys;
|
||||
|
||||
if (staticMetaKeys.empty()) {
|
||||
staticMetaKeys.insert("track");
|
||||
staticMetaKeys.insert("disc");
|
||||
staticMetaKeys.insert("bpm");
|
||||
staticMetaKeys.insert("duration");
|
||||
staticMetaKeys.insert("filesize");
|
||||
staticMetaKeys.insert("year");
|
||||
staticMetaKeys.insert("title");
|
||||
staticMetaKeys.insert("filename");
|
||||
staticMetaKeys.insert("filetime");
|
||||
}
|
||||
|
||||
return staticMetaKeys.find(metakey) != staticMetaKeys.end();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Helper method to determine what metakeys that have a special many to one relation
|
||||
//////////////////////////////////////////
|
||||
bool LocalLibrary::IsSpecialMTOMetaKey(std::string &metakey){
|
||||
static std::set<std::string> specialMTOMetaKeys;
|
||||
|
||||
if (specialMTOMetaKeys.empty()) {
|
||||
specialMTOMetaKeys.insert("album");
|
||||
specialMTOMetaKeys.insert("visual_genre");
|
||||
specialMTOMetaKeys.insert("visual_artist");
|
||||
}
|
||||
|
||||
return specialMTOMetaKeys.find(metakey)!=specialMTOMetaKeys.end();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Helper method to determine what metakeys that have a special many to meny relation
|
||||
//////////////////////////////////////////
|
||||
bool LocalLibrary::IsSpecialMTMMetaKey(std::string &metakey) {
|
||||
static std::set<std::string> specialMTMMetaKeys;
|
||||
|
||||
if (specialMTMMetaKeys.empty()) {
|
||||
specialMTMMetaKeys.insert("artist");
|
||||
specialMTMMetaKeys.insert("genre");
|
||||
}
|
||||
|
||||
return specialMTMMetaKeys.find(metakey) != specialMTMMetaKeys.end();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Create all tables, indexes, etc in the database.
|
||||
///
|
||||
///This will assume that the database has been initialized.
|
||||
//////////////////////////////////////////
|
||||
void LocalLibrary::CreateDatabase(db::Connection &db){
|
||||
// Create the tracks-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS tracks ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"track INTEGER DEFAULT 0,"
|
||||
"disc TEXT DEFAULT '1',"
|
||||
"bpm REAL DEFAULT 0,"
|
||||
"duration INTEGER DEFAULT 0,"
|
||||
"filesize INTEGER DEFAULT 0,"
|
||||
"year INTEGER DEFAULT 0,"
|
||||
"visual_genre_id INTEGER DEFAULT 0,"
|
||||
"visual_artist_id INTEGER DEFAULT 0,"
|
||||
"album_artist_id INTEGER DEFAULT 0,"
|
||||
"path_id INTEGER,"
|
||||
"album_id INTEGER DEFAULT 0,"
|
||||
"title TEXT default '',"
|
||||
"filename TEXT default '',"
|
||||
"filetime INTEGER DEFAULT 0,"
|
||||
"thumbnail_id INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the genres-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS genres ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"aggregated INTEGER DEFAULT 0,"
|
||||
"sort_order INTEGER DEFAULT 0)");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS track_genres ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"genre_id INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the artists-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS artists ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"aggregated INTEGER DEFAULT 0,"
|
||||
"sort_order INTEGER DEFAULT 0)");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS track_artists ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"artist_id INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the meta-tables
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS meta_keys ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT)");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS meta_values ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"meta_key_id INTEGER DEFAULT 0,"
|
||||
"sort_order INTEGER DEFAULT 0,"
|
||||
"content TEXT)");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS track_meta ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"meta_value_id INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the albums-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS albums ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"thumbnail_id INTEGER default 0,"
|
||||
"sort_order INTEGER DEFAULT 0)");
|
||||
|
||||
// Create the paths-table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS paths ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"path TEXT default ''"
|
||||
")");
|
||||
|
||||
// Create the thumbnails table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS thumbnails ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"filename TEXT default '',"
|
||||
"filesize INTEGER DEFAULT 0,"
|
||||
"checksum INTEGER DEFAULT 0"
|
||||
")");
|
||||
|
||||
// Create the playlists
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS playlists ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT default '',"
|
||||
"user_id INTEGER default 0"
|
||||
")");
|
||||
|
||||
// Create the playlist_tracks table
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS playlist_tracks ("
|
||||
"track_id INTEGER DEFAULT 0,"
|
||||
"playlist_id INTEGER DEFAULT 0,"
|
||||
"sort_order INTEGER DEFAULT 0"
|
||||
")");
|
||||
|
||||
// INDEXES
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS users_index ON users (login)");
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS paths_index ON paths (path)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS genre_index ON genres (sort_order)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS artist_index ON artists (sort_order)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS album_index ON albums (sort_order)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS thumbnail_index ON thumbnails (filesize)");
|
||||
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index1 ON track_genres (track_id,genre_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index2 ON track_genres (genre_id,track_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackartist_index1 ON track_artists (track_id,artist_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackartist_index2 ON track_artists (artist_id,track_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackmeta_index1 ON track_meta (track_id,meta_value_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS trackmeta_index2 ON track_meta (meta_value_id,track_id)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS metakey_index1 ON meta_keys (name)");
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS metavalues_index1 ON meta_values (meta_key_id)");
|
||||
|
||||
db.Execute("CREATE INDEX IF NOT EXISTS playlist_index ON playlist_tracks (playlist_id,sort_order)");
|
||||
|
||||
db.Analyze();
|
||||
}
|
@ -1,50 +1,50 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/library/metadata/MetadataKeyValue.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
MetadataKeyValue::MetadataKeyValue(const DBID newId, const char *value)
|
||||
: id (newId) {
|
||||
if (value) {
|
||||
this->value = value;
|
||||
}
|
||||
}
|
||||
|
||||
MetadataKeyValue::~MetadataKeyValue() {
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License Agreement:
|
||||
//
|
||||
// The following are Copyright © 2008, Daniel Önnerby
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/library/metadata/MetadataKeyValue.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
MetadataKeyValue::MetadataKeyValue(const DBID newId, const char *value)
|
||||
: id (newId) {
|
||||
if (value) {
|
||||
this->value = value;
|
||||
}
|
||||
}
|
||||
|
||||
MetadataKeyValue::~MetadataKeyValue() {
|
||||
}
|
||||
|
@ -30,22 +30,22 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/library/metadata/MetadataValue.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
MetadataValue::MetadataValue(const DBID newId, const char *value)
|
||||
: id(newId) {
|
||||
if (value) {
|
||||
this->value = value;
|
||||
}
|
||||
}
|
||||
|
||||
MetadataValue::MetadataValue() {
|
||||
}
|
||||
|
||||
MetadataValue::~MetadataValue() {
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/library/metadata/MetadataValue.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
MetadataValue::MetadataValue(const DBID newId, const char *value)
|
||||
: id(newId) {
|
||||
if (value) {
|
||||
this->value = value;
|
||||
}
|
||||
}
|
||||
|
||||
MetadataValue::MetadataValue() {
|
||||
}
|
||||
|
||||
MetadataValue::~MetadataValue() {
|
||||
}
|
||||
|
@ -30,74 +30,74 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/library/query/QueryBase.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
#include <boost/atomic.hpp>
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::query;
|
||||
|
||||
static boost::atomic<int> nextId(0);
|
||||
|
||||
QueryBase::QueryBase()
|
||||
: status(0)
|
||||
, options(0)
|
||||
, queryId(0)
|
||||
, cancel(false) {
|
||||
this->queryId = nextId++;
|
||||
}
|
||||
|
||||
QueryBase::~QueryBase() {
|
||||
}
|
||||
|
||||
std::string QueryBase::Name() {
|
||||
return "QueryBase";
|
||||
}
|
||||
|
||||
bool QueryBase::Run(db::Connection &db) {
|
||||
this->SetStatus(Running);
|
||||
try {
|
||||
if (this->IsCanceled()) {
|
||||
this->SetStatus(Canceled);
|
||||
return true;
|
||||
}
|
||||
else if (OnRun(db)) {
|
||||
this->SetStatus(Finished);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
|
||||
this->SetStatus(Failed);
|
||||
return false;
|
||||
}
|
||||
|
||||
int QueryBase::GetStatus() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->status;
|
||||
}
|
||||
|
||||
void QueryBase::SetStatus(int status) {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
int QueryBase::GetId() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->queryId;
|
||||
}
|
||||
|
||||
int QueryBase::GetOptions() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->options;
|
||||
}
|
||||
|
||||
void QueryBase::SetOptions(int options) {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
this->options = options;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/library/query/QueryBase.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
#include <boost/atomic.hpp>
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::query;
|
||||
|
||||
static boost::atomic<int> nextId(0);
|
||||
|
||||
QueryBase::QueryBase()
|
||||
: status(0)
|
||||
, options(0)
|
||||
, queryId(0)
|
||||
, cancel(false) {
|
||||
this->queryId = nextId++;
|
||||
}
|
||||
|
||||
QueryBase::~QueryBase() {
|
||||
}
|
||||
|
||||
std::string QueryBase::Name() {
|
||||
return "QueryBase";
|
||||
}
|
||||
|
||||
bool QueryBase::Run(db::Connection &db) {
|
||||
this->SetStatus(Running);
|
||||
try {
|
||||
if (this->IsCanceled()) {
|
||||
this->SetStatus(Canceled);
|
||||
return true;
|
||||
}
|
||||
else if (OnRun(db)) {
|
||||
this->SetStatus(Finished);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
|
||||
this->SetStatus(Failed);
|
||||
return false;
|
||||
}
|
||||
|
||||
int QueryBase::GetStatus() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->status;
|
||||
}
|
||||
|
||||
void QueryBase::SetStatus(int status) {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
int QueryBase::GetId() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->queryId;
|
||||
}
|
||||
|
||||
int QueryBase::GetOptions() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->options;
|
||||
}
|
||||
|
||||
void QueryBase::SetOptions(int options) {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
this->options = options;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,186 +30,186 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/library/track/LibraryTrack.h>
|
||||
#include <core/library/LibraryFactory.h>
|
||||
|
||||
#include <core/support/Common.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <core/db/Statement.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
LibraryTrack::LibraryTrack()
|
||||
: id(0)
|
||||
, libraryId(0) {
|
||||
}
|
||||
|
||||
LibraryTrack::LibraryTrack(DBID id, int libraryId)
|
||||
: id(id)
|
||||
, libraryId(libraryId) {
|
||||
}
|
||||
|
||||
LibraryTrack::LibraryTrack(DBID id, musik::core::LibraryPtr library)
|
||||
: id(id)
|
||||
, libraryId(library->Id()) {
|
||||
}
|
||||
|
||||
LibraryTrack::~LibraryTrack() {
|
||||
}
|
||||
|
||||
std::string LibraryTrack::GetValue(const char* metakey) {
|
||||
boost::mutex::scoped_lock lock(this->data.mutex);
|
||||
MetadataMap::iterator metavalue = this->data.metadata.find(metakey);
|
||||
if (metavalue != this->data.metadata.end()) {
|
||||
return metavalue->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void LibraryTrack::SetValue(const char* metakey, const char* value) {
|
||||
boost::mutex::scoped_lock lock(this->data.mutex);
|
||||
this->data.metadata.insert(std::pair<std::string, std::string>(metakey,value));
|
||||
}
|
||||
|
||||
void LibraryTrack::ClearValue(const char* metakey) {
|
||||
boost::mutex::scoped_lock lock(this->data.mutex);
|
||||
this->data.metadata.erase(metakey);
|
||||
}
|
||||
|
||||
void LibraryTrack::SetThumbnail(const char *data, long size) {
|
||||
delete this->data.thumbnailData;
|
||||
this->data.thumbnailData = new char[size];
|
||||
this->data.thumbnailSize = size;
|
||||
|
||||
memcpy(this->data.thumbnailData, data, size);
|
||||
}
|
||||
|
||||
std::string LibraryTrack::URI() {
|
||||
return this->GetValue("filename");
|
||||
}
|
||||
|
||||
Track::MetadataIteratorRange LibraryTrack::GetValues(const char* metakey) {
|
||||
boost::mutex::scoped_lock lock(this->data.mutex);
|
||||
return this->data.metadata.equal_range(metakey);
|
||||
}
|
||||
|
||||
Track::MetadataIteratorRange LibraryTrack::GetAllValues() {
|
||||
return Track::MetadataIteratorRange(
|
||||
this->data.metadata.begin(),
|
||||
this->data.metadata.end());
|
||||
|
||||
return Track::MetadataIteratorRange();
|
||||
}
|
||||
|
||||
DBID LibraryTrack::Id() {
|
||||
return this->id;
|
||||
}
|
||||
|
||||
int LibraryTrack::LibraryId() {
|
||||
return this->libraryId;
|
||||
}
|
||||
|
||||
TrackPtr LibraryTrack::Copy() {
|
||||
return TrackPtr(new LibraryTrack(this->id, this->libraryId));
|
||||
}
|
||||
|
||||
bool LibraryTrack::Load(Track *target, db::Connection &db) {
|
||||
/* if no ID is specified, see if we can look one up by filename
|
||||
in the current database. */
|
||||
if (target->Id() == 0) {
|
||||
std::string path = target->GetValue("filename");
|
||||
|
||||
if (!path.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
db::Statement idFromFn(
|
||||
"SELECT id " \
|
||||
"FROM tracks " \
|
||||
"WHERE filename=? " \
|
||||
"LIMIT 1", db);
|
||||
|
||||
idFromFn.BindText(0, path.c_str());
|
||||
|
||||
if (idFromFn.Step() != db::Row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target->SetId(idFromFn.ColumnInt(0));
|
||||
}
|
||||
|
||||
db::Statement genresQuery(
|
||||
"SELECT g.name " \
|
||||
"FROM genres g, track_genres tg " \
|
||||
"WHERE tg.genre_id=g.id AND tg.track_id=? " \
|
||||
"ORDER BY tg.id", db);
|
||||
|
||||
db::Statement artistsQuery(
|
||||
"SELECT ar.name " \
|
||||
"FROM artists ar, track_artists ta " \
|
||||
"WHERE ta.artist_id=ar.id AND ta.track_id=? "\
|
||||
"ORDER BY ta.id", db);
|
||||
|
||||
db::Statement allMetadataQuery(
|
||||
"SELECT mv.content, mk.name " \
|
||||
"FROM meta_values mv, meta_keys mk, track_meta tm " \
|
||||
"WHERE tm.track_id=? AND tm.meta_value_id=mv.id AND mv.meta_key_id=mk.id " \
|
||||
"ORDER BY tm.id", db);
|
||||
|
||||
db::Statement trackQuery(
|
||||
"SELECT t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name, t.filetime " \
|
||||
"FROM tracks t, paths p, albums al " \
|
||||
"WHERE t.id=? AND t.album_id=al.id", db);
|
||||
|
||||
trackQuery.BindInt(0, target->Id());
|
||||
if (trackQuery.Step() == db::Row) {
|
||||
target->SetValue("track", trackQuery.ColumnText(0));
|
||||
target->SetValue("disc", trackQuery.ColumnText(1));
|
||||
target->SetValue("bpm", trackQuery.ColumnText(2));
|
||||
target->SetValue("duration", trackQuery.ColumnText(3));
|
||||
target->SetValue("filesize", trackQuery.ColumnText(4));
|
||||
target->SetValue("year", trackQuery.ColumnText(5));
|
||||
target->SetValue("title", trackQuery.ColumnText(6));
|
||||
target->SetValue("filename", trackQuery.ColumnText(7));
|
||||
target->SetValue("thumbnail_id", trackQuery.ColumnText(8));
|
||||
target->SetValue("album", trackQuery.ColumnText(9));
|
||||
target->SetValue("filetime", trackQuery.ColumnText(10));
|
||||
|
||||
genresQuery.BindInt(0, target->Id());
|
||||
while (genresQuery.Step() == db::Row) {
|
||||
target->SetValue("genre", genresQuery.ColumnText(0));
|
||||
}
|
||||
|
||||
artistsQuery.BindInt(0, target->Id());
|
||||
while (artistsQuery.Step() == db::Row) {
|
||||
target->SetValue("artist", artistsQuery.ColumnText(0));
|
||||
}
|
||||
|
||||
allMetadataQuery.BindInt(0, target->Id());
|
||||
while (allMetadataQuery.Step() == db::Row) {
|
||||
target->SetValue(allMetadataQuery.ColumnText(1), allMetadataQuery.ColumnText(0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
LibraryTrack::LibraryData::LibraryData()
|
||||
: thumbnailData(NULL)
|
||||
, thumbnailSize(0) {
|
||||
}
|
||||
|
||||
LibraryTrack::LibraryData::~LibraryData() {
|
||||
delete this->thumbnailData;
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/library/track/LibraryTrack.h>
|
||||
#include <core/library/LibraryFactory.h>
|
||||
|
||||
#include <core/support/Common.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <core/db/Statement.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
LibraryTrack::LibraryTrack()
|
||||
: id(0)
|
||||
, libraryId(0) {
|
||||
}
|
||||
|
||||
LibraryTrack::LibraryTrack(DBID id, int libraryId)
|
||||
: id(id)
|
||||
, libraryId(libraryId) {
|
||||
}
|
||||
|
||||
LibraryTrack::LibraryTrack(DBID id, musik::core::LibraryPtr library)
|
||||
: id(id)
|
||||
, libraryId(library->Id()) {
|
||||
}
|
||||
|
||||
LibraryTrack::~LibraryTrack() {
|
||||
}
|
||||
|
||||
std::string LibraryTrack::GetValue(const char* metakey) {
|
||||
boost::mutex::scoped_lock lock(this->data.mutex);
|
||||
MetadataMap::iterator metavalue = this->data.metadata.find(metakey);
|
||||
if (metavalue != this->data.metadata.end()) {
|
||||
return metavalue->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void LibraryTrack::SetValue(const char* metakey, const char* value) {
|
||||
boost::mutex::scoped_lock lock(this->data.mutex);
|
||||
this->data.metadata.insert(std::pair<std::string, std::string>(metakey,value));
|
||||
}
|
||||
|
||||
void LibraryTrack::ClearValue(const char* metakey) {
|
||||
boost::mutex::scoped_lock lock(this->data.mutex);
|
||||
this->data.metadata.erase(metakey);
|
||||
}
|
||||
|
||||
void LibraryTrack::SetThumbnail(const char *data, long size) {
|
||||
delete this->data.thumbnailData;
|
||||
this->data.thumbnailData = new char[size];
|
||||
this->data.thumbnailSize = size;
|
||||
|
||||
memcpy(this->data.thumbnailData, data, size);
|
||||
}
|
||||
|
||||
std::string LibraryTrack::URI() {
|
||||
return this->GetValue("filename");
|
||||
}
|
||||
|
||||
Track::MetadataIteratorRange LibraryTrack::GetValues(const char* metakey) {
|
||||
boost::mutex::scoped_lock lock(this->data.mutex);
|
||||
return this->data.metadata.equal_range(metakey);
|
||||
}
|
||||
|
||||
Track::MetadataIteratorRange LibraryTrack::GetAllValues() {
|
||||
return Track::MetadataIteratorRange(
|
||||
this->data.metadata.begin(),
|
||||
this->data.metadata.end());
|
||||
|
||||
return Track::MetadataIteratorRange();
|
||||
}
|
||||
|
||||
DBID LibraryTrack::Id() {
|
||||
return this->id;
|
||||
}
|
||||
|
||||
int LibraryTrack::LibraryId() {
|
||||
return this->libraryId;
|
||||
}
|
||||
|
||||
TrackPtr LibraryTrack::Copy() {
|
||||
return TrackPtr(new LibraryTrack(this->id, this->libraryId));
|
||||
}
|
||||
|
||||
bool LibraryTrack::Load(Track *target, db::Connection &db) {
|
||||
/* if no ID is specified, see if we can look one up by filename
|
||||
in the current database. */
|
||||
if (target->Id() == 0) {
|
||||
std::string path = target->GetValue("filename");
|
||||
|
||||
if (!path.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
db::Statement idFromFn(
|
||||
"SELECT id " \
|
||||
"FROM tracks " \
|
||||
"WHERE filename=? " \
|
||||
"LIMIT 1", db);
|
||||
|
||||
idFromFn.BindText(0, path.c_str());
|
||||
|
||||
if (idFromFn.Step() != db::Row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target->SetId(idFromFn.ColumnInt(0));
|
||||
}
|
||||
|
||||
db::Statement genresQuery(
|
||||
"SELECT g.name " \
|
||||
"FROM genres g, track_genres tg " \
|
||||
"WHERE tg.genre_id=g.id AND tg.track_id=? " \
|
||||
"ORDER BY tg.id", db);
|
||||
|
||||
db::Statement artistsQuery(
|
||||
"SELECT ar.name " \
|
||||
"FROM artists ar, track_artists ta " \
|
||||
"WHERE ta.artist_id=ar.id AND ta.track_id=? "\
|
||||
"ORDER BY ta.id", db);
|
||||
|
||||
db::Statement allMetadataQuery(
|
||||
"SELECT mv.content, mk.name " \
|
||||
"FROM meta_values mv, meta_keys mk, track_meta tm " \
|
||||
"WHERE tm.track_id=? AND tm.meta_value_id=mv.id AND mv.meta_key_id=mk.id " \
|
||||
"ORDER BY tm.id", db);
|
||||
|
||||
db::Statement trackQuery(
|
||||
"SELECT t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name, t.filetime " \
|
||||
"FROM tracks t, paths p, albums al " \
|
||||
"WHERE t.id=? AND t.album_id=al.id", db);
|
||||
|
||||
trackQuery.BindInt(0, target->Id());
|
||||
if (trackQuery.Step() == db::Row) {
|
||||
target->SetValue("track", trackQuery.ColumnText(0));
|
||||
target->SetValue("disc", trackQuery.ColumnText(1));
|
||||
target->SetValue("bpm", trackQuery.ColumnText(2));
|
||||
target->SetValue("duration", trackQuery.ColumnText(3));
|
||||
target->SetValue("filesize", trackQuery.ColumnText(4));
|
||||
target->SetValue("year", trackQuery.ColumnText(5));
|
||||
target->SetValue("title", trackQuery.ColumnText(6));
|
||||
target->SetValue("filename", trackQuery.ColumnText(7));
|
||||
target->SetValue("thumbnail_id", trackQuery.ColumnText(8));
|
||||
target->SetValue("album", trackQuery.ColumnText(9));
|
||||
target->SetValue("filetime", trackQuery.ColumnText(10));
|
||||
|
||||
genresQuery.BindInt(0, target->Id());
|
||||
while (genresQuery.Step() == db::Row) {
|
||||
target->SetValue("genre", genresQuery.ColumnText(0));
|
||||
}
|
||||
|
||||
artistsQuery.BindInt(0, target->Id());
|
||||
while (artistsQuery.Step() == db::Row) {
|
||||
target->SetValue("artist", artistsQuery.ColumnText(0));
|
||||
}
|
||||
|
||||
allMetadataQuery.BindInt(0, target->Id());
|
||||
while (allMetadataQuery.Step() == db::Row) {
|
||||
target->SetValue(allMetadataQuery.ColumnText(1), allMetadataQuery.ColumnText(0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
LibraryTrack::LibraryData::LibraryData()
|
||||
: thumbnailData(NULL)
|
||||
, thumbnailSize(0) {
|
||||
}
|
||||
|
||||
LibraryTrack::LibraryData::~LibraryData() {
|
||||
delete this->thumbnailData;
|
||||
}
|
@ -30,27 +30,27 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/library/track/Track.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
Track::~Track() {
|
||||
}
|
||||
|
||||
DBID Track::Id() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LibraryPtr Track::Library() {
|
||||
static LibraryPtr nullLibrary;
|
||||
return nullLibrary;
|
||||
}
|
||||
|
||||
int Track::LibraryId() {
|
||||
return 0;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/library/track/Track.h>
|
||||
#include <core/library/LocalLibrary.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
Track::~Track() {
|
||||
}
|
||||
|
||||
DBID Track::Id() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LibraryPtr Track::Library() {
|
||||
static LibraryPtr nullLibrary;
|
||||
return nullLibrary;
|
||||
}
|
||||
|
||||
int Track::LibraryId() {
|
||||
return 0;
|
||||
}
|
||||
|
@ -30,6 +30,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
@ -30,104 +30,104 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/playback/NonLibraryTrackHelper.h>
|
||||
#include <boost/bind.hpp>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <core/sdk/IMetadataReader.h>
|
||||
#include <core/io/DataStreamFactory.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
NonLibraryTrackHelper NonLibraryTrackHelper::sInstance;
|
||||
|
||||
NonLibraryTrackHelper::NonLibraryTrackHelper(void)
|
||||
: threadIsRunning(false) {
|
||||
}
|
||||
|
||||
NonLibraryTrackHelper::~NonLibraryTrackHelper(void) {
|
||||
}
|
||||
|
||||
NonLibraryTrackHelper& NonLibraryTrackHelper::Instance() {
|
||||
return NonLibraryTrackHelper::sInstance;
|
||||
}
|
||||
|
||||
void NonLibraryTrackHelper::ReadTrack(musik::core::TrackPtr track) {
|
||||
bool threadRunning = false;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->tracksToRead.push_back(TrackWeakPtr(track));
|
||||
threadRunning = this->threadIsRunning;
|
||||
}
|
||||
|
||||
if (!threadRunning) {
|
||||
if (this->helperThread) {
|
||||
this->helperThread->join();
|
||||
}
|
||||
|
||||
this->helperThread.reset(new boost::thread(
|
||||
boost::bind(&NonLibraryTrackHelper::ThreadLoop,this)));
|
||||
}
|
||||
}
|
||||
|
||||
void NonLibraryTrackHelper::ThreadLoop() {
|
||||
/* load all IMetadataReader plugins */
|
||||
typedef metadata::IMetadataReader PluginType;
|
||||
typedef PluginFactory::DestroyDeleter<PluginType> Deleter;
|
||||
typedef std::vector<std::shared_ptr<metadata::IMetadataReader> > MetadataReaderList;
|
||||
|
||||
MetadataReaderList metadataReaders =
|
||||
PluginFactory::Instance() .QueryInterface<PluginType, Deleter>("GetMetadataReader");
|
||||
|
||||
bool moreTracks = true;
|
||||
|
||||
while (moreTracks) {
|
||||
musik::core::TrackPtr track;
|
||||
|
||||
/* pop the next track, if one exists. */
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
if (!this->tracksToRead.empty()) {
|
||||
track = this->tracksToRead.front().lock();
|
||||
this->tracksToRead.pop_front();
|
||||
}
|
||||
|
||||
moreTracks = !this->tracksToRead.empty();
|
||||
|
||||
if (!moreTracks) {
|
||||
this->threadIsRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (track) {
|
||||
/* we only support local files. other URIs are ignored */
|
||||
if (musik::core::io::DataStreamFactory::IsLocalFileStream(track->URI().c_str())) {
|
||||
std::string url = track->URI();
|
||||
|
||||
std::string::size_type lastDot = url.find_last_of(".");
|
||||
if (lastDot != std::string::npos) {
|
||||
track->SetValue("extension", url.substr(lastDot + 1).c_str());
|
||||
}
|
||||
|
||||
/* see if we can find a MetadataReader plugin that supports this file */
|
||||
typedef MetadataReaderList::iterator Iterator;
|
||||
Iterator it = metadataReaders.begin();
|
||||
while (it != metadataReaders.end()) {
|
||||
if ((*it)->CanRead(track->GetValue("extension").c_str())) {
|
||||
(*it)->Read(url.c_str(), track.get());
|
||||
break;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
this->TrackMetadataUpdated(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/playback/NonLibraryTrackHelper.h>
|
||||
#include <boost/bind.hpp>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <core/sdk/IMetadataReader.h>
|
||||
#include <core/io/DataStreamFactory.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
NonLibraryTrackHelper NonLibraryTrackHelper::sInstance;
|
||||
|
||||
NonLibraryTrackHelper::NonLibraryTrackHelper(void)
|
||||
: threadIsRunning(false) {
|
||||
}
|
||||
|
||||
NonLibraryTrackHelper::~NonLibraryTrackHelper(void) {
|
||||
}
|
||||
|
||||
NonLibraryTrackHelper& NonLibraryTrackHelper::Instance() {
|
||||
return NonLibraryTrackHelper::sInstance;
|
||||
}
|
||||
|
||||
void NonLibraryTrackHelper::ReadTrack(musik::core::TrackPtr track) {
|
||||
bool threadRunning = false;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
this->tracksToRead.push_back(TrackWeakPtr(track));
|
||||
threadRunning = this->threadIsRunning;
|
||||
}
|
||||
|
||||
if (!threadRunning) {
|
||||
if (this->helperThread) {
|
||||
this->helperThread->join();
|
||||
}
|
||||
|
||||
this->helperThread.reset(new boost::thread(
|
||||
boost::bind(&NonLibraryTrackHelper::ThreadLoop,this)));
|
||||
}
|
||||
}
|
||||
|
||||
void NonLibraryTrackHelper::ThreadLoop() {
|
||||
/* load all IMetadataReader plugins */
|
||||
typedef metadata::IMetadataReader PluginType;
|
||||
typedef PluginFactory::DestroyDeleter<PluginType> Deleter;
|
||||
typedef std::vector<std::shared_ptr<metadata::IMetadataReader> > MetadataReaderList;
|
||||
|
||||
MetadataReaderList metadataReaders =
|
||||
PluginFactory::Instance() .QueryInterface<PluginType, Deleter>("GetMetadataReader");
|
||||
|
||||
bool moreTracks = true;
|
||||
|
||||
while (moreTracks) {
|
||||
musik::core::TrackPtr track;
|
||||
|
||||
/* pop the next track, if one exists. */
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
if (!this->tracksToRead.empty()) {
|
||||
track = this->tracksToRead.front().lock();
|
||||
this->tracksToRead.pop_front();
|
||||
}
|
||||
|
||||
moreTracks = !this->tracksToRead.empty();
|
||||
|
||||
if (!moreTracks) {
|
||||
this->threadIsRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (track) {
|
||||
/* we only support local files. other URIs are ignored */
|
||||
if (musik::core::io::DataStreamFactory::IsLocalFileStream(track->URI().c_str())) {
|
||||
std::string url = track->URI();
|
||||
|
||||
std::string::size_type lastDot = url.find_last_of(".");
|
||||
if (lastDot != std::string::npos) {
|
||||
track->SetValue("extension", url.substr(lastDot + 1).c_str());
|
||||
}
|
||||
|
||||
/* see if we can find a MetadataReader plugin that supports this file */
|
||||
typedef MetadataReaderList::iterator Iterator;
|
||||
Iterator it = metadataReaders.begin();
|
||||
while (it != metadataReaders.end()) {
|
||||
if ((*it)->CanRead(track->GetValue("extension").c_str())) {
|
||||
(*it)->Read(url.c_str(), track.get());
|
||||
break;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
this->TrackMetadataUpdated(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,341 +30,341 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/playback/Transport.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <algorithm>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
using namespace musik::core::audio;
|
||||
|
||||
static std::string TAG = "Transport";
|
||||
|
||||
#define RESET_NEXT_PLAYER() \
|
||||
delete this->nextPlayer; \
|
||||
this->nextPlayer = NULL;
|
||||
|
||||
#define DEFER(x, y) \
|
||||
{ \
|
||||
boost::thread thread(boost::bind(x, this, y)); \
|
||||
thread.detach(); \
|
||||
}
|
||||
|
||||
static void stopPlayer(Player* p) {
|
||||
p->Stop();
|
||||
}
|
||||
|
||||
static void deletePlayer(Player* p) {
|
||||
delete p;
|
||||
}
|
||||
|
||||
Transport::Transport()
|
||||
: volume(1.0)
|
||||
, state(PlaybackStopped)
|
||||
, nextPlayer(NULL)
|
||||
, nextCanStart(false) {
|
||||
this->output = Player::CreateDefaultOutput();
|
||||
}
|
||||
|
||||
Transport::~Transport() {
|
||||
}
|
||||
|
||||
Transport::PlaybackState Transport::GetPlaybackState() {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void Transport::PrepareNextTrack(const std::string& trackUrl) {
|
||||
bool startNext = false;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
this->nextPlayer = new Player(trackUrl, this->output);
|
||||
startNext = this->nextCanStart;
|
||||
}
|
||||
|
||||
if (startNext) {
|
||||
this->StartWithPlayer(this->nextPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::Start(const std::string& url) {
|
||||
musik::debug::info(TAG, "we were asked to start the track at " + url);
|
||||
|
||||
Player* newPlayer = new Player(url, this->output);
|
||||
musik::debug::info(TAG, "Player created successfully");
|
||||
|
||||
this->StartWithPlayer(newPlayer);
|
||||
}
|
||||
|
||||
void Transport::StartWithPlayer(Player* newPlayer) {
|
||||
if (newPlayer) {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
bool playingNext = (newPlayer == nextPlayer);
|
||||
if (newPlayer != nextPlayer) {
|
||||
delete nextPlayer;
|
||||
}
|
||||
|
||||
this->nextPlayer = NULL;
|
||||
|
||||
/* first argument suppresses the "Stop" event from getting triggered,
|
||||
the second param is used for gapless playback -- we won't stop the output
|
||||
and will allow pending buffers to finish */
|
||||
this->Stop(true, !playingNext);
|
||||
this->SetNextCanStart(false);
|
||||
|
||||
newPlayer->PlaybackStarted.connect(this, &Transport::OnPlaybackStarted);
|
||||
newPlayer->PlaybackAlmostEnded.connect(this, &Transport::OnPlaybackAlmostEnded);
|
||||
newPlayer->PlaybackFinished.connect(this, &Transport::OnPlaybackFinished);
|
||||
newPlayer->PlaybackError.connect(this, &Transport::OnPlaybackError);
|
||||
|
||||
musik::debug::info(TAG, "play()");
|
||||
|
||||
this->active.push_back(newPlayer);
|
||||
this->output->Resume();
|
||||
newPlayer->Play();
|
||||
}
|
||||
|
||||
this->RaiseStreamEvent(Transport::StreamScheduled, newPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::Stop() {
|
||||
this->Stop(false, true);
|
||||
}
|
||||
|
||||
void Transport::Stop(bool suppressStopEvent, bool stopOutput) {
|
||||
musik::debug::info(TAG, "stop");
|
||||
|
||||
/* if we stop the output, we kill all of the Players immediately.
|
||||
otherwise, we let them finish naturally; RemoveActive() will take
|
||||
care of disposing of them */
|
||||
if (stopOutput) {
|
||||
std::list<Player*> toDelete;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
RESET_NEXT_PLAYER();
|
||||
std::swap(toDelete, this->active);
|
||||
}
|
||||
|
||||
/* delete these in the background to avoid deadlock in some cases
|
||||
where this method is implicitly triggered via Player callback. however,
|
||||
we should stop them immediately so they stop producing audio. */
|
||||
std::for_each(toDelete.begin(), toDelete.end(), stopPlayer);
|
||||
DEFER(&Transport::DeletePlayers, toDelete);
|
||||
|
||||
/* stopping the transport will stop any buffers that are currently in
|
||||
flight. this makes the sound end immediately. */
|
||||
this->output->Stop();
|
||||
}
|
||||
|
||||
if (!suppressStopEvent) {
|
||||
/* if we know we're starting another track immediately, suppress
|
||||
the stop event. this functionality is not available to the public
|
||||
interface, it's an internal optimization */
|
||||
this->SetPlaybackState(PlaybackStopped);
|
||||
}
|
||||
}
|
||||
|
||||
bool Transport::Pause() {
|
||||
musik::debug::info(TAG, "pause");
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
this->output->Pause();
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
count = this->active.size();
|
||||
}
|
||||
|
||||
if (count) {
|
||||
this->SetPlaybackState(PlaybackPaused);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Transport::Resume() {
|
||||
musik::debug::info(TAG, "resume");
|
||||
|
||||
this->output->Resume();
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
count = this->active.size();
|
||||
|
||||
auto it = this->active.begin();
|
||||
while (it != this->active.end()) {
|
||||
(*it)->Play();
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
this->SetPlaybackState(Transport::PlaybackPlaying);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
double Transport::Position() {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (!this->active.empty()) {
|
||||
return this->active.front()->Position();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Transport::SetPosition(double seconds) {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (!this->active.empty()) {
|
||||
this->active.front()->SetPosition(seconds);
|
||||
this->TimeChanged(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
double Transport::Volume() {
|
||||
return this->volume;
|
||||
}
|
||||
|
||||
void Transport::SetVolume(double volume) {
|
||||
double oldVolume = this->volume;
|
||||
|
||||
volume = std::max(0.0, std::min(1.0, volume));
|
||||
|
||||
this->volume = volume;
|
||||
|
||||
if (oldVolume != this->volume) {
|
||||
this->VolumeChanged();
|
||||
}
|
||||
|
||||
std::string output = boost::str(
|
||||
boost::format("set volume %d%%") % round(volume * 100));
|
||||
|
||||
musik::debug::info(TAG, output);
|
||||
|
||||
this->output->SetVolume(this->volume);
|
||||
}
|
||||
|
||||
void Transport::RemoveActive(Player* player) {
|
||||
bool found = false;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
std::list<Player*>::iterator it =
|
||||
std::find(this->active.begin(), this->active.end(), player);
|
||||
|
||||
if (it != this->active.end()) {
|
||||
this->active.erase(it);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* outside of the critical section, otherwise potential deadlock */
|
||||
if (found) {
|
||||
delete player;
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::DeletePlayers(std::list<Player*> players) {
|
||||
std::for_each(players.begin(), players.end(), deletePlayer);
|
||||
}
|
||||
|
||||
void Transport::SetNextCanStart(bool nextCanStart) {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
this->nextCanStart = nextCanStart;
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackStarted(Player* player) {
|
||||
this->RaiseStreamEvent(Transport::StreamPlaying, player);
|
||||
this->SetPlaybackState(Transport::PlaybackPlaying);
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackAlmostEnded(Player* player) {
|
||||
this->SetNextCanStart(true);
|
||||
this->RaiseStreamEvent(Transport::StreamAlmostDone, player);
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
/* if another component configured a next player while we were playing,
|
||||
go ahead and get it started now. */
|
||||
if (this->nextPlayer) {
|
||||
this->StartWithPlayer(this->nextPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackFinished(Player* player) {
|
||||
this->SetNextCanStart(true);
|
||||
this->RaiseStreamEvent(Transport::StreamFinished, player);
|
||||
|
||||
bool stopped = false;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
bool startedNext = false;
|
||||
|
||||
if (this->nextPlayer) {
|
||||
this->StartWithPlayer(this->nextPlayer);
|
||||
startedNext = true;
|
||||
}
|
||||
|
||||
/* we're considered stopped if we were unable to automatically start
|
||||
the next track, and the number of players is zero... or the number
|
||||
of players is one, and it's the current player. remember, we free
|
||||
players asynchronously. */
|
||||
if (!startedNext) {
|
||||
stopped =
|
||||
!this->active.size() ||
|
||||
(this->active.size() == 1 && this->active.front() == player);
|
||||
}
|
||||
}
|
||||
|
||||
if (stopped) {
|
||||
this->Stop();
|
||||
}
|
||||
|
||||
DEFER(&Transport::RemoveActive, player);
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackError(Player* player) {
|
||||
this->SetNextCanStart(true);
|
||||
this->RaiseStreamEvent(Transport::StreamError, player);
|
||||
this->SetPlaybackState(Transport::PlaybackStopped);
|
||||
DEFER(&Transport::RemoveActive, player);
|
||||
}
|
||||
|
||||
void Transport::SetPlaybackState(int state) {
|
||||
bool changed = false;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
changed = (this->state != state);
|
||||
this->state = (PlaybackState) state;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this->PlaybackEvent(state);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::RaiseStreamEvent(int type, Player* player) {
|
||||
this->StreamEvent(type, player->GetUrl());
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/playback/Transport.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <algorithm>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
using namespace musik::core::audio;
|
||||
|
||||
static std::string TAG = "Transport";
|
||||
|
||||
#define RESET_NEXT_PLAYER() \
|
||||
delete this->nextPlayer; \
|
||||
this->nextPlayer = NULL;
|
||||
|
||||
#define DEFER(x, y) \
|
||||
{ \
|
||||
boost::thread thread(boost::bind(x, this, y)); \
|
||||
thread.detach(); \
|
||||
}
|
||||
|
||||
static void stopPlayer(Player* p) {
|
||||
p->Stop();
|
||||
}
|
||||
|
||||
static void deletePlayer(Player* p) {
|
||||
delete p;
|
||||
}
|
||||
|
||||
Transport::Transport()
|
||||
: volume(1.0)
|
||||
, state(PlaybackStopped)
|
||||
, nextPlayer(NULL)
|
||||
, nextCanStart(false) {
|
||||
this->output = Player::CreateDefaultOutput();
|
||||
}
|
||||
|
||||
Transport::~Transport() {
|
||||
}
|
||||
|
||||
Transport::PlaybackState Transport::GetPlaybackState() {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void Transport::PrepareNextTrack(const std::string& trackUrl) {
|
||||
bool startNext = false;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
this->nextPlayer = new Player(trackUrl, this->output);
|
||||
startNext = this->nextCanStart;
|
||||
}
|
||||
|
||||
if (startNext) {
|
||||
this->StartWithPlayer(this->nextPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::Start(const std::string& url) {
|
||||
musik::debug::info(TAG, "we were asked to start the track at " + url);
|
||||
|
||||
Player* newPlayer = new Player(url, this->output);
|
||||
musik::debug::info(TAG, "Player created successfully");
|
||||
|
||||
this->StartWithPlayer(newPlayer);
|
||||
}
|
||||
|
||||
void Transport::StartWithPlayer(Player* newPlayer) {
|
||||
if (newPlayer) {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
bool playingNext = (newPlayer == nextPlayer);
|
||||
if (newPlayer != nextPlayer) {
|
||||
delete nextPlayer;
|
||||
}
|
||||
|
||||
this->nextPlayer = NULL;
|
||||
|
||||
/* first argument suppresses the "Stop" event from getting triggered,
|
||||
the second param is used for gapless playback -- we won't stop the output
|
||||
and will allow pending buffers to finish */
|
||||
this->Stop(true, !playingNext);
|
||||
this->SetNextCanStart(false);
|
||||
|
||||
newPlayer->PlaybackStarted.connect(this, &Transport::OnPlaybackStarted);
|
||||
newPlayer->PlaybackAlmostEnded.connect(this, &Transport::OnPlaybackAlmostEnded);
|
||||
newPlayer->PlaybackFinished.connect(this, &Transport::OnPlaybackFinished);
|
||||
newPlayer->PlaybackError.connect(this, &Transport::OnPlaybackError);
|
||||
|
||||
musik::debug::info(TAG, "play()");
|
||||
|
||||
this->active.push_back(newPlayer);
|
||||
this->output->Resume();
|
||||
newPlayer->Play();
|
||||
}
|
||||
|
||||
this->RaiseStreamEvent(Transport::StreamScheduled, newPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::Stop() {
|
||||
this->Stop(false, true);
|
||||
}
|
||||
|
||||
void Transport::Stop(bool suppressStopEvent, bool stopOutput) {
|
||||
musik::debug::info(TAG, "stop");
|
||||
|
||||
/* if we stop the output, we kill all of the Players immediately.
|
||||
otherwise, we let them finish naturally; RemoveActive() will take
|
||||
care of disposing of them */
|
||||
if (stopOutput) {
|
||||
std::list<Player*> toDelete;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
RESET_NEXT_PLAYER();
|
||||
std::swap(toDelete, this->active);
|
||||
}
|
||||
|
||||
/* delete these in the background to avoid deadlock in some cases
|
||||
where this method is implicitly triggered via Player callback. however,
|
||||
we should stop them immediately so they stop producing audio. */
|
||||
std::for_each(toDelete.begin(), toDelete.end(), stopPlayer);
|
||||
DEFER(&Transport::DeletePlayers, toDelete);
|
||||
|
||||
/* stopping the transport will stop any buffers that are currently in
|
||||
flight. this makes the sound end immediately. */
|
||||
this->output->Stop();
|
||||
}
|
||||
|
||||
if (!suppressStopEvent) {
|
||||
/* if we know we're starting another track immediately, suppress
|
||||
the stop event. this functionality is not available to the public
|
||||
interface, it's an internal optimization */
|
||||
this->SetPlaybackState(PlaybackStopped);
|
||||
}
|
||||
}
|
||||
|
||||
bool Transport::Pause() {
|
||||
musik::debug::info(TAG, "pause");
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
this->output->Pause();
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
count = this->active.size();
|
||||
}
|
||||
|
||||
if (count) {
|
||||
this->SetPlaybackState(PlaybackPaused);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Transport::Resume() {
|
||||
musik::debug::info(TAG, "resume");
|
||||
|
||||
this->output->Resume();
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
count = this->active.size();
|
||||
|
||||
auto it = this->active.begin();
|
||||
while (it != this->active.end()) {
|
||||
(*it)->Play();
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
this->SetPlaybackState(Transport::PlaybackPlaying);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
double Transport::Position() {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (!this->active.empty()) {
|
||||
return this->active.front()->Position();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Transport::SetPosition(double seconds) {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (!this->active.empty()) {
|
||||
this->active.front()->SetPosition(seconds);
|
||||
this->TimeChanged(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
double Transport::Volume() {
|
||||
return this->volume;
|
||||
}
|
||||
|
||||
void Transport::SetVolume(double volume) {
|
||||
double oldVolume = this->volume;
|
||||
|
||||
volume = std::max(0.0, std::min(1.0, volume));
|
||||
|
||||
this->volume = volume;
|
||||
|
||||
if (oldVolume != this->volume) {
|
||||
this->VolumeChanged();
|
||||
}
|
||||
|
||||
std::string output = boost::str(
|
||||
boost::format("set volume %d%%") % round(volume * 100));
|
||||
|
||||
musik::debug::info(TAG, output);
|
||||
|
||||
this->output->SetVolume(this->volume);
|
||||
}
|
||||
|
||||
void Transport::RemoveActive(Player* player) {
|
||||
bool found = false;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
std::list<Player*>::iterator it =
|
||||
std::find(this->active.begin(), this->active.end(), player);
|
||||
|
||||
if (it != this->active.end()) {
|
||||
this->active.erase(it);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* outside of the critical section, otherwise potential deadlock */
|
||||
if (found) {
|
||||
delete player;
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::DeletePlayers(std::list<Player*> players) {
|
||||
std::for_each(players.begin(), players.end(), deletePlayer);
|
||||
}
|
||||
|
||||
void Transport::SetNextCanStart(bool nextCanStart) {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
this->nextCanStart = nextCanStart;
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackStarted(Player* player) {
|
||||
this->RaiseStreamEvent(Transport::StreamPlaying, player);
|
||||
this->SetPlaybackState(Transport::PlaybackPlaying);
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackAlmostEnded(Player* player) {
|
||||
this->SetNextCanStart(true);
|
||||
this->RaiseStreamEvent(Transport::StreamAlmostDone, player);
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
/* if another component configured a next player while we were playing,
|
||||
go ahead and get it started now. */
|
||||
if (this->nextPlayer) {
|
||||
this->StartWithPlayer(this->nextPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackFinished(Player* player) {
|
||||
this->SetNextCanStart(true);
|
||||
this->RaiseStreamEvent(Transport::StreamFinished, player);
|
||||
|
||||
bool stopped = false;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
bool startedNext = false;
|
||||
|
||||
if (this->nextPlayer) {
|
||||
this->StartWithPlayer(this->nextPlayer);
|
||||
startedNext = true;
|
||||
}
|
||||
|
||||
/* we're considered stopped if we were unable to automatically start
|
||||
the next track, and the number of players is zero... or the number
|
||||
of players is one, and it's the current player. remember, we free
|
||||
players asynchronously. */
|
||||
if (!startedNext) {
|
||||
stopped =
|
||||
!this->active.size() ||
|
||||
(this->active.size() == 1 && this->active.front() == player);
|
||||
}
|
||||
}
|
||||
|
||||
if (stopped) {
|
||||
this->Stop();
|
||||
}
|
||||
|
||||
DEFER(&Transport::RemoveActive, player);
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackError(Player* player) {
|
||||
this->SetNextCanStart(true);
|
||||
this->RaiseStreamEvent(Transport::StreamError, player);
|
||||
this->SetPlaybackState(Transport::PlaybackStopped);
|
||||
DEFER(&Transport::RemoveActive, player);
|
||||
}
|
||||
|
||||
void Transport::SetPlaybackState(int state) {
|
||||
bool changed = false;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
changed = (this->state != state);
|
||||
this->state = (PlaybackState) state;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this->PlaybackEvent(state);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::RaiseStreamEvent(int type, Player* player) {
|
||||
this->StreamEvent(type, player->GetUrl());
|
||||
}
|
||||
|
@ -30,142 +30,142 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <core/config.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/debug.h>
|
||||
#include <iostream>
|
||||
|
||||
static const std::string TAG = "PluginFactory";
|
||||
static boost::mutex instanceMutex;
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
PluginFactory& PluginFactory:: Instance() {
|
||||
boost::mutex::scoped_lock lock(instanceMutex);
|
||||
|
||||
static PluginFactory* instance = NULL;
|
||||
|
||||
if (instance == NULL) {
|
||||
instance = new PluginFactory();
|
||||
}
|
||||
|
||||
return *instance;
|
||||
}
|
||||
|
||||
PluginFactory::PluginFactory() {
|
||||
musik::debug::info(TAG, "loading plugins");
|
||||
this->LoadPlugins();
|
||||
}
|
||||
|
||||
PluginFactory::~PluginFactory(void){
|
||||
for (size_t i = 0; i < this->loadedPlugins.size(); i++) {
|
||||
this->loadedPlugins[i]->Destroy();
|
||||
}
|
||||
|
||||
std::vector<void*>::iterator dll = this->loadedDlls.begin();
|
||||
for( ; dll != this->loadedDlls.end(); dll++) {
|
||||
#ifdef WIN32
|
||||
FreeLibrary((HMODULE) (*dll));
|
||||
#else
|
||||
dlclose(*dll);
|
||||
#endif
|
||||
}
|
||||
|
||||
loadedDlls.clear();
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void PluginFactory::LoadPlugins(){
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
#ifdef WIN32
|
||||
{
|
||||
std::wstring wpath = u8to16(GetPluginDirectory());
|
||||
SetDllDirectory(wpath.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string pluginDir(GetPluginDirectory());
|
||||
boost::filesystem::path dir(pluginDir);
|
||||
|
||||
try {
|
||||
boost::filesystem::directory_iterator end;
|
||||
for (boost::filesystem::directory_iterator file(dir); file != end; file++) {
|
||||
if (boost::filesystem::is_regular(file->status())){
|
||||
std::string filename(file->path().string());
|
||||
#ifdef WIN32
|
||||
/* if the file ends with ".dll", we'll try to load it*/
|
||||
if (filename.substr(filename.size() - 4) == ".dll") {
|
||||
HMODULE dll = LoadLibrary(u8to16(filename).c_str());
|
||||
if (dll != NULL) {
|
||||
/* every plugin has a "GetPlugin" method. */
|
||||
CallGetPlugin getPluginCall = (CallGetPlugin) GetProcAddress(dll, "GetPlugin");
|
||||
|
||||
if (getPluginCall) {
|
||||
/* exists? add it! */
|
||||
this->loadedPlugins.push_back(getPluginCall());
|
||||
this->loadedDlls.push_back(dll);
|
||||
}
|
||||
else {
|
||||
/* otherwise, free nad move on */
|
||||
FreeLibrary(dll);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
if (filename.substr(filename.size() - 6) == ".dylib") {
|
||||
int openFlags = RTLD_LOCAL;
|
||||
#else
|
||||
if (filename.substr(filename.size() - 3) == ".so") {
|
||||
int openFlags = RTLD_NOW;
|
||||
#endif
|
||||
void* dll = NULL;
|
||||
|
||||
try {
|
||||
dll = dlopen(filename.c_str(), openFlags);
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "exception while loading plugin " << filename << std::endl;
|
||||
|
||||
musik::debug::err(TAG, "exception while loading plugin " + filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!dll) {
|
||||
char *err = dlerror();
|
||||
|
||||
std::cerr << "exception while loading plugin " << filename << " " << err << std::endl;
|
||||
|
||||
musik::debug::err(
|
||||
TAG,
|
||||
"could not load shared library " + filename +
|
||||
" error: " + std::string(err));
|
||||
}
|
||||
else {
|
||||
CallGetPlugin getPluginCall;
|
||||
*(void **)(&getPluginCall) = dlsym(dll, "GetPlugin");
|
||||
|
||||
if (getPluginCall) {
|
||||
musik::debug::info(TAG, "loaded: " + filename);
|
||||
this->loadedPlugins.push_back(getPluginCall());
|
||||
this->loadedDlls.push_back(dll);
|
||||
}
|
||||
else {
|
||||
dlclose(dll);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(...) {
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <core/config.h>
|
||||
#include <core/support/Common.h>
|
||||
#include <core/debug.h>
|
||||
#include <iostream>
|
||||
|
||||
static const std::string TAG = "PluginFactory";
|
||||
static boost::mutex instanceMutex;
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
PluginFactory& PluginFactory:: Instance() {
|
||||
boost::mutex::scoped_lock lock(instanceMutex);
|
||||
|
||||
static PluginFactory* instance = NULL;
|
||||
|
||||
if (instance == NULL) {
|
||||
instance = new PluginFactory();
|
||||
}
|
||||
|
||||
return *instance;
|
||||
}
|
||||
|
||||
PluginFactory::PluginFactory() {
|
||||
musik::debug::info(TAG, "loading plugins");
|
||||
this->LoadPlugins();
|
||||
}
|
||||
|
||||
PluginFactory::~PluginFactory(void){
|
||||
for (size_t i = 0; i < this->loadedPlugins.size(); i++) {
|
||||
this->loadedPlugins[i]->Destroy();
|
||||
}
|
||||
|
||||
std::vector<void*>::iterator dll = this->loadedDlls.begin();
|
||||
for( ; dll != this->loadedDlls.end(); dll++) {
|
||||
#ifdef WIN32
|
||||
FreeLibrary((HMODULE) (*dll));
|
||||
#else
|
||||
dlclose(*dll);
|
||||
#endif
|
||||
}
|
||||
|
||||
loadedDlls.clear();
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void PluginFactory::LoadPlugins(){
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
#ifdef WIN32
|
||||
{
|
||||
std::wstring wpath = u8to16(GetPluginDirectory());
|
||||
SetDllDirectory(wpath.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string pluginDir(GetPluginDirectory());
|
||||
boost::filesystem::path dir(pluginDir);
|
||||
|
||||
try {
|
||||
boost::filesystem::directory_iterator end;
|
||||
for (boost::filesystem::directory_iterator file(dir); file != end; file++) {
|
||||
if (boost::filesystem::is_regular(file->status())){
|
||||
std::string filename(file->path().string());
|
||||
#ifdef WIN32
|
||||
/* if the file ends with ".dll", we'll try to load it*/
|
||||
if (filename.substr(filename.size() - 4) == ".dll") {
|
||||
HMODULE dll = LoadLibrary(u8to16(filename).c_str());
|
||||
if (dll != NULL) {
|
||||
/* every plugin has a "GetPlugin" method. */
|
||||
CallGetPlugin getPluginCall = (CallGetPlugin) GetProcAddress(dll, "GetPlugin");
|
||||
|
||||
if (getPluginCall) {
|
||||
/* exists? add it! */
|
||||
this->loadedPlugins.push_back(getPluginCall());
|
||||
this->loadedDlls.push_back(dll);
|
||||
}
|
||||
else {
|
||||
/* otherwise, free nad move on */
|
||||
FreeLibrary(dll);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
if (filename.substr(filename.size() - 6) == ".dylib") {
|
||||
int openFlags = RTLD_LOCAL;
|
||||
#else
|
||||
if (filename.substr(filename.size() - 3) == ".so") {
|
||||
int openFlags = RTLD_NOW;
|
||||
#endif
|
||||
void* dll = NULL;
|
||||
|
||||
try {
|
||||
dll = dlopen(filename.c_str(), openFlags);
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "exception while loading plugin " << filename << std::endl;
|
||||
|
||||
musik::debug::err(TAG, "exception while loading plugin " + filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!dll) {
|
||||
char *err = dlerror();
|
||||
|
||||
std::cerr << "exception while loading plugin " << filename << " " << err << std::endl;
|
||||
|
||||
musik::debug::err(
|
||||
TAG,
|
||||
"could not load shared library " + filename +
|
||||
" error: " + std::string(err));
|
||||
}
|
||||
else {
|
||||
CallGetPlugin getPluginCall;
|
||||
*(void **)(&getPluginCall) = dlsym(dll, "GetPlugin");
|
||||
|
||||
if (getPluginCall) {
|
||||
musik::debug::info(TAG, "loaded: " + filename);
|
||||
this->loadedPlugins.push_back(getPluginCall());
|
||||
this->loadedDlls.push_back(dll);
|
||||
}
|
||||
else {
|
||||
dlclose(dll);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(...) {
|
||||
}
|
||||
}
|
||||
|
@ -30,119 +30,119 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/support/Common.h>
|
||||
#include <core/config.h>
|
||||
#include <utf8/utf8.h>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
/* nothing special for Win32 */
|
||||
#elif __APPLE__
|
||||
#include <mach-o/dyld.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
std::string musik::core::GetPluginDirectory() {
|
||||
std::string path(GetApplicationDirectory());
|
||||
path.append("/plugins/");
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string musik::core::GetApplicationDirectory() {
|
||||
std::string result;
|
||||
|
||||
#ifdef WIN32
|
||||
wchar_t widePath[2048];
|
||||
int length = GetModuleFileName(NULL, widePath, 2048);
|
||||
if (length != 0 && length < 2048) {
|
||||
result.assign(GetPath(u16to8(widePath).c_str()));
|
||||
}
|
||||
#elif __APPLE__
|
||||
char pathbuf[PATH_MAX + 1];
|
||||
uint32_t bufsize = sizeof(pathbuf);
|
||||
_NSGetExecutablePath(pathbuf, &bufsize);
|
||||
char *resolved = realpath(pathbuf, NULL);
|
||||
result.assign(resolved);
|
||||
free(resolved);
|
||||
size_t last = result.find_last_of("/");
|
||||
result = result.substr(0, last); /* remove filename component */
|
||||
#else
|
||||
std::string pathToProc = boost::str(boost::format("/proc/%d/exe") % (int) getpid());
|
||||
char pathbuf[PATH_MAX + 1];
|
||||
readlink(pathToProc.c_str(), pathbuf, PATH_MAX);
|
||||
result.assign(pathbuf);
|
||||
size_t last = result.find_last_of("/");
|
||||
result = result.substr(0, last); /* remove filename component */
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string musik::core::GetDataDirectory() {
|
||||
std::string directory;
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD bufferSize = GetEnvironmentVariable(_T("APPDATA"), 0, 0);
|
||||
wchar_t *buffer = new wchar_t[bufferSize + 2];
|
||||
GetEnvironmentVariable(_T("APPDATA"), buffer, bufferSize);
|
||||
directory.assign(u16to8(buffer));
|
||||
directory.append("/mC2/");
|
||||
delete[] buffer;
|
||||
#else
|
||||
directory = std::string(std::getenv("HOME"));
|
||||
directory.append("/.mC2/");
|
||||
#endif
|
||||
|
||||
boost::filesystem::path path(directory);
|
||||
if (!boost::filesystem::exists(path)) {
|
||||
boost::filesystem::create_directories(path);
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
std::string musik::core::GetPath(const std::string &sFile) {
|
||||
std::string sPath;
|
||||
int length;
|
||||
|
||||
#ifdef WIN32
|
||||
wchar_t widePath[2048];
|
||||
wchar_t *szFile = NULL;
|
||||
|
||||
length = GetFullPathName(u8to16(sFile).c_str(), 2048, widePath, &szFile);
|
||||
if(length != 0 && length < 2048) {
|
||||
sPath.assign(u16to8(widePath).c_str());
|
||||
if(szFile!=0) {
|
||||
std::string sTheFile = u16to8(szFile);
|
||||
sPath.assign(sPath.substr(0,length-sTheFile.length()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
sPath.assign(sFile);
|
||||
}
|
||||
#else //TODO: check this POSIX GetPath works
|
||||
char* szDir;
|
||||
sPath.assign(getcwd((char*)szDir, (size_t) length));
|
||||
|
||||
#endif //WIN32
|
||||
return sPath;
|
||||
}
|
||||
|
||||
uint64 musik::core::Checksum(char *data,unsigned int bytes) {
|
||||
uint64 sum = 0;
|
||||
for(unsigned int i = 0; i < bytes; ++i) {
|
||||
char ch = *(data + i);
|
||||
sum += (uint64) ch;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/support/Common.h>
|
||||
#include <core/config.h>
|
||||
#include <utf8/utf8.h>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
/* nothing special for Win32 */
|
||||
#elif __APPLE__
|
||||
#include <mach-o/dyld.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
std::string musik::core::GetPluginDirectory() {
|
||||
std::string path(GetApplicationDirectory());
|
||||
path.append("/plugins/");
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string musik::core::GetApplicationDirectory() {
|
||||
std::string result;
|
||||
|
||||
#ifdef WIN32
|
||||
wchar_t widePath[2048];
|
||||
int length = GetModuleFileName(NULL, widePath, 2048);
|
||||
if (length != 0 && length < 2048) {
|
||||
result.assign(GetPath(u16to8(widePath).c_str()));
|
||||
}
|
||||
#elif __APPLE__
|
||||
char pathbuf[PATH_MAX + 1];
|
||||
uint32_t bufsize = sizeof(pathbuf);
|
||||
_NSGetExecutablePath(pathbuf, &bufsize);
|
||||
char *resolved = realpath(pathbuf, NULL);
|
||||
result.assign(resolved);
|
||||
free(resolved);
|
||||
size_t last = result.find_last_of("/");
|
||||
result = result.substr(0, last); /* remove filename component */
|
||||
#else
|
||||
std::string pathToProc = boost::str(boost::format("/proc/%d/exe") % (int) getpid());
|
||||
char pathbuf[PATH_MAX + 1];
|
||||
readlink(pathToProc.c_str(), pathbuf, PATH_MAX);
|
||||
result.assign(pathbuf);
|
||||
size_t last = result.find_last_of("/");
|
||||
result = result.substr(0, last); /* remove filename component */
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string musik::core::GetDataDirectory() {
|
||||
std::string directory;
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD bufferSize = GetEnvironmentVariable(_T("APPDATA"), 0, 0);
|
||||
wchar_t *buffer = new wchar_t[bufferSize + 2];
|
||||
GetEnvironmentVariable(_T("APPDATA"), buffer, bufferSize);
|
||||
directory.assign(u16to8(buffer));
|
||||
directory.append("/mC2/");
|
||||
delete[] buffer;
|
||||
#else
|
||||
directory = std::string(std::getenv("HOME"));
|
||||
directory.append("/.mC2/");
|
||||
#endif
|
||||
|
||||
boost::filesystem::path path(directory);
|
||||
if (!boost::filesystem::exists(path)) {
|
||||
boost::filesystem::create_directories(path);
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
std::string musik::core::GetPath(const std::string &sFile) {
|
||||
std::string sPath;
|
||||
int length;
|
||||
|
||||
#ifdef WIN32
|
||||
wchar_t widePath[2048];
|
||||
wchar_t *szFile = NULL;
|
||||
|
||||
length = GetFullPathName(u8to16(sFile).c_str(), 2048, widePath, &szFile);
|
||||
if(length != 0 && length < 2048) {
|
||||
sPath.assign(u16to8(widePath).c_str());
|
||||
if(szFile!=0) {
|
||||
std::string sTheFile = u16to8(szFile);
|
||||
sPath.assign(sPath.substr(0,length-sTheFile.length()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
sPath.assign(sFile);
|
||||
}
|
||||
#else //TODO: check this POSIX GetPath works
|
||||
char* szDir;
|
||||
sPath.assign(getcwd((char*)szDir, (size_t) length));
|
||||
|
||||
#endif //WIN32
|
||||
return sPath;
|
||||
}
|
||||
|
||||
uint64 musik::core::Checksum(char *data,unsigned int bytes) {
|
||||
uint64 sum = 0;
|
||||
for(unsigned int i = 0; i < bytes; ++i) {
|
||||
char ch = *(data + i);
|
||||
sum += (uint64) ch;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
@ -30,294 +30,294 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/support/Preferences.h>
|
||||
|
||||
#include <core/support/Common.h>
|
||||
#include <core/db/CachedStatement.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Preferences::Preferences(const char* nameSpace,const char* library)
|
||||
:nameSpace(nameSpace)
|
||||
,libraryId(0)
|
||||
{
|
||||
this->IOPtr = IO::Instance();
|
||||
this->settings = this->IOPtr->GetNamespace(nameSpace,library,this->libraryId);
|
||||
}
|
||||
|
||||
Preferences::~Preferences(void){
|
||||
}
|
||||
|
||||
|
||||
bool Preferences::GetBool(const char* key,bool defaultValue){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
|
||||
IO::SettingMap::iterator setting = this->settings->find(key);
|
||||
if(setting!=this->settings->end()){
|
||||
return setting->second.Value(defaultValue);
|
||||
}
|
||||
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int Preferences::GetInt(const char* key,int defaultValue){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
IO::SettingMap::iterator setting = this->settings->find(key);
|
||||
if(setting!=this->settings->end()){
|
||||
return setting->second.Value(defaultValue);
|
||||
}
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
std::string Preferences::GetString(const char* key,const char* defaultValue){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
IO::SettingMap::iterator setting = this->settings->find(key);
|
||||
if(setting!=this->settings->end()){
|
||||
return setting->second.Value(std::string(defaultValue));
|
||||
}
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,std::string(defaultValue));
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void Preferences::SetBool(const char* key,bool value){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,value);
|
||||
}
|
||||
|
||||
void Preferences::SetInt(const char* key,int value){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,value);
|
||||
}
|
||||
|
||||
void Preferences::SetString(const char* key,const char* value){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,std::string(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Preferences::IO::Ptr Preferences::IO::Instance(){
|
||||
static boost::mutex instanceMutex;
|
||||
boost::mutex::scoped_lock oLock(instanceMutex);
|
||||
|
||||
static IO::Ptr sInstance(new Preferences::IO());
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
Preferences::IO::IO(void){
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
std::string dataDir = GetDataDirectory();
|
||||
std::string dbFile = GetDataDirectory() + "settings.db";
|
||||
this->db.Open(dbFile.c_str(),0,128);
|
||||
|
||||
Preferences::CreateDB(this->db);
|
||||
|
||||
}
|
||||
|
||||
void Preferences::IO::SaveSetting(const char* nameSpace,int libraryId,const char *key,Setting setting){
|
||||
int nameSpaceId(0);
|
||||
db::CachedStatement getStmt("SELECT id FROM namespaces WHERE name=? AND library_id=?",this->db);
|
||||
getStmt.BindText(0,nameSpace);
|
||||
getStmt.BindInt(1,libraryId);
|
||||
|
||||
if(getStmt.Step()==db::Row){
|
||||
nameSpaceId = getStmt.ColumnInt(0);
|
||||
db::Statement insertSetting("INSERT OR REPLACE INTO settings (namespace_id,type,the_key,the_value) VALUES (?,?,?,?)",this->db);
|
||||
insertSetting.BindInt(0,nameSpaceId);
|
||||
insertSetting.BindInt(1,setting.type);
|
||||
insertSetting.BindText(2,key);
|
||||
switch(setting.type){
|
||||
case (Setting::Bool):
|
||||
insertSetting.BindInt(3,setting.valueBool?1:0);
|
||||
break;
|
||||
case (Setting::Int):
|
||||
insertSetting.BindInt(3,setting.valueInt);
|
||||
break;
|
||||
case (Setting::Text):
|
||||
insertSetting.BindText(3,setting.valueText);
|
||||
break;
|
||||
}
|
||||
insertSetting.Step();
|
||||
|
||||
(*this->libraryNamespaces[libraryId][nameSpace])[key] = setting;
|
||||
}
|
||||
}
|
||||
|
||||
Preferences::IO::~IO(void){
|
||||
this->db.Close();
|
||||
}
|
||||
|
||||
Preferences::Setting::Setting() : type(0){
|
||||
}
|
||||
|
||||
Preferences::Setting::Setting(bool value) : type(1),valueBool(value){
|
||||
}
|
||||
|
||||
Preferences::Setting::Setting(int value) : type(2),valueInt(value){
|
||||
}
|
||||
|
||||
Preferences::Setting::Setting(std::string value) : type(3),valueText(value){
|
||||
}
|
||||
|
||||
|
||||
Preferences::Setting::Setting(db::Statement &stmt) :
|
||||
type(stmt.ColumnInt(0)) {
|
||||
switch(type){
|
||||
case Setting::Int:
|
||||
this->valueInt = stmt.ColumnInt(2);
|
||||
break;
|
||||
case Setting::Bool:
|
||||
this->valueBool = (stmt.ColumnInt(2)>0);
|
||||
break;
|
||||
default:
|
||||
this->valueText.assign(stmt.ColumnText(2));
|
||||
}
|
||||
}
|
||||
|
||||
bool Preferences::Setting::Value(bool defaultValue){
|
||||
switch(this->type){
|
||||
case Setting::Bool:
|
||||
return this->valueBool;
|
||||
break;
|
||||
case Setting::Int:
|
||||
return this->valueInt>0;
|
||||
break;
|
||||
case Setting::Text:
|
||||
return !this->valueText.empty();
|
||||
break;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int Preferences::Setting::Value(int defaultValue){
|
||||
switch(this->type){
|
||||
case Setting::Bool:
|
||||
return this->valueBool?1:0;
|
||||
break;
|
||||
case Setting::Int:
|
||||
return this->valueInt;
|
||||
break;
|
||||
case Setting::Text:
|
||||
try{
|
||||
return boost::lexical_cast<int>(this->valueText);
|
||||
}
|
||||
catch(...){
|
||||
}
|
||||
break;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
std::string Preferences::Setting::Value(std::string defaultValue){
|
||||
switch(this->type){
|
||||
case Setting::Bool:
|
||||
return this->valueBool ? "1" : "0";
|
||||
break;
|
||||
case Setting::Int:
|
||||
try{
|
||||
return boost::lexical_cast<std::string>(this->valueInt);
|
||||
}
|
||||
catch(...){
|
||||
}
|
||||
break;
|
||||
case Setting::Text:
|
||||
return this->valueText;
|
||||
break;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Preferences::IO::SettingMapPtr Preferences::IO::GetNamespace(const char* nameSpace,const char* library,int &libraryId){
|
||||
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
if(library!=NULL){
|
||||
db::Statement getLibStmt("SELECT id FROM libraries WHERE name=?",this->db);
|
||||
getLibStmt.BindText(0,library);
|
||||
if(getLibStmt.Step()==db::Row){
|
||||
libraryId = getLibStmt.ColumnInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// First check if it's in the NamespaceMap
|
||||
NamespaceMap::iterator ns = this->libraryNamespaces[libraryId].find(nameSpace);
|
||||
if(ns!=this->libraryNamespaces[libraryId].end()){
|
||||
// Found namespace, return settings
|
||||
return ns->second;
|
||||
}
|
||||
|
||||
|
||||
// Not in cache, lets load it from db.
|
||||
int nameSpaceId(0);
|
||||
db::Statement getStmt("SELECT id FROM namespaces WHERE name=? AND library_id=?",this->db);
|
||||
getStmt.BindText(0,nameSpace);
|
||||
getStmt.BindInt(1,libraryId);
|
||||
|
||||
SettingMapPtr newSettings( new SettingMap() );
|
||||
this->libraryNamespaces[libraryId][nameSpace] = newSettings;
|
||||
|
||||
if(getStmt.Step()==db::Row){
|
||||
// Namespace exists, load the settings
|
||||
nameSpaceId = getStmt.ColumnInt(0);
|
||||
|
||||
db::Statement selectSettings("SELECT type,the_key,the_value FROM settings WHERE namespace_id=?",this->db);
|
||||
selectSettings.BindInt(0,nameSpaceId);
|
||||
while( selectSettings.Step()==db::Row ){
|
||||
(*newSettings)[selectSettings.ColumnText(1)] = Setting(selectSettings);
|
||||
}
|
||||
|
||||
return newSettings;
|
||||
}else{
|
||||
// First time namespace is accessed, create it.
|
||||
db::Statement insertNamespace("INSERT INTO namespaces (name,library_id) VALUES (?,?)",this->db);
|
||||
insertNamespace.BindText(0,nameSpace);
|
||||
insertNamespace.BindInt(1,libraryId);
|
||||
insertNamespace.Step();
|
||||
nameSpaceId = this->db.LastInsertedId();
|
||||
return newSettings;
|
||||
}
|
||||
}
|
||||
|
||||
void Preferences::CreateDB(db::Connection &db){
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS namespaces ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT)");
|
||||
|
||||
// Add a library_id relation
|
||||
db.Execute("ALTER TABLE namespaces ADD COLUMN library_id INTEGER DEFAULT 0");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS settings ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"namespace_id INTEGER DEFAULT 0,"
|
||||
"type INTEGER DEFAULT 0,"
|
||||
"the_key TEXT,"
|
||||
"the_value TEXT)");
|
||||
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS namespace_index ON namespaces (name,library_id)");
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS setting_index ON settings (namespace_id,the_key)");
|
||||
|
||||
// Start by initializing the db
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS libraries ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT,"
|
||||
"type INTEGER DEFAULT 0)");
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS library_index ON libraries (name)");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/support/Preferences.h>
|
||||
|
||||
#include <core/support/Common.h>
|
||||
#include <core/db/CachedStatement.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Preferences::Preferences(const char* nameSpace,const char* library)
|
||||
:nameSpace(nameSpace)
|
||||
,libraryId(0)
|
||||
{
|
||||
this->IOPtr = IO::Instance();
|
||||
this->settings = this->IOPtr->GetNamespace(nameSpace,library,this->libraryId);
|
||||
}
|
||||
|
||||
Preferences::~Preferences(void){
|
||||
}
|
||||
|
||||
|
||||
bool Preferences::GetBool(const char* key,bool defaultValue){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
|
||||
IO::SettingMap::iterator setting = this->settings->find(key);
|
||||
if(setting!=this->settings->end()){
|
||||
return setting->second.Value(defaultValue);
|
||||
}
|
||||
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int Preferences::GetInt(const char* key,int defaultValue){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
IO::SettingMap::iterator setting = this->settings->find(key);
|
||||
if(setting!=this->settings->end()){
|
||||
return setting->second.Value(defaultValue);
|
||||
}
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
std::string Preferences::GetString(const char* key,const char* defaultValue){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
IO::SettingMap::iterator setting = this->settings->find(key);
|
||||
if(setting!=this->settings->end()){
|
||||
return setting->second.Value(std::string(defaultValue));
|
||||
}
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,std::string(defaultValue));
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void Preferences::SetBool(const char* key,bool value){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,value);
|
||||
}
|
||||
|
||||
void Preferences::SetInt(const char* key,int value){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,value);
|
||||
}
|
||||
|
||||
void Preferences::SetString(const char* key,const char* value){
|
||||
boost::mutex::scoped_lock lock(IO::Instance()->mutex);
|
||||
this->IOPtr->SaveSetting(this->nameSpace.c_str(),this->libraryId,key,std::string(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Preferences::IO::Ptr Preferences::IO::Instance(){
|
||||
static boost::mutex instanceMutex;
|
||||
boost::mutex::scoped_lock oLock(instanceMutex);
|
||||
|
||||
static IO::Ptr sInstance(new Preferences::IO());
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
Preferences::IO::IO(void){
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
std::string dataDir = GetDataDirectory();
|
||||
std::string dbFile = GetDataDirectory() + "settings.db";
|
||||
this->db.Open(dbFile.c_str(),0,128);
|
||||
|
||||
Preferences::CreateDB(this->db);
|
||||
|
||||
}
|
||||
|
||||
void Preferences::IO::SaveSetting(const char* nameSpace,int libraryId,const char *key,Setting setting){
|
||||
int nameSpaceId(0);
|
||||
db::CachedStatement getStmt("SELECT id FROM namespaces WHERE name=? AND library_id=?",this->db);
|
||||
getStmt.BindText(0,nameSpace);
|
||||
getStmt.BindInt(1,libraryId);
|
||||
|
||||
if(getStmt.Step()==db::Row){
|
||||
nameSpaceId = getStmt.ColumnInt(0);
|
||||
db::Statement insertSetting("INSERT OR REPLACE INTO settings (namespace_id,type,the_key,the_value) VALUES (?,?,?,?)",this->db);
|
||||
insertSetting.BindInt(0,nameSpaceId);
|
||||
insertSetting.BindInt(1,setting.type);
|
||||
insertSetting.BindText(2,key);
|
||||
switch(setting.type){
|
||||
case (Setting::Bool):
|
||||
insertSetting.BindInt(3,setting.valueBool?1:0);
|
||||
break;
|
||||
case (Setting::Int):
|
||||
insertSetting.BindInt(3,setting.valueInt);
|
||||
break;
|
||||
case (Setting::Text):
|
||||
insertSetting.BindText(3,setting.valueText);
|
||||
break;
|
||||
}
|
||||
insertSetting.Step();
|
||||
|
||||
(*this->libraryNamespaces[libraryId][nameSpace])[key] = setting;
|
||||
}
|
||||
}
|
||||
|
||||
Preferences::IO::~IO(void){
|
||||
this->db.Close();
|
||||
}
|
||||
|
||||
Preferences::Setting::Setting() : type(0){
|
||||
}
|
||||
|
||||
Preferences::Setting::Setting(bool value) : type(1),valueBool(value){
|
||||
}
|
||||
|
||||
Preferences::Setting::Setting(int value) : type(2),valueInt(value){
|
||||
}
|
||||
|
||||
Preferences::Setting::Setting(std::string value) : type(3),valueText(value){
|
||||
}
|
||||
|
||||
|
||||
Preferences::Setting::Setting(db::Statement &stmt) :
|
||||
type(stmt.ColumnInt(0)) {
|
||||
switch(type){
|
||||
case Setting::Int:
|
||||
this->valueInt = stmt.ColumnInt(2);
|
||||
break;
|
||||
case Setting::Bool:
|
||||
this->valueBool = (stmt.ColumnInt(2)>0);
|
||||
break;
|
||||
default:
|
||||
this->valueText.assign(stmt.ColumnText(2));
|
||||
}
|
||||
}
|
||||
|
||||
bool Preferences::Setting::Value(bool defaultValue){
|
||||
switch(this->type){
|
||||
case Setting::Bool:
|
||||
return this->valueBool;
|
||||
break;
|
||||
case Setting::Int:
|
||||
return this->valueInt>0;
|
||||
break;
|
||||
case Setting::Text:
|
||||
return !this->valueText.empty();
|
||||
break;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int Preferences::Setting::Value(int defaultValue){
|
||||
switch(this->type){
|
||||
case Setting::Bool:
|
||||
return this->valueBool?1:0;
|
||||
break;
|
||||
case Setting::Int:
|
||||
return this->valueInt;
|
||||
break;
|
||||
case Setting::Text:
|
||||
try{
|
||||
return boost::lexical_cast<int>(this->valueText);
|
||||
}
|
||||
catch(...){
|
||||
}
|
||||
break;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
std::string Preferences::Setting::Value(std::string defaultValue){
|
||||
switch(this->type){
|
||||
case Setting::Bool:
|
||||
return this->valueBool ? "1" : "0";
|
||||
break;
|
||||
case Setting::Int:
|
||||
try{
|
||||
return boost::lexical_cast<std::string>(this->valueInt);
|
||||
}
|
||||
catch(...){
|
||||
}
|
||||
break;
|
||||
case Setting::Text:
|
||||
return this->valueText;
|
||||
break;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Preferences::IO::SettingMapPtr Preferences::IO::GetNamespace(const char* nameSpace,const char* library,int &libraryId){
|
||||
|
||||
boost::mutex::scoped_lock lock(this->mutex);
|
||||
|
||||
if(library!=NULL){
|
||||
db::Statement getLibStmt("SELECT id FROM libraries WHERE name=?",this->db);
|
||||
getLibStmt.BindText(0,library);
|
||||
if(getLibStmt.Step()==db::Row){
|
||||
libraryId = getLibStmt.ColumnInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// First check if it's in the NamespaceMap
|
||||
NamespaceMap::iterator ns = this->libraryNamespaces[libraryId].find(nameSpace);
|
||||
if(ns!=this->libraryNamespaces[libraryId].end()){
|
||||
// Found namespace, return settings
|
||||
return ns->second;
|
||||
}
|
||||
|
||||
|
||||
// Not in cache, lets load it from db.
|
||||
int nameSpaceId(0);
|
||||
db::Statement getStmt("SELECT id FROM namespaces WHERE name=? AND library_id=?",this->db);
|
||||
getStmt.BindText(0,nameSpace);
|
||||
getStmt.BindInt(1,libraryId);
|
||||
|
||||
SettingMapPtr newSettings( new SettingMap() );
|
||||
this->libraryNamespaces[libraryId][nameSpace] = newSettings;
|
||||
|
||||
if(getStmt.Step()==db::Row){
|
||||
// Namespace exists, load the settings
|
||||
nameSpaceId = getStmt.ColumnInt(0);
|
||||
|
||||
db::Statement selectSettings("SELECT type,the_key,the_value FROM settings WHERE namespace_id=?",this->db);
|
||||
selectSettings.BindInt(0,nameSpaceId);
|
||||
while( selectSettings.Step()==db::Row ){
|
||||
(*newSettings)[selectSettings.ColumnText(1)] = Setting(selectSettings);
|
||||
}
|
||||
|
||||
return newSettings;
|
||||
}else{
|
||||
// First time namespace is accessed, create it.
|
||||
db::Statement insertNamespace("INSERT INTO namespaces (name,library_id) VALUES (?,?)",this->db);
|
||||
insertNamespace.BindText(0,nameSpace);
|
||||
insertNamespace.BindInt(1,libraryId);
|
||||
insertNamespace.Step();
|
||||
nameSpaceId = this->db.LastInsertedId();
|
||||
return newSettings;
|
||||
}
|
||||
}
|
||||
|
||||
void Preferences::CreateDB(db::Connection &db){
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS namespaces ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT)");
|
||||
|
||||
// Add a library_id relation
|
||||
db.Execute("ALTER TABLE namespaces ADD COLUMN library_id INTEGER DEFAULT 0");
|
||||
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS settings ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"namespace_id INTEGER DEFAULT 0,"
|
||||
"type INTEGER DEFAULT 0,"
|
||||
"the_key TEXT,"
|
||||
"the_value TEXT)");
|
||||
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS namespace_index ON namespaces (name,library_id)");
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS setting_index ON settings (namespace_id,the_key)");
|
||||
|
||||
// Start by initializing the db
|
||||
db.Execute("CREATE TABLE IF NOT EXISTS libraries ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT,"
|
||||
"type INTEGER DEFAULT 0)");
|
||||
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS library_index ON libraries (name)");
|
||||
|
||||
}
|
||||
|
||||
|
@ -30,45 +30,45 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/support/ThreadHelper.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
ThreadHelper::ThreadHelper()
|
||||
: exit(false) {
|
||||
}
|
||||
|
||||
ThreadHelper::~ThreadHelper() {
|
||||
}
|
||||
|
||||
bool ThreadHelper::Exited() {
|
||||
boost::mutex::scoped_lock lock(this->exitMutex);
|
||||
return this->exit;
|
||||
}
|
||||
|
||||
void ThreadHelper::Exit() {
|
||||
boost::mutex::scoped_lock lock(this->exitMutex);
|
||||
this->exit = true;
|
||||
this->notify.notify_all();
|
||||
}
|
||||
|
||||
void ThreadHelper::NotificationWait(){
|
||||
boost::mutex::scoped_lock lock(this->exitMutex);
|
||||
if (!this->exit) {
|
||||
this->notify.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadHelper::NotificationTimedWait(const boost::xtime &time) {
|
||||
boost::mutex::scoped_lock lock(this->exitMutex);
|
||||
if (!this->exit) {
|
||||
this->notify.timed_wait(lock, time);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadHelper::Notify(){
|
||||
this->notify.notify_all();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/support/ThreadHelper.h>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
ThreadHelper::ThreadHelper()
|
||||
: exit(false) {
|
||||
}
|
||||
|
||||
ThreadHelper::~ThreadHelper() {
|
||||
}
|
||||
|
||||
bool ThreadHelper::Exited() {
|
||||
boost::mutex::scoped_lock lock(this->exitMutex);
|
||||
return this->exit;
|
||||
}
|
||||
|
||||
void ThreadHelper::Exit() {
|
||||
boost::mutex::scoped_lock lock(this->exitMutex);
|
||||
this->exit = true;
|
||||
this->notify.notify_all();
|
||||
}
|
||||
|
||||
void ThreadHelper::NotificationWait(){
|
||||
boost::mutex::scoped_lock lock(this->exitMutex);
|
||||
if (!this->exit) {
|
||||
this->notify.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadHelper::NotificationTimedWait(const boost::xtime &time) {
|
||||
boost::mutex::scoped_lock lock(this->exitMutex);
|
||||
if (!this->exit) {
|
||||
this->notify.timed_wait(lock, time);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadHelper::Notify(){
|
||||
this->notify.notify_all();
|
||||
}
|
||||
|
@ -30,54 +30,54 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/support/Version.h>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
Version::Version()
|
||||
: version(0) {
|
||||
}
|
||||
|
||||
Version::Version(VERSION major, VERSION minor, VERSION revision, VERSION build) {
|
||||
version = ((major & 0xff) << 48) | ((minor & 0xff) << 32) | ((revision & 0xff) << 16) | (build & 0xff);
|
||||
}
|
||||
|
||||
Version::Version(VERSION version)
|
||||
: version(version){
|
||||
}
|
||||
|
||||
void Version::setVersion(VERSION major, VERSION minor, VERSION revision, VERSION build){
|
||||
version = ((major & 0xff) << 48) | ((minor & 0xff) << 32) | ((revision & 0xff) << 16) | (build & 0xff);
|
||||
}
|
||||
|
||||
void Version::setVersion(VERSION version){
|
||||
version = version;
|
||||
}
|
||||
|
||||
Version::~Version(void){
|
||||
}
|
||||
|
||||
std::string Version::getVersion() {
|
||||
return boost::str(boost::format("%1%.%2%.%3%.%4%")
|
||||
% ((version >> 48) & 0xff)
|
||||
% ((version >> 32) & 0xff)
|
||||
% ((version >> 16) & 0xff)
|
||||
% (version & 0xff));
|
||||
}
|
||||
|
||||
int Version::getMajorVersion() {
|
||||
return (int)((version >> 48) & 0xff);
|
||||
}
|
||||
int Version::getMinorVersion() {
|
||||
return (int)((version >> 32) & 0xff);
|
||||
}
|
||||
int Version::getRevisionVersion() {
|
||||
return (int)((version >> 16) & 0xff);
|
||||
}
|
||||
int Version::getBuildVersion() {
|
||||
return (int)(version & 0xff);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include <core/support/Version.h>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
using namespace musik::core;
|
||||
|
||||
Version::Version()
|
||||
: version(0) {
|
||||
}
|
||||
|
||||
Version::Version(VERSION major, VERSION minor, VERSION revision, VERSION build) {
|
||||
version = ((major & 0xff) << 48) | ((minor & 0xff) << 32) | ((revision & 0xff) << 16) | (build & 0xff);
|
||||
}
|
||||
|
||||
Version::Version(VERSION version)
|
||||
: version(version){
|
||||
}
|
||||
|
||||
void Version::setVersion(VERSION major, VERSION minor, VERSION revision, VERSION build){
|
||||
version = ((major & 0xff) << 48) | ((minor & 0xff) << 32) | ((revision & 0xff) << 16) | (build & 0xff);
|
||||
}
|
||||
|
||||
void Version::setVersion(VERSION version){
|
||||
version = version;
|
||||
}
|
||||
|
||||
Version::~Version(void){
|
||||
}
|
||||
|
||||
std::string Version::getVersion() {
|
||||
return boost::str(boost::format("%1%.%2%.%3%.%4%")
|
||||
% ((version >> 48) & 0xff)
|
||||
% ((version >> 32) & 0xff)
|
||||
% ((version >> 16) & 0xff)
|
||||
% (version & 0xff));
|
||||
}
|
||||
|
||||
int Version::getMajorVersion() {
|
||||
return (int)((version >> 48) & 0xff);
|
||||
}
|
||||
int Version::getMinorVersion() {
|
||||
return (int)((version >> 32) & 0xff);
|
||||
}
|
||||
int Version::getRevisionVersion() {
|
||||
return (int)((version >> 16) & 0xff);
|
||||
}
|
||||
int Version::getBuildVersion() {
|
||||
return (int)(version & 0xff);
|
||||
}
|
||||
|
@ -30,141 +30,141 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <cursespp/Colors.h>
|
||||
#include <cursespp/Screen.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <app/query/CategoryTrackListQuery.h>
|
||||
#include "BrowseLayout.h"
|
||||
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
#define CATEGORY_WIDTH 25
|
||||
#define DEFAULT_CATEGORY constants::Track::ARTIST
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::library;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
BrowseLayout::BrowseLayout(
|
||||
PlaybackService& playback,
|
||||
LibraryPtr library)
|
||||
: LayoutBase()
|
||||
, playback(playback) {
|
||||
this->library = library;
|
||||
this->InitializeWindows();
|
||||
}
|
||||
|
||||
BrowseLayout::~BrowseLayout() {
|
||||
|
||||
}
|
||||
|
||||
void BrowseLayout::Layout() {
|
||||
size_t cx = this->GetWidth(), cy = this->GetHeight();
|
||||
|
||||
if (cx == 0 || cy == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t x = this->GetX(), y = this->GetY();
|
||||
|
||||
this->MoveAndResize(x, y, cx, cy);
|
||||
|
||||
this->SetSize(cx, cy);
|
||||
this->SetPosition(x, y);
|
||||
|
||||
this->categoryList->MoveAndResize(x, y, CATEGORY_WIDTH, cy);
|
||||
|
||||
this->trackList->MoveAndResize(
|
||||
x + CATEGORY_WIDTH, y, cx - CATEGORY_WIDTH, cy);
|
||||
|
||||
this->categoryList->SetFocusOrder(0);
|
||||
this->trackList->SetFocusOrder(1);
|
||||
}
|
||||
|
||||
void BrowseLayout::InitializeWindows() {
|
||||
this->categoryList.reset(new CategoryListView(this->library, DEFAULT_CATEGORY));
|
||||
this->trackList.reset(new TrackListView(this->playback, this->library));
|
||||
|
||||
this->AddWindow(this->categoryList);
|
||||
this->AddWindow(this->trackList);
|
||||
|
||||
this->categoryList->SelectionChanged.connect(
|
||||
this, &BrowseLayout::OnCategoryViewSelectionChanged);
|
||||
|
||||
this->categoryList->Invalidated.connect(
|
||||
this, &BrowseLayout::OnCategoryViewInvalidated);
|
||||
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
IWindowPtr BrowseLayout::GetFocus() {
|
||||
return this->focused ? this->focused : LayoutBase::GetFocus();
|
||||
}
|
||||
|
||||
void BrowseLayout::OnVisibilityChanged(bool visible) {
|
||||
LayoutBase::OnVisibilityChanged(visible);
|
||||
|
||||
if (visible) {
|
||||
this->categoryList->Requery();
|
||||
}
|
||||
}
|
||||
|
||||
void BrowseLayout::RequeryTrackList(ListWindow *view) {
|
||||
if (view == this->categoryList.get()) {
|
||||
DBID selectedId = this->categoryList->GetSelectedId();
|
||||
if (selectedId != -1) {
|
||||
this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
|
||||
new CategoryTrackListQuery(
|
||||
this->library,
|
||||
this->categoryList->GetFieldName(),
|
||||
selectedId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BrowseLayout::OnCategoryViewSelectionChanged(
|
||||
ListWindow *view, size_t newIndex, size_t oldIndex) {
|
||||
this->RequeryTrackList(view);
|
||||
}
|
||||
|
||||
void BrowseLayout::OnCategoryViewInvalidated(
|
||||
ListWindow *view, size_t selectedIndex) {
|
||||
this->RequeryTrackList(view);
|
||||
}
|
||||
|
||||
bool BrowseLayout::KeyPress(const std::string& key) {
|
||||
if (key == "^M") { /* enter. play the selection */
|
||||
auto tracks = this->trackList->GetTrackList();
|
||||
auto focus = this->GetFocus();
|
||||
|
||||
size_t index = (focus == this->trackList)
|
||||
? this->trackList->GetSelectedIndex() : 0;
|
||||
|
||||
this->playback.Play(*tracks, index);
|
||||
return true;
|
||||
}
|
||||
if (key == "KEY_F(5)") {
|
||||
this->categoryList->Requery();
|
||||
return true;
|
||||
}
|
||||
else if (key == "ALT_1" || key == "M-1") {
|
||||
this->categoryList->SetFieldName(constants::Track::ARTIST);
|
||||
return true;
|
||||
}
|
||||
else if (key == "ALT_2" || key == "M-2") {
|
||||
this->categoryList->SetFieldName(constants::Track::ALBUM);
|
||||
return true;
|
||||
}
|
||||
else if (key == "ALT_3" || key == "M-3") {
|
||||
this->categoryList->SetFieldName(constants::Track::GENRE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return LayoutBase::KeyPress(key);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <cursespp/Colors.h>
|
||||
#include <cursespp/Screen.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <app/query/CategoryTrackListQuery.h>
|
||||
#include "BrowseLayout.h"
|
||||
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
#define CATEGORY_WIDTH 25
|
||||
#define DEFAULT_CATEGORY constants::Track::ARTIST
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::library;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
BrowseLayout::BrowseLayout(
|
||||
PlaybackService& playback,
|
||||
LibraryPtr library)
|
||||
: LayoutBase()
|
||||
, playback(playback) {
|
||||
this->library = library;
|
||||
this->InitializeWindows();
|
||||
}
|
||||
|
||||
BrowseLayout::~BrowseLayout() {
|
||||
|
||||
}
|
||||
|
||||
void BrowseLayout::Layout() {
|
||||
size_t cx = this->GetWidth(), cy = this->GetHeight();
|
||||
|
||||
if (cx == 0 || cy == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t x = this->GetX(), y = this->GetY();
|
||||
|
||||
this->MoveAndResize(x, y, cx, cy);
|
||||
|
||||
this->SetSize(cx, cy);
|
||||
this->SetPosition(x, y);
|
||||
|
||||
this->categoryList->MoveAndResize(x, y, CATEGORY_WIDTH, cy);
|
||||
|
||||
this->trackList->MoveAndResize(
|
||||
x + CATEGORY_WIDTH, y, cx - CATEGORY_WIDTH, cy);
|
||||
|
||||
this->categoryList->SetFocusOrder(0);
|
||||
this->trackList->SetFocusOrder(1);
|
||||
}
|
||||
|
||||
void BrowseLayout::InitializeWindows() {
|
||||
this->categoryList.reset(new CategoryListView(this->library, DEFAULT_CATEGORY));
|
||||
this->trackList.reset(new TrackListView(this->playback, this->library));
|
||||
|
||||
this->AddWindow(this->categoryList);
|
||||
this->AddWindow(this->trackList);
|
||||
|
||||
this->categoryList->SelectionChanged.connect(
|
||||
this, &BrowseLayout::OnCategoryViewSelectionChanged);
|
||||
|
||||
this->categoryList->Invalidated.connect(
|
||||
this, &BrowseLayout::OnCategoryViewInvalidated);
|
||||
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
IWindowPtr BrowseLayout::GetFocus() {
|
||||
return this->focused ? this->focused : LayoutBase::GetFocus();
|
||||
}
|
||||
|
||||
void BrowseLayout::OnVisibilityChanged(bool visible) {
|
||||
LayoutBase::OnVisibilityChanged(visible);
|
||||
|
||||
if (visible) {
|
||||
this->categoryList->Requery();
|
||||
}
|
||||
}
|
||||
|
||||
void BrowseLayout::RequeryTrackList(ListWindow *view) {
|
||||
if (view == this->categoryList.get()) {
|
||||
DBID selectedId = this->categoryList->GetSelectedId();
|
||||
if (selectedId != -1) {
|
||||
this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
|
||||
new CategoryTrackListQuery(
|
||||
this->library,
|
||||
this->categoryList->GetFieldName(),
|
||||
selectedId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BrowseLayout::OnCategoryViewSelectionChanged(
|
||||
ListWindow *view, size_t newIndex, size_t oldIndex) {
|
||||
this->RequeryTrackList(view);
|
||||
}
|
||||
|
||||
void BrowseLayout::OnCategoryViewInvalidated(
|
||||
ListWindow *view, size_t selectedIndex) {
|
||||
this->RequeryTrackList(view);
|
||||
}
|
||||
|
||||
bool BrowseLayout::KeyPress(const std::string& key) {
|
||||
if (key == "^M") { /* enter. play the selection */
|
||||
auto tracks = this->trackList->GetTrackList();
|
||||
auto focus = this->GetFocus();
|
||||
|
||||
size_t index = (focus == this->trackList)
|
||||
? this->trackList->GetSelectedIndex() : 0;
|
||||
|
||||
this->playback.Play(*tracks, index);
|
||||
return true;
|
||||
}
|
||||
if (key == "KEY_F(5)") {
|
||||
this->categoryList->Requery();
|
||||
return true;
|
||||
}
|
||||
else if (key == "ALT_1" || key == "M-1") {
|
||||
this->categoryList->SetFieldName(constants::Track::ARTIST);
|
||||
return true;
|
||||
}
|
||||
else if (key == "ALT_2" || key == "M-2") {
|
||||
this->categoryList->SetFieldName(constants::Track::ALBUM);
|
||||
return true;
|
||||
}
|
||||
else if (key == "ALT_3" || key == "M-3") {
|
||||
this->categoryList->SetFieldName(constants::Track::GENRE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return LayoutBase::KeyPress(key);
|
||||
}
|
||||
|
@ -30,104 +30,104 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ConsoleLayout.h"
|
||||
#include <cursespp/Screen.h>
|
||||
#include <cursespp/IMessage.h>
|
||||
|
||||
#define MESSAGE_TYPE_UPDATE 1001
|
||||
#define UPDATE_INTERVAL_MS 1000
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
ConsoleLayout::ConsoleLayout(Transport& transport, LibraryPtr library)
|
||||
: LayoutBase() {
|
||||
this->logs.reset(new LogWindow(this));
|
||||
this->output.reset(new OutputWindow(this));
|
||||
this->resources.reset(new ResourcesWindow(this));
|
||||
|
||||
this->commands.reset(new CommandWindow(
|
||||
this,
|
||||
transport,
|
||||
library,
|
||||
*this->output,
|
||||
*this->logs));
|
||||
|
||||
this->AddWindow(this->commands);
|
||||
this->AddWindow(this->logs);
|
||||
this->AddWindow(this->output);
|
||||
this->AddWindow(this->resources);
|
||||
|
||||
this->PostMessage(MESSAGE_TYPE_UPDATE, 0, 0, UPDATE_INTERVAL_MS);
|
||||
}
|
||||
|
||||
ConsoleLayout::~ConsoleLayout() {
|
||||
|
||||
}
|
||||
|
||||
void ConsoleLayout::Layout() {
|
||||
/* this layout */
|
||||
this->MoveAndResize(
|
||||
0,
|
||||
0,
|
||||
Screen::GetWidth(),
|
||||
Screen::GetHeight());
|
||||
|
||||
this->SetFrameVisible(false);
|
||||
|
||||
/* top left */
|
||||
this->output->MoveAndResize(
|
||||
0,
|
||||
0,
|
||||
Screen::GetWidth() / 2,
|
||||
Screen::GetHeight() - 3);
|
||||
|
||||
this->output->SetFocusOrder(1);
|
||||
|
||||
/* bottom left */
|
||||
this->commands->MoveAndResize(
|
||||
0,
|
||||
Screen::GetHeight() - 3,
|
||||
Screen::GetWidth() / 2,
|
||||
3);
|
||||
|
||||
this->commands->SetFocusOrder(0);
|
||||
|
||||
/* top right */
|
||||
this->logs->MoveAndResize(
|
||||
Screen::GetWidth() / 2,
|
||||
0,
|
||||
Screen::GetWidth() / 2,
|
||||
Screen::GetHeight() - 3);
|
||||
|
||||
this->logs->SetFocusOrder(2);
|
||||
|
||||
/* bottom right */
|
||||
this->resources->MoveAndResize(
|
||||
Screen::GetWidth() / 2,
|
||||
Screen::GetHeight() - 3,
|
||||
Screen::GetWidth() / 2,
|
||||
3);
|
||||
}
|
||||
|
||||
void ConsoleLayout::Show() {
|
||||
LayoutBase::Show();
|
||||
this->UpdateWindows();
|
||||
}
|
||||
|
||||
void ConsoleLayout::ProcessMessage(IMessage &message) {
|
||||
if (message.Type() == MESSAGE_TYPE_UPDATE) {
|
||||
this->UpdateWindows();
|
||||
this->PostMessage(MESSAGE_TYPE_UPDATE, 0, 0, UPDATE_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleLayout::UpdateWindows() {
|
||||
this->logs->Update();
|
||||
this->resources->Update();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ConsoleLayout.h"
|
||||
#include <cursespp/Screen.h>
|
||||
#include <cursespp/IMessage.h>
|
||||
|
||||
#define MESSAGE_TYPE_UPDATE 1001
|
||||
#define UPDATE_INTERVAL_MS 1000
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
ConsoleLayout::ConsoleLayout(Transport& transport, LibraryPtr library)
|
||||
: LayoutBase() {
|
||||
this->logs.reset(new LogWindow(this));
|
||||
this->output.reset(new OutputWindow(this));
|
||||
this->resources.reset(new ResourcesWindow(this));
|
||||
|
||||
this->commands.reset(new CommandWindow(
|
||||
this,
|
||||
transport,
|
||||
library,
|
||||
*this->output,
|
||||
*this->logs));
|
||||
|
||||
this->AddWindow(this->commands);
|
||||
this->AddWindow(this->logs);
|
||||
this->AddWindow(this->output);
|
||||
this->AddWindow(this->resources);
|
||||
|
||||
this->PostMessage(MESSAGE_TYPE_UPDATE, 0, 0, UPDATE_INTERVAL_MS);
|
||||
}
|
||||
|
||||
ConsoleLayout::~ConsoleLayout() {
|
||||
|
||||
}
|
||||
|
||||
void ConsoleLayout::Layout() {
|
||||
/* this layout */
|
||||
this->MoveAndResize(
|
||||
0,
|
||||
0,
|
||||
Screen::GetWidth(),
|
||||
Screen::GetHeight());
|
||||
|
||||
this->SetFrameVisible(false);
|
||||
|
||||
/* top left */
|
||||
this->output->MoveAndResize(
|
||||
0,
|
||||
0,
|
||||
Screen::GetWidth() / 2,
|
||||
Screen::GetHeight() - 3);
|
||||
|
||||
this->output->SetFocusOrder(1);
|
||||
|
||||
/* bottom left */
|
||||
this->commands->MoveAndResize(
|
||||
0,
|
||||
Screen::GetHeight() - 3,
|
||||
Screen::GetWidth() / 2,
|
||||
3);
|
||||
|
||||
this->commands->SetFocusOrder(0);
|
||||
|
||||
/* top right */
|
||||
this->logs->MoveAndResize(
|
||||
Screen::GetWidth() / 2,
|
||||
0,
|
||||
Screen::GetWidth() / 2,
|
||||
Screen::GetHeight() - 3);
|
||||
|
||||
this->logs->SetFocusOrder(2);
|
||||
|
||||
/* bottom right */
|
||||
this->resources->MoveAndResize(
|
||||
Screen::GetWidth() / 2,
|
||||
Screen::GetHeight() - 3,
|
||||
Screen::GetWidth() / 2,
|
||||
3);
|
||||
}
|
||||
|
||||
void ConsoleLayout::Show() {
|
||||
LayoutBase::Show();
|
||||
this->UpdateWindows();
|
||||
}
|
||||
|
||||
void ConsoleLayout::ProcessMessage(IMessage &message) {
|
||||
if (message.Type() == MESSAGE_TYPE_UPDATE) {
|
||||
this->UpdateWindows();
|
||||
this->PostMessage(MESSAGE_TYPE_UPDATE, 0, 0, UPDATE_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleLayout::UpdateWindows() {
|
||||
this->logs->Update();
|
||||
this->resources->Update();
|
||||
}
|
||||
|
@ -30,135 +30,135 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <cursespp/Colors.h>
|
||||
#include <cursespp/Screen.h>
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
|
||||
#include <app/query/CategoryTrackListQuery.h>
|
||||
|
||||
#include "LibraryLayout.h"
|
||||
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
#ifdef WIN32
|
||||
#define TRANSPORT_HEIGHT 3
|
||||
#else
|
||||
#define TRANSPORT_HEIGHT 2
|
||||
#endif
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::library;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
LibraryLayout::LibraryLayout(PlaybackService& playback, LibraryPtr library)
|
||||
: LayoutBase()
|
||||
, playback(playback)
|
||||
, transport(playback.GetTransport()) {
|
||||
this->library = library;
|
||||
this->InitializeWindows();
|
||||
}
|
||||
|
||||
LibraryLayout::~LibraryLayout() {
|
||||
|
||||
}
|
||||
|
||||
void LibraryLayout::Layout() {
|
||||
int x = 0, y = 0;
|
||||
int cx = Screen::GetWidth(), cy = Screen::GetHeight();
|
||||
|
||||
this->MoveAndResize(x, y, cx, cy);
|
||||
|
||||
this->browseLayout->MoveAndResize(x, y, cx, cy - TRANSPORT_HEIGHT);
|
||||
this->browseLayout->Layout();
|
||||
this->nowPlayingLayout->MoveAndResize(x, y, cx, cy - TRANSPORT_HEIGHT);
|
||||
this->nowPlayingLayout->Layout();
|
||||
|
||||
this->transportView->MoveAndResize(
|
||||
1,
|
||||
cy - TRANSPORT_HEIGHT,
|
||||
cx - 2,
|
||||
TRANSPORT_HEIGHT);
|
||||
|
||||
if (!this->visibleLayout) {
|
||||
this->ShowBrowse();
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryLayout::ChangeMainLayout(std::shared_ptr<cursespp::LayoutBase> newLayout) {
|
||||
if (this->visibleLayout != newLayout) {
|
||||
if (this->visibleLayout) {
|
||||
this->RemoveWindow(this->visibleLayout);
|
||||
this->visibleLayout->Hide();
|
||||
}
|
||||
|
||||
this->visibleLayout = newLayout;
|
||||
this->AddWindow(this->visibleLayout);
|
||||
this->visibleLayout->Layout();
|
||||
this->visibleLayout->Show();
|
||||
|
||||
if (this->IsVisible()) {
|
||||
this->BringToTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryLayout::ShowNowPlaying() {
|
||||
this->ChangeMainLayout(this->nowPlayingLayout);
|
||||
}
|
||||
|
||||
void LibraryLayout::ShowBrowse() {
|
||||
this->ChangeMainLayout(this->browseLayout);
|
||||
}
|
||||
|
||||
void LibraryLayout::InitializeWindows() {
|
||||
this->browseLayout.reset(new BrowseLayout(this->playback, this->library));
|
||||
this->nowPlayingLayout.reset(new NowPlayingLayout(this->playback, this->library));
|
||||
this->transportView.reset(new TransportWindow(this->playback));
|
||||
|
||||
this->AddWindow(this->transportView);
|
||||
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
IWindowPtr LibraryLayout::FocusNext() {
|
||||
return this->visibleLayout->FocusNext();
|
||||
}
|
||||
|
||||
IWindowPtr LibraryLayout::FocusPrev() {
|
||||
return this->visibleLayout->FocusPrev();
|
||||
}
|
||||
|
||||
IWindowPtr LibraryLayout::GetFocus() {
|
||||
return this->visibleLayout->GetFocus();
|
||||
}
|
||||
|
||||
bool LibraryLayout::KeyPress(const std::string& key) {
|
||||
if (key == "^[" || key == "M-n") { /* escape switches between browse/now playing */
|
||||
(this->visibleLayout == this->nowPlayingLayout)
|
||||
? this->ShowBrowse() : this->ShowNowPlaying();
|
||||
}
|
||||
/* forward to the visible layout */
|
||||
else if (this->visibleLayout && this->visibleLayout->KeyPress(key)) {
|
||||
return true;
|
||||
}
|
||||
else if (key == " ") {
|
||||
/* copied from GlobalHotkeys. should probably be generalized
|
||||
at some point. */
|
||||
int state = this->transport.GetPlaybackState();
|
||||
if (state == Transport::PlaybackPaused) {
|
||||
this->transport.Resume();
|
||||
}
|
||||
else if (state == Transport::PlaybackPlaying) {
|
||||
this->transport.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
return LayoutBase::KeyPress(key);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <cursespp/Colors.h>
|
||||
#include <cursespp/Screen.h>
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
|
||||
#include <app/query/CategoryTrackListQuery.h>
|
||||
|
||||
#include "LibraryLayout.h"
|
||||
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
#ifdef WIN32
|
||||
#define TRANSPORT_HEIGHT 3
|
||||
#else
|
||||
#define TRANSPORT_HEIGHT 2
|
||||
#endif
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::library;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
LibraryLayout::LibraryLayout(PlaybackService& playback, LibraryPtr library)
|
||||
: LayoutBase()
|
||||
, playback(playback)
|
||||
, transport(playback.GetTransport()) {
|
||||
this->library = library;
|
||||
this->InitializeWindows();
|
||||
}
|
||||
|
||||
LibraryLayout::~LibraryLayout() {
|
||||
|
||||
}
|
||||
|
||||
void LibraryLayout::Layout() {
|
||||
int x = 0, y = 0;
|
||||
int cx = Screen::GetWidth(), cy = Screen::GetHeight();
|
||||
|
||||
this->MoveAndResize(x, y, cx, cy);
|
||||
|
||||
this->browseLayout->MoveAndResize(x, y, cx, cy - TRANSPORT_HEIGHT);
|
||||
this->browseLayout->Layout();
|
||||
this->nowPlayingLayout->MoveAndResize(x, y, cx, cy - TRANSPORT_HEIGHT);
|
||||
this->nowPlayingLayout->Layout();
|
||||
|
||||
this->transportView->MoveAndResize(
|
||||
1,
|
||||
cy - TRANSPORT_HEIGHT,
|
||||
cx - 2,
|
||||
TRANSPORT_HEIGHT);
|
||||
|
||||
if (!this->visibleLayout) {
|
||||
this->ShowBrowse();
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryLayout::ChangeMainLayout(std::shared_ptr<cursespp::LayoutBase> newLayout) {
|
||||
if (this->visibleLayout != newLayout) {
|
||||
if (this->visibleLayout) {
|
||||
this->RemoveWindow(this->visibleLayout);
|
||||
this->visibleLayout->Hide();
|
||||
}
|
||||
|
||||
this->visibleLayout = newLayout;
|
||||
this->AddWindow(this->visibleLayout);
|
||||
this->visibleLayout->Layout();
|
||||
this->visibleLayout->Show();
|
||||
|
||||
if (this->IsVisible()) {
|
||||
this->BringToTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryLayout::ShowNowPlaying() {
|
||||
this->ChangeMainLayout(this->nowPlayingLayout);
|
||||
}
|
||||
|
||||
void LibraryLayout::ShowBrowse() {
|
||||
this->ChangeMainLayout(this->browseLayout);
|
||||
}
|
||||
|
||||
void LibraryLayout::InitializeWindows() {
|
||||
this->browseLayout.reset(new BrowseLayout(this->playback, this->library));
|
||||
this->nowPlayingLayout.reset(new NowPlayingLayout(this->playback, this->library));
|
||||
this->transportView.reset(new TransportWindow(this->playback));
|
||||
|
||||
this->AddWindow(this->transportView);
|
||||
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
IWindowPtr LibraryLayout::FocusNext() {
|
||||
return this->visibleLayout->FocusNext();
|
||||
}
|
||||
|
||||
IWindowPtr LibraryLayout::FocusPrev() {
|
||||
return this->visibleLayout->FocusPrev();
|
||||
}
|
||||
|
||||
IWindowPtr LibraryLayout::GetFocus() {
|
||||
return this->visibleLayout->GetFocus();
|
||||
}
|
||||
|
||||
bool LibraryLayout::KeyPress(const std::string& key) {
|
||||
if (key == "^[" || key == "M-n") { /* escape switches between browse/now playing */
|
||||
(this->visibleLayout == this->nowPlayingLayout)
|
||||
? this->ShowBrowse() : this->ShowNowPlaying();
|
||||
}
|
||||
/* forward to the visible layout */
|
||||
else if (this->visibleLayout && this->visibleLayout->KeyPress(key)) {
|
||||
return true;
|
||||
}
|
||||
else if (key == " ") {
|
||||
/* copied from GlobalHotkeys. should probably be generalized
|
||||
at some point. */
|
||||
int state = this->transport.GetPlaybackState();
|
||||
if (state == Transport::PlaybackPaused) {
|
||||
this->transport.Resume();
|
||||
}
|
||||
else if (state == Transport::PlaybackPlaying) {
|
||||
this->transport.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
return LayoutBase::KeyPress(key);
|
||||
}
|
||||
|
@ -30,91 +30,91 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <cursespp/Colors.h>
|
||||
#include <cursespp/Screen.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <app/query/NowPlayingTrackListQuery.h>
|
||||
#include "NowPlayingLayout.h"
|
||||
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::library;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
NowPlayingLayout::NowPlayingLayout(
|
||||
PlaybackService& playback,
|
||||
musik::core::LibraryPtr library)
|
||||
: LayoutBase()
|
||||
, playback(playback)
|
||||
, library(library) {
|
||||
this->InitializeWindows();
|
||||
}
|
||||
|
||||
NowPlayingLayout::~NowPlayingLayout() {
|
||||
|
||||
}
|
||||
|
||||
void NowPlayingLayout::Layout() {
|
||||
size_t cx = this->GetWidth(), cy = this->GetHeight();
|
||||
|
||||
if (cx && cy) {
|
||||
this->trackList->MoveAndResize(
|
||||
0,
|
||||
0,
|
||||
this->GetWidth(),
|
||||
this->GetHeight());
|
||||
|
||||
this->trackList->SetFocusOrder(1);
|
||||
}
|
||||
}
|
||||
|
||||
void NowPlayingLayout::InitializeWindows() {
|
||||
this->trackList.reset(new TrackListView(this->playback, this->library));
|
||||
this->trackList->Requeried.connect(this, &NowPlayingLayout::OnTrackListRequeried);
|
||||
this->AddWindow(this->trackList);
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
IWindowPtr NowPlayingLayout::GetFocus() {
|
||||
return this->trackList;
|
||||
}
|
||||
|
||||
void NowPlayingLayout::OnVisibilityChanged(bool visible) {
|
||||
LayoutBase::OnVisibilityChanged(visible);
|
||||
|
||||
if (visible) {
|
||||
this->RequeryTrackList();
|
||||
}
|
||||
else {
|
||||
this->trackList->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void NowPlayingLayout::OnTrackListRequeried() {
|
||||
if (playback.Count()) {
|
||||
size_t index = playback.GetIndex();
|
||||
this->trackList->SetSelectedIndex(index);
|
||||
this->trackList->ScrollTo(index);
|
||||
}
|
||||
}
|
||||
|
||||
void NowPlayingLayout::RequeryTrackList() {
|
||||
this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
|
||||
new NowPlayingTrackListQuery(this->playback)));
|
||||
}
|
||||
|
||||
bool NowPlayingLayout::KeyPress(const std::string& key) {
|
||||
if (key == "^M") { /* enter. play the selection */
|
||||
this->playback.Play(this->trackList->GetSelectedIndex());
|
||||
return true;
|
||||
}
|
||||
|
||||
return LayoutBase::KeyPress(key);
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <cursespp/Colors.h>
|
||||
#include <cursespp/Screen.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <app/query/NowPlayingTrackListQuery.h>
|
||||
#include "NowPlayingLayout.h"
|
||||
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::library;
|
||||
using namespace musik::box;
|
||||
using namespace cursespp;
|
||||
|
||||
NowPlayingLayout::NowPlayingLayout(
|
||||
PlaybackService& playback,
|
||||
musik::core::LibraryPtr library)
|
||||
: LayoutBase()
|
||||
, playback(playback)
|
||||
, library(library) {
|
||||
this->InitializeWindows();
|
||||
}
|
||||
|
||||
NowPlayingLayout::~NowPlayingLayout() {
|
||||
|
||||
}
|
||||
|
||||
void NowPlayingLayout::Layout() {
|
||||
size_t cx = this->GetWidth(), cy = this->GetHeight();
|
||||
|
||||
if (cx && cy) {
|
||||
this->trackList->MoveAndResize(
|
||||
0,
|
||||
0,
|
||||
this->GetWidth(),
|
||||
this->GetHeight());
|
||||
|
||||
this->trackList->SetFocusOrder(1);
|
||||
}
|
||||
}
|
||||
|
||||
void NowPlayingLayout::InitializeWindows() {
|
||||
this->trackList.reset(new TrackListView(this->playback, this->library));
|
||||
this->trackList->Requeried.connect(this, &NowPlayingLayout::OnTrackListRequeried);
|
||||
this->AddWindow(this->trackList);
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
IWindowPtr NowPlayingLayout::GetFocus() {
|
||||
return this->trackList;
|
||||
}
|
||||
|
||||
void NowPlayingLayout::OnVisibilityChanged(bool visible) {
|
||||
LayoutBase::OnVisibilityChanged(visible);
|
||||
|
||||
if (visible) {
|
||||
this->RequeryTrackList();
|
||||
}
|
||||
else {
|
||||
this->trackList->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void NowPlayingLayout::OnTrackListRequeried() {
|
||||
if (playback.Count()) {
|
||||
size_t index = playback.GetIndex();
|
||||
this->trackList->SetSelectedIndex(index);
|
||||
this->trackList->ScrollTo(index);
|
||||
}
|
||||
}
|
||||
|
||||
void NowPlayingLayout::RequeryTrackList() {
|
||||
this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
|
||||
new NowPlayingTrackListQuery(this->playback)));
|
||||
}
|
||||
|
||||
bool NowPlayingLayout::KeyPress(const std::string& key) {
|
||||
if (key == "^M") { /* enter. play the selection */
|
||||
this->playback.Play(this->trackList->GetSelectedIndex());
|
||||
return true;
|
||||
}
|
||||
|
||||
return LayoutBase::KeyPress(key);
|
||||
}
|
@ -30,92 +30,92 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CategoryListViewQuery.h"
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
|
||||
using namespace musik::core::db;
|
||||
using namespace musik::core::library::constants;
|
||||
using namespace musik::box;
|
||||
|
||||
#define RESET_RESULT(x) x.reset(new std::vector<std::shared_ptr<Result> >);
|
||||
|
||||
static const std::string ALBUM_QUERY =
|
||||
"SELECT DISTINCT albums.id, albums.name "
|
||||
"FROM albums, tracks "
|
||||
"WHERE albums.id = tracks.album_id "
|
||||
"ORDER BY albums.sort_order;";
|
||||
|
||||
static const std::string ARTIST_QUERY =
|
||||
"SELECT DISTINCT artists.id, artists.name "
|
||||
"FROM artists, tracks "
|
||||
"WHERE artists.id = tracks.visual_artist_id "
|
||||
"ORDER BY artists.sort_order;";
|
||||
|
||||
static const std::string GENRE_QUERY =
|
||||
"SELECT DISTINCT genres.id, genres.name "
|
||||
"FROM genres, tracks "
|
||||
"WHERE genres.id = tracks.visual_genre_id "
|
||||
"ORDER BY genres.sort_order;";
|
||||
|
||||
static boost::mutex QUERY_MAP_MUTEX;
|
||||
static std::map<std::string, std::string> FIELD_TO_QUERY_MAP;
|
||||
|
||||
static void initFieldToQueryMap() {
|
||||
FIELD_TO_QUERY_MAP[Track::ALBUM] = ALBUM_QUERY;
|
||||
FIELD_TO_QUERY_MAP[Track::ARTIST] = ARTIST_QUERY;
|
||||
FIELD_TO_QUERY_MAP[Track::GENRE] = GENRE_QUERY;
|
||||
}
|
||||
|
||||
CategoryListViewQuery::CategoryListViewQuery(const std::string& trackField) {
|
||||
this->trackField = trackField;
|
||||
|
||||
RESET_RESULT(result);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(QUERY_MAP_MUTEX);
|
||||
|
||||
if (!FIELD_TO_QUERY_MAP.size()) {
|
||||
initFieldToQueryMap();
|
||||
}
|
||||
}
|
||||
|
||||
if (FIELD_TO_QUERY_MAP.find(trackField) == FIELD_TO_QUERY_MAP.end()) {
|
||||
throw "invalid field for CategoryListView specified";
|
||||
}
|
||||
}
|
||||
|
||||
CategoryListViewQuery::~CategoryListViewQuery() {
|
||||
|
||||
}
|
||||
|
||||
CategoryListViewQuery::ResultList CategoryListViewQuery::GetResult() {
|
||||
return this->result;
|
||||
}
|
||||
|
||||
bool CategoryListViewQuery::OnRun(Connection& db) {
|
||||
RESET_RESULT(result);
|
||||
|
||||
std::string query = FIELD_TO_QUERY_MAP[this->trackField];
|
||||
Statement stmt(query.c_str(), db);
|
||||
|
||||
while (stmt.Step() == Row) {
|
||||
std::shared_ptr<Result> row(new Result());
|
||||
row->id = stmt.ColumnInt64(0);
|
||||
row->displayValue = stmt.ColumnText(1);
|
||||
result->push_back(row);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CategoryListViewQuery.h"
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
|
||||
using namespace musik::core::db;
|
||||
using namespace musik::core::library::constants;
|
||||
using namespace musik::box;
|
||||
|
||||
#define RESET_RESULT(x) x.reset(new std::vector<std::shared_ptr<Result> >);
|
||||
|
||||
static const std::string ALBUM_QUERY =
|
||||
"SELECT DISTINCT albums.id, albums.name "
|
||||
"FROM albums, tracks "
|
||||
"WHERE albums.id = tracks.album_id "
|
||||
"ORDER BY albums.sort_order;";
|
||||
|
||||
static const std::string ARTIST_QUERY =
|
||||
"SELECT DISTINCT artists.id, artists.name "
|
||||
"FROM artists, tracks "
|
||||
"WHERE artists.id = tracks.visual_artist_id "
|
||||
"ORDER BY artists.sort_order;";
|
||||
|
||||
static const std::string GENRE_QUERY =
|
||||
"SELECT DISTINCT genres.id, genres.name "
|
||||
"FROM genres, tracks "
|
||||
"WHERE genres.id = tracks.visual_genre_id "
|
||||
"ORDER BY genres.sort_order;";
|
||||
|
||||
static boost::mutex QUERY_MAP_MUTEX;
|
||||
static std::map<std::string, std::string> FIELD_TO_QUERY_MAP;
|
||||
|
||||
static void initFieldToQueryMap() {
|
||||
FIELD_TO_QUERY_MAP[Track::ALBUM] = ALBUM_QUERY;
|
||||
FIELD_TO_QUERY_MAP[Track::ARTIST] = ARTIST_QUERY;
|
||||
FIELD_TO_QUERY_MAP[Track::GENRE] = GENRE_QUERY;
|
||||
}
|
||||
|
||||
CategoryListViewQuery::CategoryListViewQuery(const std::string& trackField) {
|
||||
this->trackField = trackField;
|
||||
|
||||
RESET_RESULT(result);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(QUERY_MAP_MUTEX);
|
||||
|
||||
if (!FIELD_TO_QUERY_MAP.size()) {
|
||||
initFieldToQueryMap();
|
||||
}
|
||||
}
|
||||
|
||||
if (FIELD_TO_QUERY_MAP.find(trackField) == FIELD_TO_QUERY_MAP.end()) {
|
||||
throw "invalid field for CategoryListView specified";
|
||||
}
|
||||
}
|
||||
|
||||
CategoryListViewQuery::~CategoryListViewQuery() {
|
||||
|
||||
}
|
||||
|
||||
CategoryListViewQuery::ResultList CategoryListViewQuery::GetResult() {
|
||||
return this->result;
|
||||
}
|
||||
|
||||
bool CategoryListViewQuery::OnRun(Connection& db) {
|
||||
RESET_RESULT(result);
|
||||
|
||||
std::string query = FIELD_TO_QUERY_MAP[this->trackField];
|
||||
Statement stmt(query.c_str(), db);
|
||||
|
||||
while (stmt.Step() == Row) {
|
||||
std::shared_ptr<Result> row(new Result());
|
||||
row->id = stmt.ColumnInt64(0);
|
||||
row->displayValue = stmt.ColumnText(1);
|
||||
result->push_back(row);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -30,117 +30,117 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CategoryTrackListQuery.h"
|
||||
|
||||
#include <core/library/track/LibraryTrack.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
using musik::core::TrackPtr;
|
||||
using musik::core::LibraryTrack;
|
||||
using musik::core::LibraryPtr;
|
||||
|
||||
using namespace musik::core::db;
|
||||
using namespace musik::core::library::constants;
|
||||
using namespace musik::box;
|
||||
|
||||
static std::map<std::string, std::string> FIELD_TO_FOREIGN_KEY =
|
||||
{
|
||||
std::make_pair(Track::ALBUM, Track::ALBUM_ID),
|
||||
std::make_pair(Track::ARTIST, Track::ARTIST_ID),
|
||||
std::make_pair(Track::GENRE, Track::GENRE_ID),
|
||||
std::make_pair(Track::ALBUM_ARTIST, Track::ALBUM_ARTIST_ID)
|
||||
};
|
||||
|
||||
CategoryTrackListQuery::CategoryTrackListQuery(LibraryPtr library, const std::string& column, DBID id) {
|
||||
this->library = library;
|
||||
this->id = id;
|
||||
this->result.reset(new std::vector<TrackPtr>());
|
||||
this->headers.reset(new std::set<size_t>());
|
||||
this->hash = 0;
|
||||
|
||||
if (FIELD_TO_FOREIGN_KEY.find(column) == FIELD_TO_FOREIGN_KEY.end()) {
|
||||
throw std::runtime_error("invalid input column specified");
|
||||
}
|
||||
|
||||
this->column = FIELD_TO_FOREIGN_KEY[column];
|
||||
}
|
||||
|
||||
CategoryTrackListQuery::~CategoryTrackListQuery() {
|
||||
|
||||
}
|
||||
|
||||
CategoryTrackListQuery::Result CategoryTrackListQuery::GetResult() {
|
||||
return this->result;
|
||||
}
|
||||
|
||||
CategoryTrackListQuery::Headers CategoryTrackListQuery::GetHeaders() {
|
||||
return this->headers;
|
||||
}
|
||||
|
||||
size_t CategoryTrackListQuery::GetQueryHash() {
|
||||
if (this->hash == 0) {
|
||||
std::string parts = boost::str(
|
||||
boost::format("%s-%s") % this->column % this->id);
|
||||
|
||||
this->hash = std::hash<std::string>()(parts);
|
||||
}
|
||||
|
||||
return this->hash;
|
||||
}
|
||||
|
||||
bool CategoryTrackListQuery::OnRun(Connection& db) {
|
||||
if (result) {
|
||||
result.reset(new std::vector<TrackPtr>());
|
||||
headers.reset(new std::set<size_t>());
|
||||
}
|
||||
|
||||
this->query = boost::str(boost::format(
|
||||
"SELECT DISTINCT t.id, t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name AS album, gn.name AS genre, ar.name AS artist, t.filetime " \
|
||||
"FROM tracks t, paths p, albums al, artists ar, genres gn " \
|
||||
"WHERE t.%s=? AND t.album_id=al.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id "
|
||||
"ORDER BY album, disc, track, artist") % this->column);
|
||||
|
||||
std::string lastAlbum;
|
||||
size_t index = 0;
|
||||
|
||||
Statement trackQuery(this->query.c_str(), db);
|
||||
|
||||
trackQuery.BindInt(0, this->id);
|
||||
while (trackQuery.Step() == Row) {
|
||||
std::string album = trackQuery.ColumnText(10);
|
||||
DBID id = trackQuery.ColumnInt64(0);
|
||||
|
||||
if (album != lastAlbum) {
|
||||
headers->insert(index);
|
||||
lastAlbum = album;
|
||||
}
|
||||
|
||||
TrackPtr track = TrackPtr(new LibraryTrack(id, this->library));
|
||||
track->SetValue(Track::TRACK_NUM, trackQuery.ColumnText(1));
|
||||
track->SetValue(Track::DISC_NUM, trackQuery.ColumnText(2));
|
||||
track->SetValue(Track::BPM, trackQuery.ColumnText(3));
|
||||
track->SetValue(Track::DURATION, trackQuery.ColumnText(4));
|
||||
track->SetValue(Track::FILESIZE, trackQuery.ColumnText(5));
|
||||
track->SetValue(Track::YEAR, trackQuery.ColumnText(6));
|
||||
track->SetValue(Track::TITLE, trackQuery.ColumnText(7));
|
||||
track->SetValue(Track::FILENAME, trackQuery.ColumnText(8));
|
||||
track->SetValue(Track::THUMBNAIL_ID, trackQuery.ColumnText(9));
|
||||
track->SetValue(Track::ALBUM, album.c_str());
|
||||
track->SetValue(Track::GENRE, trackQuery.ColumnText(11));
|
||||
track->SetValue(Track::ARTIST, trackQuery.ColumnText(12));
|
||||
track->SetValue(Track::FILETIME, trackQuery.ColumnText(13));
|
||||
|
||||
result->push_back(track);
|
||||
++index;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CategoryTrackListQuery.h"
|
||||
|
||||
#include <core/library/track/LibraryTrack.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
using musik::core::TrackPtr;
|
||||
using musik::core::LibraryTrack;
|
||||
using musik::core::LibraryPtr;
|
||||
|
||||
using namespace musik::core::db;
|
||||
using namespace musik::core::library::constants;
|
||||
using namespace musik::box;
|
||||
|
||||
static std::map<std::string, std::string> FIELD_TO_FOREIGN_KEY =
|
||||
{
|
||||
std::make_pair(Track::ALBUM, Track::ALBUM_ID),
|
||||
std::make_pair(Track::ARTIST, Track::ARTIST_ID),
|
||||
std::make_pair(Track::GENRE, Track::GENRE_ID),
|
||||
std::make_pair(Track::ALBUM_ARTIST, Track::ALBUM_ARTIST_ID)
|
||||
};
|
||||
|
||||
CategoryTrackListQuery::CategoryTrackListQuery(LibraryPtr library, const std::string& column, DBID id) {
|
||||
this->library = library;
|
||||
this->id = id;
|
||||
this->result.reset(new std::vector<TrackPtr>());
|
||||
this->headers.reset(new std::set<size_t>());
|
||||
this->hash = 0;
|
||||
|
||||
if (FIELD_TO_FOREIGN_KEY.find(column) == FIELD_TO_FOREIGN_KEY.end()) {
|
||||
throw std::runtime_error("invalid input column specified");
|
||||
}
|
||||
|
||||
this->column = FIELD_TO_FOREIGN_KEY[column];
|
||||
}
|
||||
|
||||
CategoryTrackListQuery::~CategoryTrackListQuery() {
|
||||
|
||||
}
|
||||
|
||||
CategoryTrackListQuery::Result CategoryTrackListQuery::GetResult() {
|
||||
return this->result;
|
||||
}
|
||||
|
||||
CategoryTrackListQuery::Headers CategoryTrackListQuery::GetHeaders() {
|
||||
return this->headers;
|
||||
}
|
||||
|
||||
size_t CategoryTrackListQuery::GetQueryHash() {
|
||||
if (this->hash == 0) {
|
||||
std::string parts = boost::str(
|
||||
boost::format("%s-%s") % this->column % this->id);
|
||||
|
||||
this->hash = std::hash<std::string>()(parts);
|
||||
}
|
||||
|
||||
return this->hash;
|
||||
}
|
||||
|
||||
bool CategoryTrackListQuery::OnRun(Connection& db) {
|
||||
if (result) {
|
||||
result.reset(new std::vector<TrackPtr>());
|
||||
headers.reset(new std::set<size_t>());
|
||||
}
|
||||
|
||||
this->query = boost::str(boost::format(
|
||||
"SELECT DISTINCT t.id, t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name AS album, gn.name AS genre, ar.name AS artist, t.filetime " \
|
||||
"FROM tracks t, paths p, albums al, artists ar, genres gn " \
|
||||
"WHERE t.%s=? AND t.album_id=al.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id "
|
||||
"ORDER BY album, disc, track, artist") % this->column);
|
||||
|
||||
std::string lastAlbum;
|
||||
size_t index = 0;
|
||||
|
||||
Statement trackQuery(this->query.c_str(), db);
|
||||
|
||||
trackQuery.BindInt(0, this->id);
|
||||
while (trackQuery.Step() == Row) {
|
||||
std::string album = trackQuery.ColumnText(10);
|
||||
DBID id = trackQuery.ColumnInt64(0);
|
||||
|
||||
if (album != lastAlbum) {
|
||||
headers->insert(index);
|
||||
lastAlbum = album;
|
||||
}
|
||||
|
||||
TrackPtr track = TrackPtr(new LibraryTrack(id, this->library));
|
||||
track->SetValue(Track::TRACK_NUM, trackQuery.ColumnText(1));
|
||||
track->SetValue(Track::DISC_NUM, trackQuery.ColumnText(2));
|
||||
track->SetValue(Track::BPM, trackQuery.ColumnText(3));
|
||||
track->SetValue(Track::DURATION, trackQuery.ColumnText(4));
|
||||
track->SetValue(Track::FILESIZE, trackQuery.ColumnText(5));
|
||||
track->SetValue(Track::YEAR, trackQuery.ColumnText(6));
|
||||
track->SetValue(Track::TITLE, trackQuery.ColumnText(7));
|
||||
track->SetValue(Track::FILENAME, trackQuery.ColumnText(8));
|
||||
track->SetValue(Track::THUMBNAIL_ID, trackQuery.ColumnText(9));
|
||||
track->SetValue(Track::ALBUM, album.c_str());
|
||||
track->SetValue(Track::GENRE, trackQuery.ColumnText(11));
|
||||
track->SetValue(Track::ARTIST, trackQuery.ColumnText(12));
|
||||
track->SetValue(Track::FILETIME, trackQuery.ColumnText(13));
|
||||
|
||||
result->push_back(track);
|
||||
++index;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -30,59 +30,59 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "NowPlayingTrackListQuery.h"
|
||||
|
||||
#include <core/library/track/LibraryTrack.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
using musik::core::TrackPtr;
|
||||
using musik::core::LibraryTrack;
|
||||
using musik::core::LibraryPtr;
|
||||
|
||||
using namespace musik::core::db;
|
||||
using namespace musik::core::library::constants;
|
||||
using namespace musik::box;
|
||||
|
||||
NowPlayingTrackListQuery::NowPlayingTrackListQuery(PlaybackService& playback)
|
||||
: playback(playback) {
|
||||
this->result.reset(new std::vector<TrackPtr>());
|
||||
this->headers.reset(new std::set<size_t>());
|
||||
this->hash = 0;
|
||||
}
|
||||
|
||||
NowPlayingTrackListQuery::~NowPlayingTrackListQuery() {
|
||||
|
||||
}
|
||||
|
||||
NowPlayingTrackListQuery::Result NowPlayingTrackListQuery::GetResult() {
|
||||
return this->result;
|
||||
}
|
||||
|
||||
NowPlayingTrackListQuery::Headers NowPlayingTrackListQuery::GetHeaders() {
|
||||
return this->headers;
|
||||
}
|
||||
|
||||
size_t NowPlayingTrackListQuery::GetQueryHash() {
|
||||
if (this->hash == 0) {
|
||||
this->hash = std::hash<std::string>()(this->Name());
|
||||
}
|
||||
|
||||
return this->hash;
|
||||
}
|
||||
|
||||
bool NowPlayingTrackListQuery::OnRun(Connection& db) {
|
||||
if (result) {
|
||||
result.reset(new std::vector<TrackPtr>());
|
||||
headers.reset(new std::set<size_t>());
|
||||
}
|
||||
|
||||
this->playback.Copy(*result);
|
||||
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "NowPlayingTrackListQuery.h"
|
||||
|
||||
#include <core/library/track/LibraryTrack.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
using musik::core::TrackPtr;
|
||||
using musik::core::LibraryTrack;
|
||||
using musik::core::LibraryPtr;
|
||||
|
||||
using namespace musik::core::db;
|
||||
using namespace musik::core::library::constants;
|
||||
using namespace musik::box;
|
||||
|
||||
NowPlayingTrackListQuery::NowPlayingTrackListQuery(PlaybackService& playback)
|
||||
: playback(playback) {
|
||||
this->result.reset(new std::vector<TrackPtr>());
|
||||
this->headers.reset(new std::set<size_t>());
|
||||
this->hash = 0;
|
||||
}
|
||||
|
||||
NowPlayingTrackListQuery::~NowPlayingTrackListQuery() {
|
||||
|
||||
}
|
||||
|
||||
NowPlayingTrackListQuery::Result NowPlayingTrackListQuery::GetResult() {
|
||||
return this->result;
|
||||
}
|
||||
|
||||
NowPlayingTrackListQuery::Headers NowPlayingTrackListQuery::GetHeaders() {
|
||||
return this->headers;
|
||||
}
|
||||
|
||||
size_t NowPlayingTrackListQuery::GetQueryHash() {
|
||||
if (this->hash == 0) {
|
||||
this->hash = std::hash<std::string>()(this->Name());
|
||||
}
|
||||
|
||||
return this->hash;
|
||||
}
|
||||
|
||||
bool NowPlayingTrackListQuery::OnRun(Connection& db) {
|
||||
if (result) {
|
||||
result.reset(new std::vector<TrackPtr>());
|
||||
headers.reset(new std::set<size_t>());
|
||||
}
|
||||
|
||||
this->playback.Copy(*result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user