More CRLF -> LF conversion.

This commit is contained in:
casey langen 2016-06-11 18:15:05 -07:00
parent c7cba8984d
commit 15d3160cf5
127 changed files with 21264 additions and 21264 deletions

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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

View 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;
}

View File

@ -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();
}

View File

@ -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"

View File

@ -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

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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";
}

View File

@ -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();
}

View File

@ -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"

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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++;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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

View 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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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"

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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"

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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");
}
}
}

View File

@ -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"";
}

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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() {
}

View File

@ -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() {
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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);
}
}
}
}

View File

@ -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());
}

View File

@ -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(...) {
}
}

View File

@ -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;
}

View File

@ -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)");
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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