diff --git a/src/contrib/alsaout/alsaout_plugin.cpp b/src/contrib/alsaout/alsaout_plugin.cpp index c6cdce220..aaed68fba 100755 --- a/src/contrib/alsaout/alsaout_plugin.cpp +++ b/src/contrib/alsaout/alsaout_plugin.cpp @@ -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 -#include -#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 +#include +#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(); +} diff --git a/src/contrib/apedecoder/APEDecoder.cpp b/src/contrib/apedecoder/APEDecoder.cpp index 1a4f111ed..d2a17cd7f 100644 --- a/src/contrib/apedecoder/APEDecoder.cpp +++ b/src/contrib/apedecoder/APEDecoder.cpp @@ -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> 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<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> 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< - -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 + +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(); +} diff --git a/src/contrib/apedecoder/stdafx.cpp b/src/contrib/apedecoder/stdafx.cpp index 28b3fef26..0da753787 100644 --- a/src/contrib/apedecoder/stdafx.cpp +++ b/src/contrib/apedecoder/stdafx.cpp @@ -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 diff --git a/src/contrib/bpm_analyzer/BPMAnalyzer.cpp b/src/contrib/bpm_analyzer/BPMAnalyzer.cpp index 0f187febe..8b0cff8e1 100644 --- a/src/contrib/bpm_analyzer/BPMAnalyzer.cpp +++ b/src/contrib/bpm_analyzer/BPMAnalyzer.cpp @@ -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 -#include - -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(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 +#include + +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(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; +} + diff --git a/src/contrib/bpm_analyzer/bpm_analyzer_plugin.cpp b/src/contrib/bpm_analyzer/bpm_analyzer_plugin.cpp index cebe84290..194dd34d5 100644 --- a/src/contrib/bpm_analyzer/bpm_analyzer_plugin.cpp +++ b/src/contrib/bpm_analyzer/bpm_analyzer_plugin.cpp @@ -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 -#include -#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 +#include +#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(); +} diff --git a/src/contrib/bpm_analyzer/pch.cpp b/src/contrib/bpm_analyzer/pch.cpp index ac4ae04c7..815f8a271 100644 --- a/src/contrib/bpm_analyzer/pch.cpp +++ b/src/contrib/bpm_analyzer/pch.cpp @@ -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" \ No newline at end of file diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/3dnow_win.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/3dnow_win.cpp index b13ac8411..3397222f9 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/3dnow_win.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/3dnow_win.cpp @@ -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 - -// 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 + +// 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 diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/AAFilter.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/AAFilter.cpp index 9a5cf539c..6bf529cce 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/AAFilter.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/AAFilter.cpp @@ -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 -#include -#include -#include -#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 +#include +#include +#include +#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(); +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp index 1766739a6..c33e3c58b 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp @@ -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 -#include -#include -#include -#include - -#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 +#include +#include +#include +#include + +#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; +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/FIRFilter.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/FIRFilter.cpp index 55d5cdc7e..2229c419d 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/FIRFilter.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/FIRFilter.cpp @@ -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 -#include -#include -#include -#include -#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 +#include +#include +#include +#include +#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; + } +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/RateTransposer.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/RateTransposer.cpp index b7414b90a..9e5c962d1 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/RateTransposer.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/RateTransposer.cpp @@ -1,626 +1,626 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Sample rate transposer. Changes sample rate by using linear interpolation -/// together with anti-alias filtering (first order interpolation with anti- -/// alias filtering should be quite adequate for this application) -/// -/// Author : Copyright (c) Olli Parviainen -/// Author e-mail : oparviai 'at' iki.fi -/// SoundTouch WWW: http://www.surina.net/soundtouch -/// -//////////////////////////////////////////////////////////////////////////////// -// -// Last changed : $Date: 2006/03/19 10:05:49 $ -// File revision : $Revision: 1.13 $ -// -// $Id: RateTransposer.cpp,v 1.13 2006/03/19 10:05:49 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 -#include -#include -#include -#include -#include "RateTransposer.h" -#include "AAFilter.h" - -using namespace soundtouch; - - -/// A linear samplerate transposer class that uses integer arithmetics. -/// for the transposing. -class RateTransposerInteger : public RateTransposer -{ -protected: - int iSlopeCount; - uint uRate; - SAMPLETYPE sPrevSampleL, sPrevSampleR; - - virtual void resetRegisters(); - - virtual uint transposeStereo(SAMPLETYPE *dest, - const SAMPLETYPE *src, - uint numSamples); - virtual uint transposeMono(SAMPLETYPE *dest, - const SAMPLETYPE *src, - uint numSamples); - -public: - RateTransposerInteger(); - virtual ~RateTransposerInteger(); - - /// Sets new target rate. Normal rate = 1.0, smaller values represent slower - /// rate, larger faster rates. - virtual void setRate(float newRate); - -}; - - -/// A linear samplerate transposer class that uses floating point arithmetics -/// for the transposing. -class RateTransposerFloat : public RateTransposer -{ -protected: - float fSlopeCount; - float fRateStep; - SAMPLETYPE sPrevSampleL, sPrevSampleR; - - virtual void resetRegisters(); - - virtual uint transposeStereo(SAMPLETYPE *dest, - const SAMPLETYPE *src, - uint numSamples); - virtual uint transposeMono(SAMPLETYPE *dest, - const SAMPLETYPE *src, - uint numSamples); - -public: - RateTransposerFloat(); - virtual ~RateTransposerFloat(); -}; - - - -#ifndef min -#define min(a,b) ((a > b) ? b : a) -#define max(a,b) ((a < b) ? b : a) -#endif - - -// Operator 'new' is overloaded so that it automatically creates a suitable instance -// depending on if we've a MMX/SSE/etc-capable CPU available or not. -void * RateTransposer::operator new(size_t s) -{ - // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! - assert(FALSE); - return NULL; -} - - -RateTransposer *RateTransposer::newInstance() -{ -#ifdef INTEGER_SAMPLES - return ::new RateTransposerInteger; -#else - return ::new RateTransposerFloat; -#endif -} - - -// Constructor -RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer) -{ - uChannels = 2; - bUseAAFilter = TRUE; - - // Instantiates the anti-alias filter with default tap length - // of 32 - pAAFilter = new AAFilter(32); -} - - - -RateTransposer::~RateTransposer() -{ - delete pAAFilter; -} - - - -/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable -void RateTransposer::enableAAFilter(const BOOL newMode) -{ - bUseAAFilter = newMode; -} - - -/// Returns nonzero if anti-alias filter is enabled. -BOOL RateTransposer::isAAFilterEnabled() const -{ - return bUseAAFilter; -} - - -AAFilter *RateTransposer::getAAFilter() const -{ - return pAAFilter; -} - - - -// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower -// uRate, larger faster uRates. -void RateTransposer::setRate(float newRate) -{ - float fCutoff; - - fRate = newRate; - - // design a new anti-alias filter - if (newRate > 1.0f) - { - fCutoff = 0.5f / newRate; - } - else - { - fCutoff = 0.5f * newRate; - } - pAAFilter->setCutoffFreq(fCutoff); -} - - -// Outputs as many samples of the 'outputBuffer' as possible, and if there's -// any room left, outputs also as many of the incoming samples as possible. -// The goal is to drive the outputBuffer empty. -// -// It's allowed for 'output' and 'input' parameters to point to the same -// memory position. -void RateTransposer::flushStoreBuffer() -{ - if (storeBuffer.isEmpty()) return; - - outputBuffer.moveSamples(storeBuffer); -} - - -// Adds 'numSamples' pcs of samples from the 'samples' memory position into -// the input of the object. -void RateTransposer::putSamples(const SAMPLETYPE *samples, uint numSamples) -{ - processSamples(samples, numSamples); -} - - - -// Transposes up the sample rate, causing the observed playback 'rate' of the -// sound to decrease -void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples) -{ - int count, sizeTemp, num; - - // If the parameter 'uRate' value is smaller than 'SCALE', first transpose - // the samples and then apply the anti-alias filter to remove aliasing. - - // First check that there's enough room in 'storeBuffer' - // (+16 is to reserve some slack in the destination buffer) - sizeTemp = (int)((float)numSamples / fRate + 16.0f); - - // Transpose the samples, store the result into the end of "storeBuffer" - count = transpose(storeBuffer.ptrEnd(sizeTemp), src, numSamples); - storeBuffer.putSamples(count); - - // Apply the anti-alias filter to samples in "store output", output the - // result to "dest" - num = storeBuffer.numSamples(); - count = pAAFilter->evaluate(outputBuffer.ptrEnd(num), - storeBuffer.ptrBegin(), num, uChannels); - outputBuffer.putSamples(count); - - // Remove the processed samples from "storeBuffer" - storeBuffer.receiveSamples(count); -} - - -// Transposes down the sample rate, causing the observed playback 'rate' of the -// sound to increase -void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples) -{ - int count, sizeTemp; - - // If the parameter 'uRate' value is larger than 'SCALE', first apply the - // anti-alias filter to remove high frequencies (prevent them from folding - // over the lover frequencies), then transpose. */ - - // Add the new samples to the end of the storeBuffer */ - storeBuffer.putSamples(src, numSamples); - - // Anti-alias filter the samples to prevent folding and output the filtered - // data to tempBuffer. Note : because of the FIR filter length, the - // filtering routine takes in 'filter_length' more samples than it outputs. - assert(tempBuffer.isEmpty()); - sizeTemp = storeBuffer.numSamples(); - - count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp), - storeBuffer.ptrBegin(), sizeTemp, uChannels); - - // Remove the filtered samples from 'storeBuffer' - storeBuffer.receiveSamples(count); - - // Transpose the samples (+16 is to reserve some slack in the destination buffer) - sizeTemp = (int)((float)numSamples / fRate + 16.0f); - count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count); - outputBuffer.putSamples(count); -} - - -// Transposes sample rate by applying anti-alias filter to prevent folding. -// Returns amount of samples returned in the "dest" buffer. -// The maximum amount of samples that can be returned at a time is set by -// the 'set_returnBuffer_size' function. -void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples) -{ - uint count; - uint sizeReq; - - if (numSamples == 0) return; - assert(pAAFilter); - - // If anti-alias filter is turned off, simply transpose without applying - // the filter - if (bUseAAFilter == FALSE) - { - sizeReq = (int)((float)numSamples / fRate + 1.0f); - count = transpose(outputBuffer.ptrEnd(sizeReq), src, numSamples); - outputBuffer.putSamples(count); - return; - } - - // Transpose with anti-alias filter - if (fRate < 1.0f) - { - upsample(src, numSamples); - } - else - { - downsample(src, numSamples); - } -} - - -// Transposes the sample rate of the given samples using linear interpolation. -// Returns the number of samples returned in the "dest" buffer -inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) -{ - if (uChannels == 2) - { - return transposeStereo(dest, src, numSamples); - } - else - { - return transposeMono(dest, src, numSamples); - } -} - - -// Sets the number of channels, 1 = mono, 2 = stereo -void RateTransposer::setChannels(const uint numchannels) -{ - if (uChannels == numchannels) return; - - assert(numchannels == 1 || numchannels == 2); - uChannels = numchannels; - - storeBuffer.setChannels(uChannels); - tempBuffer.setChannels(uChannels); - outputBuffer.setChannels(uChannels); - - // Inits the linear interpolation registers - resetRegisters(); -} - - -// Clears all the samples in the object -void RateTransposer::clear() -{ - outputBuffer.clear(); - storeBuffer.clear(); -} - - -// Returns nonzero if there aren't any samples available for outputting. -uint RateTransposer::isEmpty() -{ - int res; - - res = FIFOProcessor::isEmpty(); - if (res == 0) return 0; - return storeBuffer.isEmpty(); -} - - -////////////////////////////////////////////////////////////////////////////// -// -// RateTransposerInteger - integer arithmetic implementation -// - -/// fixed-point interpolation routine precision -#define SCALE 65536 - -// Constructor -RateTransposerInteger::RateTransposerInteger() : RateTransposer() -{ - // call these here as these are virtual functions; calling these - // from the base class constructor wouldn't execute the overloaded - // versions (peculiar C++ can be). - resetRegisters(); - setRate(1.0f); -} - - -RateTransposerInteger::~RateTransposerInteger() -{ -} - - -void RateTransposerInteger::resetRegisters() -{ - iSlopeCount = 0; - sPrevSampleL = - sPrevSampleR = 0; -} - - - -// Transposes the sample rate of the given samples using linear interpolation. -// 'Mono' version of the routine. Returns the number of samples returned in -// the "dest" buffer -uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) -{ - unsigned int i, used; - LONG_SAMPLETYPE temp, vol1; - - used = 0; - i = 0; - - // Process the last sample saved from the previous call first... - while (iSlopeCount <= SCALE) - { - vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); - temp = vol1 * sPrevSampleL + iSlopeCount * src[0]; - dest[i] = (SAMPLETYPE)(temp / SCALE); - i++; - iSlopeCount += uRate; - } - // now always (iSlopeCount > SCALE) - iSlopeCount -= SCALE; - - while (1) - { - while (iSlopeCount > SCALE) - { - iSlopeCount -= SCALE; - used ++; - if (used >= numSamples - 1) goto end; - } - vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); - temp = src[used] * vol1 + iSlopeCount * src[used + 1]; - dest[i] = (SAMPLETYPE)(temp / SCALE); - - i++; - iSlopeCount += uRate; - } -end: - // Store the last sample for the next round - sPrevSampleL = src[numSamples - 1]; - - return i; -} - - -// Transposes the sample rate of the given samples using linear interpolation. -// 'Stereo' version of the routine. Returns the number of samples returned in -// the "dest" buffer -uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) -{ - unsigned int srcPos, i, used; - LONG_SAMPLETYPE temp, vol1; - - if (numSamples == 0) return 0; // no samples, no work - - used = 0; - i = 0; - - // Process the last sample saved from the sPrevSampleLious call first... - while (iSlopeCount <= SCALE) - { - vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); - temp = vol1 * sPrevSampleL + iSlopeCount * src[0]; - dest[2 * i] = (SAMPLETYPE)(temp / SCALE); - temp = vol1 * sPrevSampleR + iSlopeCount * src[1]; - dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE); - i++; - iSlopeCount += uRate; - } - // now always (iSlopeCount > SCALE) - iSlopeCount -= SCALE; - - while (1) - { - while (iSlopeCount > SCALE) - { - iSlopeCount -= SCALE; - used ++; - if (used >= numSamples - 1) goto end; - } - srcPos = 2 * used; - vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); - temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2]; - dest[2 * i] = (SAMPLETYPE)(temp / SCALE); - temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3]; - dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE); - - i++; - iSlopeCount += uRate; - } -end: - // Store the last sample for the next round - sPrevSampleL = src[2 * numSamples - 2]; - sPrevSampleR = src[2 * numSamples - 1]; - - return i; -} - - -// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower -// uRate, larger faster uRates. -void RateTransposerInteger::setRate(float newRate) -{ - uRate = (int)(newRate * SCALE + 0.5f); - RateTransposer::setRate(newRate); -} - - -////////////////////////////////////////////////////////////////////////////// -// -// RateTransposerFloat - floating point arithmetic implementation -// -////////////////////////////////////////////////////////////////////////////// - -// Constructor -RateTransposerFloat::RateTransposerFloat() : RateTransposer() -{ - // call these here as these are virtual functions; calling these - // from the base class constructor wouldn't execute the overloaded - // versions (peculiar C++ can be). - resetRegisters(); - setRate(1.0f); -} - - -RateTransposerFloat::~RateTransposerFloat() -{ -} - - -void RateTransposerFloat::resetRegisters() -{ - fSlopeCount = 0; - sPrevSampleL = - sPrevSampleR = 0; -} - - - -// Transposes the sample rate of the given samples using linear interpolation. -// 'Mono' version of the routine. Returns the number of samples returned in -// the "dest" buffer -uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) -{ - unsigned int i, used; - - used = 0; - i = 0; - - // Process the last sample saved from the previous call first... - while (fSlopeCount <= 1.0f) - { - dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]); - i++; - fSlopeCount += fRate; - } - fSlopeCount -= 1.0f; - - if (numSamples == 1) goto end; - - while (1) - { - while (fSlopeCount > 1.0f) - { - fSlopeCount -= 1.0f; - used ++; - if (used >= numSamples - 1) goto end; - } - dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]); - i++; - fSlopeCount += fRate; - } -end: - // Store the last sample for the next round - sPrevSampleL = src[numSamples - 1]; - - return i; -} - - -// Transposes the sample rate of the given samples using linear interpolation. -// 'Mono' version of the routine. Returns the number of samples returned in -// the "dest" buffer -uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) -{ - unsigned int srcPos, i, used; - - if (numSamples == 0) return 0; // no samples, no work - - used = 0; - i = 0; - - // Process the last sample saved from the sPrevSampleLious call first... - while (fSlopeCount <= 1.0f) - { - dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]); - dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]); - i++; - fSlopeCount += fRate; - } - // now always (iSlopeCount > 1.0f) - fSlopeCount -= 1.0f; - - if (numSamples == 1) goto end; - - while (1) - { - while (fSlopeCount > 1.0f) - { - fSlopeCount -= 1.0f; - used ++; - if (used >= numSamples - 1) goto end; - } - srcPos = 2 * used; - - dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos] - + fSlopeCount * src[srcPos + 2]); - dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1] - + fSlopeCount * src[srcPos + 3]); - - i++; - fSlopeCount += fRate; - } -end: - // Store the last sample for the next round - sPrevSampleL = src[2 * numSamples - 2]; - sPrevSampleR = src[2 * numSamples - 1]; - - return i; -} +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sample rate transposer. Changes sample rate by using linear interpolation +/// together with anti-alias filtering (first order interpolation with anti- +/// alias filtering should be quite adequate for this application) +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/03/19 10:05:49 $ +// File revision : $Revision: 1.13 $ +// +// $Id: RateTransposer.cpp,v 1.13 2006/03/19 10:05:49 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 +#include +#include +#include +#include +#include "RateTransposer.h" +#include "AAFilter.h" + +using namespace soundtouch; + + +/// A linear samplerate transposer class that uses integer arithmetics. +/// for the transposing. +class RateTransposerInteger : public RateTransposer +{ +protected: + int iSlopeCount; + uint uRate; + SAMPLETYPE sPrevSampleL, sPrevSampleR; + + virtual void resetRegisters(); + + virtual uint transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + virtual uint transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + +public: + RateTransposerInteger(); + virtual ~RateTransposerInteger(); + + /// Sets new target rate. Normal rate = 1.0, smaller values represent slower + /// rate, larger faster rates. + virtual void setRate(float newRate); + +}; + + +/// A linear samplerate transposer class that uses floating point arithmetics +/// for the transposing. +class RateTransposerFloat : public RateTransposer +{ +protected: + float fSlopeCount; + float fRateStep; + SAMPLETYPE sPrevSampleL, sPrevSampleR; + + virtual void resetRegisters(); + + virtual uint transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + virtual uint transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + +public: + RateTransposerFloat(); + virtual ~RateTransposerFloat(); +}; + + + +#ifndef min +#define min(a,b) ((a > b) ? b : a) +#define max(a,b) ((a < b) ? b : a) +#endif + + +// Operator 'new' is overloaded so that it automatically creates a suitable instance +// depending on if we've a MMX/SSE/etc-capable CPU available or not. +void * RateTransposer::operator new(size_t s) +{ + // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! + assert(FALSE); + return NULL; +} + + +RateTransposer *RateTransposer::newInstance() +{ +#ifdef INTEGER_SAMPLES + return ::new RateTransposerInteger; +#else + return ::new RateTransposerFloat; +#endif +} + + +// Constructor +RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer) +{ + uChannels = 2; + bUseAAFilter = TRUE; + + // Instantiates the anti-alias filter with default tap length + // of 32 + pAAFilter = new AAFilter(32); +} + + + +RateTransposer::~RateTransposer() +{ + delete pAAFilter; +} + + + +/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable +void RateTransposer::enableAAFilter(const BOOL newMode) +{ + bUseAAFilter = newMode; +} + + +/// Returns nonzero if anti-alias filter is enabled. +BOOL RateTransposer::isAAFilterEnabled() const +{ + return bUseAAFilter; +} + + +AAFilter *RateTransposer::getAAFilter() const +{ + return pAAFilter; +} + + + +// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower +// uRate, larger faster uRates. +void RateTransposer::setRate(float newRate) +{ + float fCutoff; + + fRate = newRate; + + // design a new anti-alias filter + if (newRate > 1.0f) + { + fCutoff = 0.5f / newRate; + } + else + { + fCutoff = 0.5f * newRate; + } + pAAFilter->setCutoffFreq(fCutoff); +} + + +// Outputs as many samples of the 'outputBuffer' as possible, and if there's +// any room left, outputs also as many of the incoming samples as possible. +// The goal is to drive the outputBuffer empty. +// +// It's allowed for 'output' and 'input' parameters to point to the same +// memory position. +void RateTransposer::flushStoreBuffer() +{ + if (storeBuffer.isEmpty()) return; + + outputBuffer.moveSamples(storeBuffer); +} + + +// Adds 'numSamples' pcs of samples from the 'samples' memory position into +// the input of the object. +void RateTransposer::putSamples(const SAMPLETYPE *samples, uint numSamples) +{ + processSamples(samples, numSamples); +} + + + +// Transposes up the sample rate, causing the observed playback 'rate' of the +// sound to decrease +void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples) +{ + int count, sizeTemp, num; + + // If the parameter 'uRate' value is smaller than 'SCALE', first transpose + // the samples and then apply the anti-alias filter to remove aliasing. + + // First check that there's enough room in 'storeBuffer' + // (+16 is to reserve some slack in the destination buffer) + sizeTemp = (int)((float)numSamples / fRate + 16.0f); + + // Transpose the samples, store the result into the end of "storeBuffer" + count = transpose(storeBuffer.ptrEnd(sizeTemp), src, numSamples); + storeBuffer.putSamples(count); + + // Apply the anti-alias filter to samples in "store output", output the + // result to "dest" + num = storeBuffer.numSamples(); + count = pAAFilter->evaluate(outputBuffer.ptrEnd(num), + storeBuffer.ptrBegin(), num, uChannels); + outputBuffer.putSamples(count); + + // Remove the processed samples from "storeBuffer" + storeBuffer.receiveSamples(count); +} + + +// Transposes down the sample rate, causing the observed playback 'rate' of the +// sound to increase +void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples) +{ + int count, sizeTemp; + + // If the parameter 'uRate' value is larger than 'SCALE', first apply the + // anti-alias filter to remove high frequencies (prevent them from folding + // over the lover frequencies), then transpose. */ + + // Add the new samples to the end of the storeBuffer */ + storeBuffer.putSamples(src, numSamples); + + // Anti-alias filter the samples to prevent folding and output the filtered + // data to tempBuffer. Note : because of the FIR filter length, the + // filtering routine takes in 'filter_length' more samples than it outputs. + assert(tempBuffer.isEmpty()); + sizeTemp = storeBuffer.numSamples(); + + count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp), + storeBuffer.ptrBegin(), sizeTemp, uChannels); + + // Remove the filtered samples from 'storeBuffer' + storeBuffer.receiveSamples(count); + + // Transpose the samples (+16 is to reserve some slack in the destination buffer) + sizeTemp = (int)((float)numSamples / fRate + 16.0f); + count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count); + outputBuffer.putSamples(count); +} + + +// Transposes sample rate by applying anti-alias filter to prevent folding. +// Returns amount of samples returned in the "dest" buffer. +// The maximum amount of samples that can be returned at a time is set by +// the 'set_returnBuffer_size' function. +void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples) +{ + uint count; + uint sizeReq; + + if (numSamples == 0) return; + assert(pAAFilter); + + // If anti-alias filter is turned off, simply transpose without applying + // the filter + if (bUseAAFilter == FALSE) + { + sizeReq = (int)((float)numSamples / fRate + 1.0f); + count = transpose(outputBuffer.ptrEnd(sizeReq), src, numSamples); + outputBuffer.putSamples(count); + return; + } + + // Transpose with anti-alias filter + if (fRate < 1.0f) + { + upsample(src, numSamples); + } + else + { + downsample(src, numSamples); + } +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// Returns the number of samples returned in the "dest" buffer +inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + if (uChannels == 2) + { + return transposeStereo(dest, src, numSamples); + } + else + { + return transposeMono(dest, src, numSamples); + } +} + + +// Sets the number of channels, 1 = mono, 2 = stereo +void RateTransposer::setChannels(const uint numchannels) +{ + if (uChannels == numchannels) return; + + assert(numchannels == 1 || numchannels == 2); + uChannels = numchannels; + + storeBuffer.setChannels(uChannels); + tempBuffer.setChannels(uChannels); + outputBuffer.setChannels(uChannels); + + // Inits the linear interpolation registers + resetRegisters(); +} + + +// Clears all the samples in the object +void RateTransposer::clear() +{ + outputBuffer.clear(); + storeBuffer.clear(); +} + + +// Returns nonzero if there aren't any samples available for outputting. +uint RateTransposer::isEmpty() +{ + int res; + + res = FIFOProcessor::isEmpty(); + if (res == 0) return 0; + return storeBuffer.isEmpty(); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// RateTransposerInteger - integer arithmetic implementation +// + +/// fixed-point interpolation routine precision +#define SCALE 65536 + +// Constructor +RateTransposerInteger::RateTransposerInteger() : RateTransposer() +{ + // call these here as these are virtual functions; calling these + // from the base class constructor wouldn't execute the overloaded + // versions (peculiar C++ can be). + resetRegisters(); + setRate(1.0f); +} + + +RateTransposerInteger::~RateTransposerInteger() +{ +} + + +void RateTransposerInteger::resetRegisters() +{ + iSlopeCount = 0; + sPrevSampleL = + sPrevSampleR = 0; +} + + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + unsigned int i, used; + LONG_SAMPLETYPE temp, vol1; + + used = 0; + i = 0; + + // Process the last sample saved from the previous call first... + while (iSlopeCount <= SCALE) + { + vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); + temp = vol1 * sPrevSampleL + iSlopeCount * src[0]; + dest[i] = (SAMPLETYPE)(temp / SCALE); + i++; + iSlopeCount += uRate; + } + // now always (iSlopeCount > SCALE) + iSlopeCount -= SCALE; + + while (1) + { + while (iSlopeCount > SCALE) + { + iSlopeCount -= SCALE; + used ++; + if (used >= numSamples - 1) goto end; + } + vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); + temp = src[used] * vol1 + iSlopeCount * src[used + 1]; + dest[i] = (SAMPLETYPE)(temp / SCALE); + + i++; + iSlopeCount += uRate; + } +end: + // Store the last sample for the next round + sPrevSampleL = src[numSamples - 1]; + + return i; +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Stereo' version of the routine. Returns the number of samples returned in +// the "dest" buffer +uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + unsigned int srcPos, i, used; + LONG_SAMPLETYPE temp, vol1; + + if (numSamples == 0) return 0; // no samples, no work + + used = 0; + i = 0; + + // Process the last sample saved from the sPrevSampleLious call first... + while (iSlopeCount <= SCALE) + { + vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); + temp = vol1 * sPrevSampleL + iSlopeCount * src[0]; + dest[2 * i] = (SAMPLETYPE)(temp / SCALE); + temp = vol1 * sPrevSampleR + iSlopeCount * src[1]; + dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE); + i++; + iSlopeCount += uRate; + } + // now always (iSlopeCount > SCALE) + iSlopeCount -= SCALE; + + while (1) + { + while (iSlopeCount > SCALE) + { + iSlopeCount -= SCALE; + used ++; + if (used >= numSamples - 1) goto end; + } + srcPos = 2 * used; + vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); + temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2]; + dest[2 * i] = (SAMPLETYPE)(temp / SCALE); + temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3]; + dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE); + + i++; + iSlopeCount += uRate; + } +end: + // Store the last sample for the next round + sPrevSampleL = src[2 * numSamples - 2]; + sPrevSampleR = src[2 * numSamples - 1]; + + return i; +} + + +// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower +// uRate, larger faster uRates. +void RateTransposerInteger::setRate(float newRate) +{ + uRate = (int)(newRate * SCALE + 0.5f); + RateTransposer::setRate(newRate); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// RateTransposerFloat - floating point arithmetic implementation +// +////////////////////////////////////////////////////////////////////////////// + +// Constructor +RateTransposerFloat::RateTransposerFloat() : RateTransposer() +{ + // call these here as these are virtual functions; calling these + // from the base class constructor wouldn't execute the overloaded + // versions (peculiar C++ can be). + resetRegisters(); + setRate(1.0f); +} + + +RateTransposerFloat::~RateTransposerFloat() +{ +} + + +void RateTransposerFloat::resetRegisters() +{ + fSlopeCount = 0; + sPrevSampleL = + sPrevSampleR = 0; +} + + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + unsigned int i, used; + + used = 0; + i = 0; + + // Process the last sample saved from the previous call first... + while (fSlopeCount <= 1.0f) + { + dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]); + i++; + fSlopeCount += fRate; + } + fSlopeCount -= 1.0f; + + if (numSamples == 1) goto end; + + while (1) + { + while (fSlopeCount > 1.0f) + { + fSlopeCount -= 1.0f; + used ++; + if (used >= numSamples - 1) goto end; + } + dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]); + i++; + fSlopeCount += fRate; + } +end: + // Store the last sample for the next round + sPrevSampleL = src[numSamples - 1]; + + return i; +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + unsigned int srcPos, i, used; + + if (numSamples == 0) return 0; // no samples, no work + + used = 0; + i = 0; + + // Process the last sample saved from the sPrevSampleLious call first... + while (fSlopeCount <= 1.0f) + { + dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]); + dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]); + i++; + fSlopeCount += fRate; + } + // now always (iSlopeCount > 1.0f) + fSlopeCount -= 1.0f; + + if (numSamples == 1) goto end; + + while (1) + { + while (fSlopeCount > 1.0f) + { + fSlopeCount -= 1.0f; + used ++; + if (used >= numSamples - 1) goto end; + } + srcPos = 2 * used; + + dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos] + + fSlopeCount * src[srcPos + 2]); + dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1] + + fSlopeCount * src[srcPos + 3]); + + i++; + fSlopeCount += fRate; + } +end: + // Store the last sample for the next round + sPrevSampleL = src[2 * numSamples - 2]; + sPrevSampleR = src[2 * numSamples - 1]; + + return i; +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/SoundTouch.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/SoundTouch.cpp index d20fd326b..381c51716 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/SoundTouch.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/SoundTouch.cpp @@ -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 -#include -#include -#include -#include -#include - -#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 +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/TDStretch.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/TDStretch.cpp index bf5470cfe..c43bfbb7e 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/TDStretch.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/TDStretch.cpp @@ -1,938 +1,938 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo -/// while maintaining the original pitch by using a time domain WSOLA-like -/// method with several performance-increasing tweaks. -/// -/// 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.24 $ -// -// $Id: TDStretch.cpp,v 1.24 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 -#include -#include -#include -#include -#include - -#include "STTypes.h" -#include "cpu_detect.h" -#include "TDStretch.h" - -using namespace soundtouch; - -#ifndef min -#define min(a,b) ((a > b) ? b : a) -#define max(a,b) ((a < b) ? b : a) -#endif - - - -/***************************************************************************** - * - * Constant definitions - * - *****************************************************************************/ - - -// Table for the hierarchical mixing position seeking algorithm -int scanOffsets[4][24]={ - { 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806, - 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0}, - {-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - { -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - { -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; - -/***************************************************************************** - * - * Implementation of the class 'TDStretch' - * - *****************************************************************************/ - - -TDStretch::TDStretch() : FIFOProcessor(&outputBuffer) -{ - bQuickseek = FALSE; - channels = 2; - bMidBufferDirty = FALSE; - - pMidBuffer = NULL; - pRefMidBufferUnaligned = NULL; - overlapLength = 0; - - setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS); - - setTempo(1.0f); -} - - - - -TDStretch::~TDStretch() -{ - delete[] pMidBuffer; - delete[] pRefMidBufferUnaligned; -} - - - -// Calculates the x having the closest 2^x value for the given value -static int _getClosest2Power(double value) -{ - return (int)(log(value) / log(2.0) + 0.5); -} - - - -// Sets routine control parameters. These control are certain time constants -// defining how the sound is stretched to the desired duration. -// -// 'sampleRate' = sample rate of the sound -// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms) -// 'seekwindowMS' = seeking window length for scanning the best overlapping -// position (default = 28 ms) -// 'overlapMS' = overlapping length (default = 12 ms) - -void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS, - uint aSeekWindowMS, uint aOverlapMS) -{ - this->sampleRate = aSampleRate; - this->sequenceMs = aSequenceMS; - this->seekWindowMs = aSeekWindowMS; - this->overlapMs = aOverlapMS; - - seekLength = (sampleRate * seekWindowMs) / 1000; - seekWindowLength = (sampleRate * sequenceMs) / 1000; - - maxOffset = seekLength; - - calculateOverlapLength(overlapMs); - - // set tempo to recalculate 'sampleReq' - setTempo(tempo); - -} - - - -/// Get routine control parameters, see setParameters() function. -/// Any of the parameters to this function can be NULL, in such case corresponding parameter -/// value isn't returned. -void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs) -{ - if (pSampleRate) - { - *pSampleRate = sampleRate; - } - - if (pSequenceMs) - { - *pSequenceMs = sequenceMs; - } - - if (pSeekWindowMs) - { - *pSeekWindowMs = seekWindowMs; - } - - if (pOverlapMs) - { - *pOverlapMs = overlapMs; - } -} - - -// Overlaps samples in 'midBuffer' with the samples in 'input' -void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const -{ - int i, itemp; - - for (i = 0; i < (int)overlapLength ; i ++) - { - itemp = overlapLength - i; - output[i] = (input[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits; - } -} - - - -void TDStretch::clearMidBuffer() -{ - if (bMidBufferDirty) - { - memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength); - bMidBufferDirty = FALSE; - } -} - - -void TDStretch::clearInput() -{ - inputBuffer.clear(); - clearMidBuffer(); -} - - -// Clears the sample buffers -void TDStretch::clear() -{ - outputBuffer.clear(); - inputBuffer.clear(); - clearMidBuffer(); -} - - - -// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero -// to enable -void TDStretch::enableQuickSeek(BOOL enable) -{ - bQuickseek = enable; -} - - -// Returns nonzero if the quick seeking algorithm is enabled. -BOOL TDStretch::isQuickSeekEnabled() const -{ - return bQuickseek; -} - - -// Seeks for the optimal overlap-mixing position. -uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos) -{ - if (channels == 2) - { - // stereo sound - if (bQuickseek) - { - return seekBestOverlapPositionStereoQuick(refPos); - } - else - { - return seekBestOverlapPositionStereo(refPos); - } - } - else - { - // mono sound - if (bQuickseek) - { - return seekBestOverlapPositionMonoQuick(refPos); - } - else - { - return seekBestOverlapPositionMono(refPos); - } - } -} - - - - -// Overlaps samples in 'midBuffer' with the samples in 'inputBuffer' at position -// of 'ovlPos'. -inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const -{ - if (channels == 2) - { - // stereo sound - overlapStereo(output, input + 2 * ovlPos); - } else { - // mono sound. - overlapMono(output, input + ovlPos); - } -} - - - - -// Seeks for the optimal overlap-mixing position. The 'stereo' version of the -// routine -// -// The best position is determined as the position where the two overlapped -// sample sequences are 'most alike', in terms of the highest cross-correlation -// value over the overlapping period -uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos) -{ - uint bestOffs; - LONG_SAMPLETYPE bestCorr, corr; - uint i; - - // Slopes the amplitudes of the 'midBuffer' samples - precalcCorrReferenceStereo(); - - bestCorr = INT_MIN; - bestOffs = 0; - - // Scans for the best correlation value by testing each possible position - // over the permitted range. - for (i = 0; i < seekLength; i ++) - { - // Calculates correlation value for the mixing position corresponding - // to 'i' - corr = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer); - - // Checks for the highest correlation value - if (corr > bestCorr) - { - bestCorr = corr; - bestOffs = i; - } - } - // clear cross correlation routine state if necessary (is so e.g. in MMX routines). - clearCrossCorrState(); - - return bestOffs; -} - - -// Seeks for the optimal overlap-mixing position. The 'stereo' version of the -// routine -// -// The best position is determined as the position where the two overlapped -// sample sequences are 'most alike', in terms of the highest cross-correlation -// value over the overlapping period -uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos) -{ - uint j; - uint bestOffs; - LONG_SAMPLETYPE bestCorr, corr; - uint scanCount, corrOffset, tempOffset; - - // Slopes the amplitude of the 'midBuffer' samples - precalcCorrReferenceStereo(); - - bestCorr = INT_MIN; - bestOffs = 0; - corrOffset = 0; - tempOffset = 0; - - // Scans for the best correlation value using four-pass hierarchical search. - // - // The look-up table 'scans' has hierarchical position adjusting steps. - // In first pass the routine searhes for the highest correlation with - // relatively coarse steps, then rescans the neighbourhood of the highest - // correlation with better resolution and so on. - for (scanCount = 0;scanCount < 4; scanCount ++) - { - j = 0; - while (scanOffsets[scanCount][j]) - { - tempOffset = corrOffset + scanOffsets[scanCount][j]; - if (tempOffset >= seekLength) break; - - // Calculates correlation value for the mixing position corresponding - // to 'tempOffset' - corr = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer); - - // Checks for the highest correlation value - if (corr > bestCorr) - { - bestCorr = corr; - bestOffs = tempOffset; - } - j ++; - } - corrOffset = bestOffs; - } - // clear cross correlation routine state if necessary (is so e.g. in MMX routines). - clearCrossCorrState(); - - return bestOffs; -} - - - -// Seeks for the optimal overlap-mixing position. The 'mono' version of the -// routine -// -// The best position is determined as the position where the two overlapped -// sample sequences are 'most alike', in terms of the highest cross-correlation -// value over the overlapping period -uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos) -{ - uint bestOffs; - LONG_SAMPLETYPE bestCorr, corr; - uint tempOffset; - const SAMPLETYPE *compare; - - // Slopes the amplitude of the 'midBuffer' samples - precalcCorrReferenceMono(); - - bestCorr = INT_MIN; - bestOffs = 0; - - // Scans for the best correlation value by testing each possible position - // over the permitted range. - for (tempOffset = 0; tempOffset < seekLength; tempOffset ++) - { - compare = refPos + tempOffset; - - // Calculates correlation value for the mixing position corresponding - // to 'tempOffset' - corr = calcCrossCorrMono(pRefMidBuffer, compare); - - // Checks for the highest correlation value - if (corr > bestCorr) - { - bestCorr = corr; - bestOffs = tempOffset; - } - } - // clear cross correlation routine state if necessary (is so e.g. in MMX routines). - clearCrossCorrState(); - - return bestOffs; -} - - -// Seeks for the optimal overlap-mixing position. The 'mono' version of the -// routine -// -// The best position is determined as the position where the two overlapped -// sample sequences are 'most alike', in terms of the highest cross-correlation -// value over the overlapping period -uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos) -{ - uint j; - uint bestOffs; - LONG_SAMPLETYPE bestCorr, corr; - uint scanCount, corrOffset, tempOffset; - - // Slopes the amplitude of the 'midBuffer' samples - precalcCorrReferenceMono(); - - bestCorr = INT_MIN; - bestOffs = 0; - corrOffset = 0; - tempOffset = 0; - - // Scans for the best correlation value using four-pass hierarchical search. - // - // The look-up table 'scans' has hierarchical position adjusting steps. - // In first pass the routine searhes for the highest correlation with - // relatively coarse steps, then rescans the neighbourhood of the highest - // correlation with better resolution and so on. - for (scanCount = 0;scanCount < 4; scanCount ++) - { - j = 0; - while (scanOffsets[scanCount][j]) - { - tempOffset = corrOffset + scanOffsets[scanCount][j]; - if (tempOffset >= seekLength) break; - - // Calculates correlation value for the mixing position corresponding - // to 'tempOffset' - corr = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer); - - // Checks for the highest correlation value - if (corr > bestCorr) - { - bestCorr = corr; - bestOffs = tempOffset; - } - j ++; - } - corrOffset = bestOffs; - } - // clear cross correlation routine state if necessary (is so e.g. in MMX routines). - clearCrossCorrState(); - - return bestOffs; -} - - -/// clear cross correlation routine state if necessary -void TDStretch::clearCrossCorrState() -{ - // default implementation is empty. -} - - -// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower -// tempo, larger faster tempo. -void TDStretch::setTempo(float newTempo) -{ - uint intskip; - - tempo = newTempo; - - // Calculate ideal skip length (according to tempo value) - nominalSkip = tempo * (seekWindowLength - overlapLength); - skipFract = 0; - intskip = (int)(nominalSkip + 0.5f); - - // Calculate how many samples are needed in the 'inputBuffer' to - // process another batch of samples - sampleReq = max(intskip + overlapLength, seekWindowLength) + maxOffset; -} - - - -// Sets the number of channels, 1 = mono, 2 = stereo -void TDStretch::setChannels(uint numChannels) -{ - if (channels == numChannels) return; - assert(numChannels == 1 || numChannels == 2); - - channels = numChannels; - inputBuffer.setChannels(channels); - outputBuffer.setChannels(channels); -} - - -// nominal tempo, no need for processing, just pass the samples through -// to outputBuffer -void TDStretch::processNominalTempo() -{ - assert(tempo == 1.0f); - - if (bMidBufferDirty) - { - // If there are samples in pMidBuffer waiting for overlapping, - // do a single sliding overlapping with them in order to prevent a - // clicking distortion in the output sound - if (inputBuffer.numSamples() < overlapLength) - { - // wait until we've got overlapLength input samples - return; - } - // Mix the samples in the beginning of 'inputBuffer' with the - // samples in 'midBuffer' using sliding overlapping - overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0); - outputBuffer.putSamples(overlapLength); - inputBuffer.receiveSamples(overlapLength); - clearMidBuffer(); - // now we've caught the nominal sample flow and may switch to - // bypass mode - } - - // Simply bypass samples from input to output - outputBuffer.moveSamples(inputBuffer); -} - - -// Processes as many processing frames of the samples 'inputBuffer', store -// the result into 'outputBuffer' -void TDStretch::processSamples() -{ - uint ovlSkip, offset; - int temp; - - /* Removed this small optimization - can introduce a click to sound when tempo setting - crosses the nominal value - if (tempo == 1.0f) - { - // tempo not changed from the original, so bypass the processing - processNominalTempo(); - return; - } - */ - - if (bMidBufferDirty == FALSE) - { - // if midBuffer is empty, move the first samples of the input stream - // into it - if (inputBuffer.numSamples() < overlapLength) - { - // wait until we've got overlapLength samples - return; - } - memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE)); - inputBuffer.receiveSamples(overlapLength); - bMidBufferDirty = TRUE; - } - - // Process samples as long as there are enough samples in 'inputBuffer' - // to form a processing frame. - while (inputBuffer.numSamples() >= sampleReq) - { - // If tempo differs from the normal ('SCALE'), scan for the best overlapping - // position - offset = seekBestOverlapPosition(inputBuffer.ptrBegin()); - - // Mix the samples in the 'inputBuffer' at position of 'offset' with the - // samples in 'midBuffer' using sliding overlapping - // ... first partially overlap with the end of the previous sequence - // (that's in 'midBuffer') - overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), offset); - outputBuffer.putSamples(overlapLength); - - // ... then copy sequence samples from 'inputBuffer' to output - temp = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe; - if (temp > 0) - { - outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), temp); - } - - // Copies the end of the current sequence from 'inputBuffer' to - // 'midBuffer' for being mixed with the beginning of the next - // processing sequence and so on - assert(offset + seekWindowLength <= inputBuffer.numSamples()); - memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength), - channels * sizeof(SAMPLETYPE) * overlapLength); - bMidBufferDirty = TRUE; - - // Remove the processed samples from the input buffer. Update - // the difference between integer & nominal skip step to 'skipFract' - // in order to prevent the error from accumulating over time. - skipFract += nominalSkip; // real skip size - ovlSkip = (int)skipFract; // rounded to integer skip - skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip - inputBuffer.receiveSamples(ovlSkip); - } -} - - -// Adds 'numsamples' pcs of samples from the 'samples' memory position into -// the input of the object. -void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples) -{ - // Add the samples into the input buffer - inputBuffer.putSamples(samples, numSamples); - // Process the samples in input buffer - processSamples(); -} - - - -/// Set new overlap length parameter & reallocate RefMidBuffer if necessary. -void TDStretch::acceptNewOverlapLength(uint newOverlapLength) -{ - uint prevOvl; - - prevOvl = overlapLength; - overlapLength = newOverlapLength; - - if (overlapLength > prevOvl) - { - delete[] pMidBuffer; - delete[] pRefMidBufferUnaligned; - - pMidBuffer = new SAMPLETYPE[overlapLength * 2]; - bMidBufferDirty = TRUE; - clearMidBuffer(); - - pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)]; - // ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency - pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & -16); - } -} - - -// Operator 'new' is overloaded so that it automatically creates a suitable instance -// depending on if we've a MMX/SSE/etc-capable CPU available or not. -void * TDStretch::operator new(size_t s) -{ - // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! - assert(FALSE); - return NULL; -} - - -TDStretch * TDStretch::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 TDStretchMMX; - } - else -#endif // ALLOW_MMX - - -#ifdef ALLOW_SSE - if (uExtensions & SUPPORT_SSE) - { - // SSE support - return ::new TDStretchSSE; - } - else -#endif // ALLOW_SSE - - -#ifdef ALLOW_3DNOW - if (uExtensions & SUPPORT_3DNOW) - { - // 3DNow! support - return ::new TDStretch3DNow; - } - else -#endif // ALLOW_3DNOW - - { - // ISA optimizations not supported, use plain C version - return ::new TDStretch; - } -} - - -////////////////////////////////////////////////////////////////////////////// -// -// Integer arithmetics specific algorithm implementations. -// -////////////////////////////////////////////////////////////////////////////// - -#ifdef INTEGER_SAMPLES - -// Slopes the amplitude of the 'midBuffer' samples so that cross correlation -// is faster to calculate -void TDStretch::precalcCorrReferenceStereo() -{ - int i, cnt2; - int temp, temp2; - - for (i=0 ; i < (int)overlapLength ;i ++) - { - temp = i * (overlapLength - i); - cnt2 = i * 2; - - temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider; - pRefMidBuffer[cnt2] = (short)(temp2); - temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider; - pRefMidBuffer[cnt2 + 1] = (short)(temp2); - } -} - - -// Slopes the amplitude of the 'midBuffer' samples so that cross correlation -// is faster to calculate -void TDStretch::precalcCorrReferenceMono() -{ - int i; - long temp; - long temp2; - - for (i=0 ; i < (int)overlapLength ;i ++) - { - temp = i * (overlapLength - i); - temp2 = (pMidBuffer[i] * temp) / slopingDivider; - pRefMidBuffer[i] = (short)temp2; - } -} - - -// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo' -// version of the routine. -void TDStretch::overlapStereo(short *output, const short *input) const -{ - int i; - short temp; - uint cnt2; - - for (i = 0; i < (int)overlapLength ; i ++) - { - temp = (short)(overlapLength - i); - cnt2 = 2 * i; - output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength; - output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength; - } -} - - -/// Calculates overlap period length in samples. -/// Integer version rounds overlap length to closest power of 2 -/// for a divide scaling operation. -void TDStretch::calculateOverlapLength(uint overlapMs) -{ - uint newOvl; - - overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0); - if (overlapDividerBits > 9) overlapDividerBits = 9; - if (overlapDividerBits < 4) overlapDividerBits = 4; - newOvl = (uint)pow(2, overlapDividerBits); - - acceptNewOverlapLength(newOvl); - - // calculate sloping divider so that crosscorrelation operation won't - // overflow 32-bit register. Max. sum of the crosscorrelation sum without - // divider would be 2^30*(N^3-N)/3, where N = overlap length - slopingDivider = (newOvl * newOvl - 1) / 3; -} - - -long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const -{ - long corr; - uint i; - - corr = 0; - for (i = 1; i < overlapLength; i ++) - { - corr += (mixingPos[i] * compare[i]) >> overlapDividerBits; - } - - return corr; -} - - -long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const -{ - long corr; - uint i; - - corr = 0; - for (i = 2; i < 2 * overlapLength; i += 2) - { - corr += (mixingPos[i] * compare[i] + - mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; - } - - return corr; -} - -#endif // INTEGER_SAMPLES - -////////////////////////////////////////////////////////////////////////////// -// -// Floating point arithmetics specific algorithm implementations. -// - -#ifdef FLOAT_SAMPLES - - -// Slopes the amplitude of the 'midBuffer' samples so that cross correlation -// is faster to calculate -void TDStretch::precalcCorrReferenceStereo() -{ - int i, cnt2; - float temp; - - for (i=0 ; i < (int)overlapLength ;i ++) - { - temp = (float)i * (float)(overlapLength - i); - cnt2 = i * 2; - pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp); - pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp); - } -} - - -// Slopes the amplitude of the 'midBuffer' samples so that cross correlation -// is faster to calculate -void TDStretch::precalcCorrReferenceMono() -{ - int i; - float temp; - - for (i=0 ; i < (int)overlapLength ;i ++) - { - temp = (float)i * (float)(overlapLength - i); - pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp); - } -} - - -// SSE-optimized version of the function overlapStereo -void TDStretch::overlapStereo(float *output, const float *input) const -{ - int i; - uint cnt2; - float fTemp; - float fScale; - float fi; - - fScale = 1.0f / (float)overlapLength; - - for (i = 0; i < (int)overlapLength ; i ++) - { - fTemp = (float)(overlapLength - i) * fScale; - fi = (float)i * fScale; - cnt2 = 2 * i; - output[cnt2 + 0] = input[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp; - output[cnt2 + 1] = input[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp; - } -} - - -/// Calculates overlap period length in samples. -void TDStretch::calculateOverlapLength(uint overlapMs) -{ - uint newOvl; - - newOvl = (sampleRate * overlapMs) / 1000; - if (newOvl < 16) newOvl = 16; - - // must be divisible by 8 - newOvl -= newOvl % 8; - - acceptNewOverlapLength(newOvl); -} - - - -double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const -{ - double corr; - uint i; - - corr = 0; - for (i = 1; i < overlapLength; i ++) - { - corr += mixingPos[i] * compare[i]; - } - - return corr; -} - - -double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const -{ - double corr; - uint i; - - corr = 0; - for (i = 2; i < 2 * overlapLength; i += 2) - { - corr += mixingPos[i] * compare[i] + - mixingPos[i + 1] * compare[i + 1]; - } - - return corr; -} - -#endif // FLOAT_SAMPLES +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo +/// while maintaining the original pitch by using a time domain WSOLA-like +/// method with several performance-increasing tweaks. +/// +/// 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.24 $ +// +// $Id: TDStretch.cpp,v 1.24 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 +#include +#include +#include +#include +#include + +#include "STTypes.h" +#include "cpu_detect.h" +#include "TDStretch.h" + +using namespace soundtouch; + +#ifndef min +#define min(a,b) ((a > b) ? b : a) +#define max(a,b) ((a < b) ? b : a) +#endif + + + +/***************************************************************************** + * + * Constant definitions + * + *****************************************************************************/ + + +// Table for the hierarchical mixing position seeking algorithm +int scanOffsets[4][24]={ + { 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806, + 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0}, + {-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + +/***************************************************************************** + * + * Implementation of the class 'TDStretch' + * + *****************************************************************************/ + + +TDStretch::TDStretch() : FIFOProcessor(&outputBuffer) +{ + bQuickseek = FALSE; + channels = 2; + bMidBufferDirty = FALSE; + + pMidBuffer = NULL; + pRefMidBufferUnaligned = NULL; + overlapLength = 0; + + setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS); + + setTempo(1.0f); +} + + + + +TDStretch::~TDStretch() +{ + delete[] pMidBuffer; + delete[] pRefMidBufferUnaligned; +} + + + +// Calculates the x having the closest 2^x value for the given value +static int _getClosest2Power(double value) +{ + return (int)(log(value) / log(2.0) + 0.5); +} + + + +// Sets routine control parameters. These control are certain time constants +// defining how the sound is stretched to the desired duration. +// +// 'sampleRate' = sample rate of the sound +// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms) +// 'seekwindowMS' = seeking window length for scanning the best overlapping +// position (default = 28 ms) +// 'overlapMS' = overlapping length (default = 12 ms) + +void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS, + uint aSeekWindowMS, uint aOverlapMS) +{ + this->sampleRate = aSampleRate; + this->sequenceMs = aSequenceMS; + this->seekWindowMs = aSeekWindowMS; + this->overlapMs = aOverlapMS; + + seekLength = (sampleRate * seekWindowMs) / 1000; + seekWindowLength = (sampleRate * sequenceMs) / 1000; + + maxOffset = seekLength; + + calculateOverlapLength(overlapMs); + + // set tempo to recalculate 'sampleReq' + setTempo(tempo); + +} + + + +/// Get routine control parameters, see setParameters() function. +/// Any of the parameters to this function can be NULL, in such case corresponding parameter +/// value isn't returned. +void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs) +{ + if (pSampleRate) + { + *pSampleRate = sampleRate; + } + + if (pSequenceMs) + { + *pSequenceMs = sequenceMs; + } + + if (pSeekWindowMs) + { + *pSeekWindowMs = seekWindowMs; + } + + if (pOverlapMs) + { + *pOverlapMs = overlapMs; + } +} + + +// Overlaps samples in 'midBuffer' with the samples in 'input' +void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const +{ + int i, itemp; + + for (i = 0; i < (int)overlapLength ; i ++) + { + itemp = overlapLength - i; + output[i] = (input[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits; + } +} + + + +void TDStretch::clearMidBuffer() +{ + if (bMidBufferDirty) + { + memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength); + bMidBufferDirty = FALSE; + } +} + + +void TDStretch::clearInput() +{ + inputBuffer.clear(); + clearMidBuffer(); +} + + +// Clears the sample buffers +void TDStretch::clear() +{ + outputBuffer.clear(); + inputBuffer.clear(); + clearMidBuffer(); +} + + + +// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero +// to enable +void TDStretch::enableQuickSeek(BOOL enable) +{ + bQuickseek = enable; +} + + +// Returns nonzero if the quick seeking algorithm is enabled. +BOOL TDStretch::isQuickSeekEnabled() const +{ + return bQuickseek; +} + + +// Seeks for the optimal overlap-mixing position. +uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos) +{ + if (channels == 2) + { + // stereo sound + if (bQuickseek) + { + return seekBestOverlapPositionStereoQuick(refPos); + } + else + { + return seekBestOverlapPositionStereo(refPos); + } + } + else + { + // mono sound + if (bQuickseek) + { + return seekBestOverlapPositionMonoQuick(refPos); + } + else + { + return seekBestOverlapPositionMono(refPos); + } + } +} + + + + +// Overlaps samples in 'midBuffer' with the samples in 'inputBuffer' at position +// of 'ovlPos'. +inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const +{ + if (channels == 2) + { + // stereo sound + overlapStereo(output, input + 2 * ovlPos); + } else { + // mono sound. + overlapMono(output, input + ovlPos); + } +} + + + + +// Seeks for the optimal overlap-mixing position. The 'stereo' version of the +// routine +// +// The best position is determined as the position where the two overlapped +// sample sequences are 'most alike', in terms of the highest cross-correlation +// value over the overlapping period +uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos) +{ + uint bestOffs; + LONG_SAMPLETYPE bestCorr, corr; + uint i; + + // Slopes the amplitudes of the 'midBuffer' samples + precalcCorrReferenceStereo(); + + bestCorr = INT_MIN; + bestOffs = 0; + + // Scans for the best correlation value by testing each possible position + // over the permitted range. + for (i = 0; i < seekLength; i ++) + { + // Calculates correlation value for the mixing position corresponding + // to 'i' + corr = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer); + + // Checks for the highest correlation value + if (corr > bestCorr) + { + bestCorr = corr; + bestOffs = i; + } + } + // clear cross correlation routine state if necessary (is so e.g. in MMX routines). + clearCrossCorrState(); + + return bestOffs; +} + + +// Seeks for the optimal overlap-mixing position. The 'stereo' version of the +// routine +// +// The best position is determined as the position where the two overlapped +// sample sequences are 'most alike', in terms of the highest cross-correlation +// value over the overlapping period +uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos) +{ + uint j; + uint bestOffs; + LONG_SAMPLETYPE bestCorr, corr; + uint scanCount, corrOffset, tempOffset; + + // Slopes the amplitude of the 'midBuffer' samples + precalcCorrReferenceStereo(); + + bestCorr = INT_MIN; + bestOffs = 0; + corrOffset = 0; + tempOffset = 0; + + // Scans for the best correlation value using four-pass hierarchical search. + // + // The look-up table 'scans' has hierarchical position adjusting steps. + // In first pass the routine searhes for the highest correlation with + // relatively coarse steps, then rescans the neighbourhood of the highest + // correlation with better resolution and so on. + for (scanCount = 0;scanCount < 4; scanCount ++) + { + j = 0; + while (scanOffsets[scanCount][j]) + { + tempOffset = corrOffset + scanOffsets[scanCount][j]; + if (tempOffset >= seekLength) break; + + // Calculates correlation value for the mixing position corresponding + // to 'tempOffset' + corr = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer); + + // Checks for the highest correlation value + if (corr > bestCorr) + { + bestCorr = corr; + bestOffs = tempOffset; + } + j ++; + } + corrOffset = bestOffs; + } + // clear cross correlation routine state if necessary (is so e.g. in MMX routines). + clearCrossCorrState(); + + return bestOffs; +} + + + +// Seeks for the optimal overlap-mixing position. The 'mono' version of the +// routine +// +// The best position is determined as the position where the two overlapped +// sample sequences are 'most alike', in terms of the highest cross-correlation +// value over the overlapping period +uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos) +{ + uint bestOffs; + LONG_SAMPLETYPE bestCorr, corr; + uint tempOffset; + const SAMPLETYPE *compare; + + // Slopes the amplitude of the 'midBuffer' samples + precalcCorrReferenceMono(); + + bestCorr = INT_MIN; + bestOffs = 0; + + // Scans for the best correlation value by testing each possible position + // over the permitted range. + for (tempOffset = 0; tempOffset < seekLength; tempOffset ++) + { + compare = refPos + tempOffset; + + // Calculates correlation value for the mixing position corresponding + // to 'tempOffset' + corr = calcCrossCorrMono(pRefMidBuffer, compare); + + // Checks for the highest correlation value + if (corr > bestCorr) + { + bestCorr = corr; + bestOffs = tempOffset; + } + } + // clear cross correlation routine state if necessary (is so e.g. in MMX routines). + clearCrossCorrState(); + + return bestOffs; +} + + +// Seeks for the optimal overlap-mixing position. The 'mono' version of the +// routine +// +// The best position is determined as the position where the two overlapped +// sample sequences are 'most alike', in terms of the highest cross-correlation +// value over the overlapping period +uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos) +{ + uint j; + uint bestOffs; + LONG_SAMPLETYPE bestCorr, corr; + uint scanCount, corrOffset, tempOffset; + + // Slopes the amplitude of the 'midBuffer' samples + precalcCorrReferenceMono(); + + bestCorr = INT_MIN; + bestOffs = 0; + corrOffset = 0; + tempOffset = 0; + + // Scans for the best correlation value using four-pass hierarchical search. + // + // The look-up table 'scans' has hierarchical position adjusting steps. + // In first pass the routine searhes for the highest correlation with + // relatively coarse steps, then rescans the neighbourhood of the highest + // correlation with better resolution and so on. + for (scanCount = 0;scanCount < 4; scanCount ++) + { + j = 0; + while (scanOffsets[scanCount][j]) + { + tempOffset = corrOffset + scanOffsets[scanCount][j]; + if (tempOffset >= seekLength) break; + + // Calculates correlation value for the mixing position corresponding + // to 'tempOffset' + corr = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer); + + // Checks for the highest correlation value + if (corr > bestCorr) + { + bestCorr = corr; + bestOffs = tempOffset; + } + j ++; + } + corrOffset = bestOffs; + } + // clear cross correlation routine state if necessary (is so e.g. in MMX routines). + clearCrossCorrState(); + + return bestOffs; +} + + +/// clear cross correlation routine state if necessary +void TDStretch::clearCrossCorrState() +{ + // default implementation is empty. +} + + +// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower +// tempo, larger faster tempo. +void TDStretch::setTempo(float newTempo) +{ + uint intskip; + + tempo = newTempo; + + // Calculate ideal skip length (according to tempo value) + nominalSkip = tempo * (seekWindowLength - overlapLength); + skipFract = 0; + intskip = (int)(nominalSkip + 0.5f); + + // Calculate how many samples are needed in the 'inputBuffer' to + // process another batch of samples + sampleReq = max(intskip + overlapLength, seekWindowLength) + maxOffset; +} + + + +// Sets the number of channels, 1 = mono, 2 = stereo +void TDStretch::setChannels(uint numChannels) +{ + if (channels == numChannels) return; + assert(numChannels == 1 || numChannels == 2); + + channels = numChannels; + inputBuffer.setChannels(channels); + outputBuffer.setChannels(channels); +} + + +// nominal tempo, no need for processing, just pass the samples through +// to outputBuffer +void TDStretch::processNominalTempo() +{ + assert(tempo == 1.0f); + + if (bMidBufferDirty) + { + // If there are samples in pMidBuffer waiting for overlapping, + // do a single sliding overlapping with them in order to prevent a + // clicking distortion in the output sound + if (inputBuffer.numSamples() < overlapLength) + { + // wait until we've got overlapLength input samples + return; + } + // Mix the samples in the beginning of 'inputBuffer' with the + // samples in 'midBuffer' using sliding overlapping + overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0); + outputBuffer.putSamples(overlapLength); + inputBuffer.receiveSamples(overlapLength); + clearMidBuffer(); + // now we've caught the nominal sample flow and may switch to + // bypass mode + } + + // Simply bypass samples from input to output + outputBuffer.moveSamples(inputBuffer); +} + + +// Processes as many processing frames of the samples 'inputBuffer', store +// the result into 'outputBuffer' +void TDStretch::processSamples() +{ + uint ovlSkip, offset; + int temp; + + /* Removed this small optimization - can introduce a click to sound when tempo setting + crosses the nominal value + if (tempo == 1.0f) + { + // tempo not changed from the original, so bypass the processing + processNominalTempo(); + return; + } + */ + + if (bMidBufferDirty == FALSE) + { + // if midBuffer is empty, move the first samples of the input stream + // into it + if (inputBuffer.numSamples() < overlapLength) + { + // wait until we've got overlapLength samples + return; + } + memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE)); + inputBuffer.receiveSamples(overlapLength); + bMidBufferDirty = TRUE; + } + + // Process samples as long as there are enough samples in 'inputBuffer' + // to form a processing frame. + while (inputBuffer.numSamples() >= sampleReq) + { + // If tempo differs from the normal ('SCALE'), scan for the best overlapping + // position + offset = seekBestOverlapPosition(inputBuffer.ptrBegin()); + + // Mix the samples in the 'inputBuffer' at position of 'offset' with the + // samples in 'midBuffer' using sliding overlapping + // ... first partially overlap with the end of the previous sequence + // (that's in 'midBuffer') + overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), offset); + outputBuffer.putSamples(overlapLength); + + // ... then copy sequence samples from 'inputBuffer' to output + temp = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe; + if (temp > 0) + { + outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), temp); + } + + // Copies the end of the current sequence from 'inputBuffer' to + // 'midBuffer' for being mixed with the beginning of the next + // processing sequence and so on + assert(offset + seekWindowLength <= inputBuffer.numSamples()); + memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength), + channels * sizeof(SAMPLETYPE) * overlapLength); + bMidBufferDirty = TRUE; + + // Remove the processed samples from the input buffer. Update + // the difference between integer & nominal skip step to 'skipFract' + // in order to prevent the error from accumulating over time. + skipFract += nominalSkip; // real skip size + ovlSkip = (int)skipFract; // rounded to integer skip + skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip + inputBuffer.receiveSamples(ovlSkip); + } +} + + +// Adds 'numsamples' pcs of samples from the 'samples' memory position into +// the input of the object. +void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples) +{ + // Add the samples into the input buffer + inputBuffer.putSamples(samples, numSamples); + // Process the samples in input buffer + processSamples(); +} + + + +/// Set new overlap length parameter & reallocate RefMidBuffer if necessary. +void TDStretch::acceptNewOverlapLength(uint newOverlapLength) +{ + uint prevOvl; + + prevOvl = overlapLength; + overlapLength = newOverlapLength; + + if (overlapLength > prevOvl) + { + delete[] pMidBuffer; + delete[] pRefMidBufferUnaligned; + + pMidBuffer = new SAMPLETYPE[overlapLength * 2]; + bMidBufferDirty = TRUE; + clearMidBuffer(); + + pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)]; + // ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency + pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & -16); + } +} + + +// Operator 'new' is overloaded so that it automatically creates a suitable instance +// depending on if we've a MMX/SSE/etc-capable CPU available or not. +void * TDStretch::operator new(size_t s) +{ + // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! + assert(FALSE); + return NULL; +} + + +TDStretch * TDStretch::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 TDStretchMMX; + } + else +#endif // ALLOW_MMX + + +#ifdef ALLOW_SSE + if (uExtensions & SUPPORT_SSE) + { + // SSE support + return ::new TDStretchSSE; + } + else +#endif // ALLOW_SSE + + +#ifdef ALLOW_3DNOW + if (uExtensions & SUPPORT_3DNOW) + { + // 3DNow! support + return ::new TDStretch3DNow; + } + else +#endif // ALLOW_3DNOW + + { + // ISA optimizations not supported, use plain C version + return ::new TDStretch; + } +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Integer arithmetics specific algorithm implementations. +// +////////////////////////////////////////////////////////////////////////////// + +#ifdef INTEGER_SAMPLES + +// Slopes the amplitude of the 'midBuffer' samples so that cross correlation +// is faster to calculate +void TDStretch::precalcCorrReferenceStereo() +{ + int i, cnt2; + int temp, temp2; + + for (i=0 ; i < (int)overlapLength ;i ++) + { + temp = i * (overlapLength - i); + cnt2 = i * 2; + + temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider; + pRefMidBuffer[cnt2] = (short)(temp2); + temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider; + pRefMidBuffer[cnt2 + 1] = (short)(temp2); + } +} + + +// Slopes the amplitude of the 'midBuffer' samples so that cross correlation +// is faster to calculate +void TDStretch::precalcCorrReferenceMono() +{ + int i; + long temp; + long temp2; + + for (i=0 ; i < (int)overlapLength ;i ++) + { + temp = i * (overlapLength - i); + temp2 = (pMidBuffer[i] * temp) / slopingDivider; + pRefMidBuffer[i] = (short)temp2; + } +} + + +// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo' +// version of the routine. +void TDStretch::overlapStereo(short *output, const short *input) const +{ + int i; + short temp; + uint cnt2; + + for (i = 0; i < (int)overlapLength ; i ++) + { + temp = (short)(overlapLength - i); + cnt2 = 2 * i; + output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength; + output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength; + } +} + + +/// Calculates overlap period length in samples. +/// Integer version rounds overlap length to closest power of 2 +/// for a divide scaling operation. +void TDStretch::calculateOverlapLength(uint overlapMs) +{ + uint newOvl; + + overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0); + if (overlapDividerBits > 9) overlapDividerBits = 9; + if (overlapDividerBits < 4) overlapDividerBits = 4; + newOvl = (uint)pow(2, overlapDividerBits); + + acceptNewOverlapLength(newOvl); + + // calculate sloping divider so that crosscorrelation operation won't + // overflow 32-bit register. Max. sum of the crosscorrelation sum without + // divider would be 2^30*(N^3-N)/3, where N = overlap length + slopingDivider = (newOvl * newOvl - 1) / 3; +} + + +long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const +{ + long corr; + uint i; + + corr = 0; + for (i = 1; i < overlapLength; i ++) + { + corr += (mixingPos[i] * compare[i]) >> overlapDividerBits; + } + + return corr; +} + + +long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const +{ + long corr; + uint i; + + corr = 0; + for (i = 2; i < 2 * overlapLength; i += 2) + { + corr += (mixingPos[i] * compare[i] + + mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; + } + + return corr; +} + +#endif // INTEGER_SAMPLES + +////////////////////////////////////////////////////////////////////////////// +// +// Floating point arithmetics specific algorithm implementations. +// + +#ifdef FLOAT_SAMPLES + + +// Slopes the amplitude of the 'midBuffer' samples so that cross correlation +// is faster to calculate +void TDStretch::precalcCorrReferenceStereo() +{ + int i, cnt2; + float temp; + + for (i=0 ; i < (int)overlapLength ;i ++) + { + temp = (float)i * (float)(overlapLength - i); + cnt2 = i * 2; + pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp); + pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp); + } +} + + +// Slopes the amplitude of the 'midBuffer' samples so that cross correlation +// is faster to calculate +void TDStretch::precalcCorrReferenceMono() +{ + int i; + float temp; + + for (i=0 ; i < (int)overlapLength ;i ++) + { + temp = (float)i * (float)(overlapLength - i); + pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp); + } +} + + +// SSE-optimized version of the function overlapStereo +void TDStretch::overlapStereo(float *output, const float *input) const +{ + int i; + uint cnt2; + float fTemp; + float fScale; + float fi; + + fScale = 1.0f / (float)overlapLength; + + for (i = 0; i < (int)overlapLength ; i ++) + { + fTemp = (float)(overlapLength - i) * fScale; + fi = (float)i * fScale; + cnt2 = 2 * i; + output[cnt2 + 0] = input[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp; + output[cnt2 + 1] = input[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp; + } +} + + +/// Calculates overlap period length in samples. +void TDStretch::calculateOverlapLength(uint overlapMs) +{ + uint newOvl; + + newOvl = (sampleRate * overlapMs) / 1000; + if (newOvl < 16) newOvl = 16; + + // must be divisible by 8 + newOvl -= newOvl % 8; + + acceptNewOverlapLength(newOvl); +} + + + +double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const +{ + double corr; + uint i; + + corr = 0; + for (i = 1; i < overlapLength; i ++) + { + corr += mixingPos[i] * compare[i]; + } + + return corr; +} + + +double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const +{ + double corr; + uint i; + + corr = 0; + for (i = 2; i < 2 * overlapLength; i += 2) + { + corr += mixingPos[i] * compare[i] + + mixingPos[i + 1] * compare[i + 1]; + } + + return corr; +} + +#endif // FLOAT_SAMPLES diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp index 75bd77109..68423c3a0 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp @@ -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 -#include -#include "cpu_detect.h" - -#ifndef __GNUC__ -#error wrong platform - this source code file is for the GNU C compiler. -#endif - -using namespace std; - -#include -////////////////////////////////////////////////////////////////////////////// -// -// 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 +#include +#include "cpu_detect.h" + +#ifndef __GNUC__ +#error wrong platform - this source code file is for the GNU C compiler. +#endif + +using namespace std; + +#include +////////////////////////////////////////////////////////////////////////////// +// +// 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 +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp index 8e7dd2970..93ae3f9e9 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp @@ -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; +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/mmx_optimized.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/mmx_optimized.cpp index 97fe0e999..762132861 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/mmx_optimized.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/mmx_optimized.cpp @@ -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 -#include - - -// 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 +#include + + +// 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 diff --git a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/sse_optimized.cpp b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/sse_optimized.cpp index 57598d78d..1e70da660 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/sse_optimized.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/SoundTouch/sse_optimized.cpp @@ -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 - -// 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 + +// 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 diff --git a/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/RunParameters.cpp b/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/RunParameters.cpp index 5bb787aed..766c36198 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/RunParameters.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/RunParameters.cpp @@ -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 -#include -#include - -#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 +#include +#include + +#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); + } +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/WavFile.cpp b/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/WavFile.cpp index dd3127b98..86dc57b8d 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/WavFile.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/WavFile.cpp @@ -1,711 +1,711 @@ -//////////////////////////////////////////////////////////////////////////////// -/// -/// Classes for easy reading & writing of WAV sound files. -/// -/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly -/// parse the WAV files with such processors. -/// -/// Admittingly, more complete WAV reader routines may exist in public domain, -/// but the reason for 'yet another' one is that those generic WAV reader -/// libraries are exhaustingly large and cumbersome! Wanted to have something -/// simpler here, i.e. something that's not already larger than rest of the -/// SoundTouch/SoundStretch program... -/// -/// 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.15 $ -// -// $Id: WavFile.cpp,v 1.15 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 -#include -#include -#include -#include - -#include "WavFile.h" - -using namespace std; - -const static char riffStr[] = "RIFF"; -const static char waveStr[] = "WAVE"; -const static char fmtStr[] = "fmt "; -const static char dataStr[] = "data"; - - -////////////////////////////////////////////////////////////////////////////// -// -// Helper functions for swapping byte order to correctly read/write WAV files -// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to -// turn-on the conversion if it appears necessary. -// -// For example, Intel x86 is little-endian and doesn't require conversion, -// while PowerPC of Mac's and many other RISC cpu's are big-endian. - -#ifdef BYTE_ORDER - // In gcc compiler detect the byte order automatically - #if BYTE_ORDER == BIG_ENDIAN - // big-endian platform. - #define _BIG_ENDIAN_ - #endif -#endif - -#ifdef _BIG_ENDIAN_ - // big-endian CPU, swap bytes in 16 & 32 bit words - - // helper-function to swap byte-order of 32bit integer - static inline void _swap32(unsigned int &dwData) - { - dwData = ((dwData >> 24) & 0x000000FF) | - ((dwData >> 8) & 0x0000FF00) | - ((dwData << 8) & 0x00FF0000) | - ((dwData << 24) & 0xFF000000); - } - - // helper-function to swap byte-order of 16bit integer - static inline void _swap16(unsigned short &wData) - { - wData = ((wData >> 8) & 0x00FF) | - ((wData << 8) & 0xFF00); - } - - // helper-function to swap byte-order of buffer of 16bit integers - static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords) - { - unsigned long i; - - for (i = 0; i < dwNumWords; i ++) - { - _swap16(pData[i]); - } - } - -#else // BIG_ENDIAN - // little-endian CPU, WAV file is ok as such - - // dummy helper-function - static inline void _swap32(unsigned int &dwData) - { - // do nothing - } - - // dummy helper-function - static inline void _swap16(unsigned short &wData) - { - // do nothing - } - - // dummy helper-function - static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes) - { - // do nothing - } - -#endif // BIG_ENDIAN - - -////////////////////////////////////////////////////////////////////////////// -// -// Class WavInFile -// - -WavInFile::WavInFile(const char *fileName) -{ - int hdrsOk; - - // Try to open the file for reading - fptr = fopen(fileName, "rb"); - if (fptr == NULL) - { - // didn't succeed - string msg = "Error : Unable to open file \""; - msg += fileName; - msg += "\" for reading."; - throw runtime_error(msg); - } - - // Read the file headers - hdrsOk = readWavHeaders(); - if (hdrsOk != 0) - { - // Something didn't match in the wav file headers - string msg = "File \""; - msg += fileName; - msg += "\" is corrupt or not a WAV file"; - throw runtime_error(msg); - } - - if (header.format.fixed != 1) - { - string msg = "File \""; - msg += fileName; - msg += "\" uses unsupported encoding."; - throw runtime_error(msg); - } - - dataRead = 0; -} - - - -WavInFile::~WavInFile() -{ - close(); -} - - - -void WavInFile::rewind() -{ - int hdrsOk; - - fseek(fptr, 0, SEEK_SET); - hdrsOk = readWavHeaders(); - assert(hdrsOk == 0); - dataRead = 0; -} - - -int WavInFile::checkCharTags() -{ - // header.format.fmt should equal to 'fmt ' - if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1; - // header.data.data_field should equal to 'data' - if (memcmp(dataStr, header.data.data_field, 4) != 0) return -1; - - return 0; -} - - -int WavInFile::read(char *buffer, int maxElems) -{ - int numBytes; - uint afterDataRead; - - // ensure it's 8 bit format - if (header.format.bits_per_sample != 8) - { - throw runtime_error("Error: WavInFile::read(char*, int) works only with 8bit samples."); - } - assert(sizeof(char) == 1); - - numBytes = maxElems; - afterDataRead = dataRead + numBytes; - if (afterDataRead > header.data.data_len) - { - // Don't read more samples than are marked available in header - numBytes = header.data.data_len - dataRead; - assert(numBytes >= 0); - } - - numBytes = fread(buffer, 1, numBytes, fptr); - dataRead += numBytes; - - return numBytes; -} - - -int WavInFile::read(short *buffer, int maxElems) -{ - unsigned int afterDataRead; - int numBytes; - int numElems; - - if (header.format.bits_per_sample == 8) - { - // 8 bit format - char *temp = new char[maxElems]; - int i; - - numElems = read(temp, maxElems); - // convert from 8 to 16 bit - for (i = 0; i < numElems; i ++) - { - buffer[i] = temp[i] << 8; - } - delete[] temp; - } - else - { - // 16 bit format - assert(header.format.bits_per_sample == 16); - assert(sizeof(short) == 2); - - numBytes = maxElems * 2; - afterDataRead = dataRead + numBytes; - if (afterDataRead > header.data.data_len) - { - // Don't read more samples than are marked available in header - numBytes = header.data.data_len - dataRead; - assert(numBytes >= 0); - } - - numBytes = fread(buffer, 1, numBytes, fptr); - dataRead += numBytes; - numElems = numBytes / 2; - - // 16bit samples, swap byte order if necessary - _swap16Buffer((unsigned short *)buffer, numElems); - } - - return numElems; -} - - - -int WavInFile::read(float *buffer, int maxElems) -{ - short *temp = new short[maxElems]; - int num; - int i; - double fscale; - - num = read(temp, maxElems); - - fscale = 1.0 / 32768.0; - // convert to floats, scale to range [-1..+1[ - for (i = 0; i < num; i ++) - { - buffer[i] = (float)(fscale * (double)temp[i]); - } - - delete[] temp; - - return num; -} - - -int WavInFile::eof() const -{ - // return true if all data has been read or file eof has reached - return (dataRead == header.data.data_len || feof(fptr)); -} - - -void WavInFile::close() -{ - fclose(fptr); - fptr = NULL; -} - - - -// test if character code is between a white space ' ' and little 'z' -static int isAlpha(char c) -{ - return (c >= ' ' && c <= 'z') ? 1 : 0; -} - - -// test if all characters are between a white space ' ' and little 'z' -static int isAlphaStr(char *str) -{ - int c; - - c = str[0]; - while (c) - { - if (isAlpha(c) == 0) return 0; - str ++; - c = str[0]; - } - - return 1; -} - - -int WavInFile::readRIFFBlock() -{ - fread(&(header.riff), sizeof(WavRiff), 1, fptr); - - // swap 32bit data byte order if necessary - _swap32((unsigned int &)header.riff.package_len); - - // header.riff.riff_char should equal to 'RIFF'); - if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1; - // header.riff.wave should equal to 'WAVE' - if (memcmp(waveStr, header.riff.wave, 4) != 0) return -1; - - return 0; -} - - - - -int WavInFile::readHeaderBlock() -{ - char label[5]; - string sLabel; - - // lead label string - fread(label, 1, 4, fptr); - label[4] = 0; - - if (isAlphaStr(label) == 0) return -1; // not a valid label - - // Decode blocks according to their label - if (strcmp(label, fmtStr) == 0) - { - int nLen, nDump; - - // 'fmt ' block - memcpy(header.format.fmt, fmtStr, 4); - - // read length of the format field - fread(&nLen, sizeof(int), 1, fptr); - // swap byte order if necessary - _swap32((unsigned int &)nLen); // int format_len; - header.format.format_len = nLen; - - // calculate how much length differs from expected - nDump = nLen - (sizeof(header.format) - 8); - - // if format_len is larger than expected, read only as much data as we've space for - if (nDump > 0) - { - nLen = sizeof(header.format) - 8; - } - - // read data - fread(&(header.format.fixed), nLen, 1, fptr); - - // swap byte order if necessary - _swap16((unsigned short &)header.format.fixed); // short int fixed; - _swap16((unsigned short &)header.format.channel_number); // short int channel_number; - _swap32((unsigned int &)header.format.sample_rate); // int sample_rate; - _swap32((unsigned int &)header.format.byte_rate); // int byte_rate; - _swap16((unsigned short &)header.format.byte_per_sample); // short int byte_per_sample; - _swap16((unsigned short &)header.format.bits_per_sample); // short int bits_per_sample; - - // if format_len is larger than expected, skip the extra data - if (nDump > 0) - { - fseek(fptr, nDump, SEEK_CUR); - } - - return 0; - } - else if (strcmp(label, dataStr) == 0) - { - // 'data' block - memcpy(header.data.data_field, dataStr, 4); - fread(&(header.data.data_len), sizeof(uint), 1, fptr); - - // swap byte order if necessary - _swap32((unsigned int &)header.data.data_len); - - return 1; - } - else - { - uint len, i; - uint temp; - // unknown block - - // read length - fread(&len, sizeof(len), 1, fptr); - // scan through the block - for (i = 0; i < len; i ++) - { - fread(&temp, 1, 1, fptr); - if (feof(fptr)) return -1; // unexpected eof - } - } - return 0; -} - - -int WavInFile::readWavHeaders() -{ - int res; - - memset(&header, 0, sizeof(header)); - - res = readRIFFBlock(); - if (res) return 1; - // read header blocks until data block is found - do - { - // read header blocks - res = readHeaderBlock(); - if (res < 0) return 1; // error in file structure - } while (res == 0); - // check that all required tags are legal - return checkCharTags(); -} - - -uint WavInFile::getNumChannels() const -{ - return header.format.channel_number; -} - - -uint WavInFile::getNumBits() const -{ - return header.format.bits_per_sample; -} - - -uint WavInFile::getBytesPerSample() const -{ - return getNumChannels() * getNumBits() / 8; -} - - -uint WavInFile::getSampleRate() const -{ - return header.format.sample_rate; -} - - - -uint WavInFile::getDataSizeInBytes() const -{ - return header.data.data_len; -} - - -uint WavInFile::getNumSamples() const -{ - return header.data.data_len / header.format.byte_per_sample; -} - - -uint WavInFile::getLengthMS() const -{ - uint numSamples; - uint sampleRate; - - numSamples = getNumSamples(); - sampleRate = getSampleRate(); - - assert(numSamples < UINT_MAX / 1000); - return (1000 * numSamples / sampleRate); -} - - -////////////////////////////////////////////////////////////////////////////// -// -// Class WavOutFile -// - -WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels) -{ - bytesWritten = 0; - fptr = fopen(fileName, "wb"); - if (fptr == NULL) - { - string msg = "Error : Unable to open file \""; - msg += fileName; - msg += "\" for writing."; - //pmsg = msg.c_str; - throw runtime_error(msg); - } - - fillInHeader(sampleRate, bits, channels); - writeHeader(); -} - - - -WavOutFile::~WavOutFile() -{ - close(); -} - - - -void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels) -{ - // fill in the 'riff' part.. - - // copy string 'RIFF' to riff_char - memcpy(&(header.riff.riff_char), riffStr, 4); - // package_len unknown so far - header.riff.package_len = 0; - // copy string 'WAVE' to wave - memcpy(&(header.riff.wave), waveStr, 4); - - - // fill in the 'format' part.. - - // copy string 'fmt ' to fmt - memcpy(&(header.format.fmt), fmtStr, 4); - - header.format.format_len = 0x10; - header.format.fixed = 1; - header.format.channel_number = (short)channels; - header.format.sample_rate = sampleRate; - header.format.bits_per_sample = (short)bits; - header.format.byte_per_sample = (short)(bits * channels / 8); - header.format.byte_rate = header.format.byte_per_sample * sampleRate; - header.format.sample_rate = sampleRate; - - // fill in the 'data' part.. - - // copy string 'data' to data_field - memcpy(&(header.data.data_field), dataStr, 4); - // data_len unknown so far - header.data.data_len = 0; -} - - -void WavOutFile::finishHeader() -{ - // supplement the file length into the header structure - header.riff.package_len = bytesWritten + 36; - header.data.data_len = bytesWritten; - - writeHeader(); -} - - - -void WavOutFile::writeHeader() -{ - WavHeader hdrTemp; - - // swap byte order if necessary - hdrTemp = header; - _swap32((unsigned int &)hdrTemp.riff.package_len); - _swap32((unsigned int &)hdrTemp.format.format_len); - _swap16((unsigned short &)hdrTemp.format.fixed); - _swap16((unsigned short &)hdrTemp.format.channel_number); - _swap32((unsigned int &)hdrTemp.format.sample_rate); - _swap32((unsigned int &)hdrTemp.format.byte_rate); - _swap16((unsigned short &)hdrTemp.format.byte_per_sample); - _swap16((unsigned short &)hdrTemp.format.bits_per_sample); - _swap32((unsigned int &)hdrTemp.data.data_len); - - // write the supplemented header in the beginning of the file - fseek(fptr, 0, SEEK_SET); - fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr); - // jump back to the end of the file - fseek(fptr, 0, SEEK_END); -} - - - -void WavOutFile::close() -{ - finishHeader(); - fclose(fptr); - fptr = NULL; -} - - -void WavOutFile::write(const char *buffer, int numElems) -{ - int res; - - if (header.format.bits_per_sample != 8) - { - throw runtime_error("Error: WavOutFile::write(const char*, int) accepts only 8bit samples."); - } - assert(sizeof(char) == 1); - - res = fwrite(buffer, 1, numElems, fptr); - if (res != numElems) - { - throw runtime_error("Error while writing to a wav file."); - } - - bytesWritten += numElems; -} - - -void WavOutFile::write(const short *buffer, int numElems) -{ - int res; - - // 16 bit samples - if (numElems < 1) return; // nothing to do - - if (header.format.bits_per_sample == 8) - { - int i; - char *temp = new char[numElems]; - // convert from 16bit format to 8bit format - for (i = 0; i < numElems; i ++) - { - temp[i] = buffer[i] >> 8; - } - // write in 8bit format - write(temp, numElems); - delete[] temp; - } - else - { - // 16bit format - unsigned short *pTemp = new unsigned short[numElems]; - - assert(header.format.bits_per_sample == 16); - - // allocate temp buffer to swap byte order if necessary - memcpy(pTemp, buffer, numElems * 2); - _swap16Buffer(pTemp, numElems); - - res = fwrite(pTemp, 2, numElems, fptr); - - delete[] pTemp; - - if (res != numElems) - { - throw runtime_error("Error while writing to a wav file."); - } - bytesWritten += 2 * numElems; - } -} - - -void WavOutFile::write(const float *buffer, int numElems) -{ - int i; - short *temp = new short[numElems]; - int iTemp; - - // convert to 16 bit integer - for (i = 0; i < numElems; i ++) - { - // convert to integer - iTemp = (int)(32768.0f * buffer[i]); - - // saturate - if (iTemp < -32768) iTemp = -32768; - if (iTemp > 32767) iTemp = 32767; - temp[i] = (short)iTemp; - } - - write(temp, numElems); - - delete[] temp; -} +//////////////////////////////////////////////////////////////////////////////// +/// +/// Classes for easy reading & writing of WAV sound files. +/// +/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly +/// parse the WAV files with such processors. +/// +/// Admittingly, more complete WAV reader routines may exist in public domain, +/// but the reason for 'yet another' one is that those generic WAV reader +/// libraries are exhaustingly large and cumbersome! Wanted to have something +/// simpler here, i.e. something that's not already larger than rest of the +/// SoundTouch/SoundStretch program... +/// +/// 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.15 $ +// +// $Id: WavFile.cpp,v 1.15 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 +#include +#include +#include +#include + +#include "WavFile.h" + +using namespace std; + +const static char riffStr[] = "RIFF"; +const static char waveStr[] = "WAVE"; +const static char fmtStr[] = "fmt "; +const static char dataStr[] = "data"; + + +////////////////////////////////////////////////////////////////////////////// +// +// Helper functions for swapping byte order to correctly read/write WAV files +// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to +// turn-on the conversion if it appears necessary. +// +// For example, Intel x86 is little-endian and doesn't require conversion, +// while PowerPC of Mac's and many other RISC cpu's are big-endian. + +#ifdef BYTE_ORDER + // In gcc compiler detect the byte order automatically + #if BYTE_ORDER == BIG_ENDIAN + // big-endian platform. + #define _BIG_ENDIAN_ + #endif +#endif + +#ifdef _BIG_ENDIAN_ + // big-endian CPU, swap bytes in 16 & 32 bit words + + // helper-function to swap byte-order of 32bit integer + static inline void _swap32(unsigned int &dwData) + { + dwData = ((dwData >> 24) & 0x000000FF) | + ((dwData >> 8) & 0x0000FF00) | + ((dwData << 8) & 0x00FF0000) | + ((dwData << 24) & 0xFF000000); + } + + // helper-function to swap byte-order of 16bit integer + static inline void _swap16(unsigned short &wData) + { + wData = ((wData >> 8) & 0x00FF) | + ((wData << 8) & 0xFF00); + } + + // helper-function to swap byte-order of buffer of 16bit integers + static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords) + { + unsigned long i; + + for (i = 0; i < dwNumWords; i ++) + { + _swap16(pData[i]); + } + } + +#else // BIG_ENDIAN + // little-endian CPU, WAV file is ok as such + + // dummy helper-function + static inline void _swap32(unsigned int &dwData) + { + // do nothing + } + + // dummy helper-function + static inline void _swap16(unsigned short &wData) + { + // do nothing + } + + // dummy helper-function + static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes) + { + // do nothing + } + +#endif // BIG_ENDIAN + + +////////////////////////////////////////////////////////////////////////////// +// +// Class WavInFile +// + +WavInFile::WavInFile(const char *fileName) +{ + int hdrsOk; + + // Try to open the file for reading + fptr = fopen(fileName, "rb"); + if (fptr == NULL) + { + // didn't succeed + string msg = "Error : Unable to open file \""; + msg += fileName; + msg += "\" for reading."; + throw runtime_error(msg); + } + + // Read the file headers + hdrsOk = readWavHeaders(); + if (hdrsOk != 0) + { + // Something didn't match in the wav file headers + string msg = "File \""; + msg += fileName; + msg += "\" is corrupt or not a WAV file"; + throw runtime_error(msg); + } + + if (header.format.fixed != 1) + { + string msg = "File \""; + msg += fileName; + msg += "\" uses unsupported encoding."; + throw runtime_error(msg); + } + + dataRead = 0; +} + + + +WavInFile::~WavInFile() +{ + close(); +} + + + +void WavInFile::rewind() +{ + int hdrsOk; + + fseek(fptr, 0, SEEK_SET); + hdrsOk = readWavHeaders(); + assert(hdrsOk == 0); + dataRead = 0; +} + + +int WavInFile::checkCharTags() +{ + // header.format.fmt should equal to 'fmt ' + if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1; + // header.data.data_field should equal to 'data' + if (memcmp(dataStr, header.data.data_field, 4) != 0) return -1; + + return 0; +} + + +int WavInFile::read(char *buffer, int maxElems) +{ + int numBytes; + uint afterDataRead; + + // ensure it's 8 bit format + if (header.format.bits_per_sample != 8) + { + throw runtime_error("Error: WavInFile::read(char*, int) works only with 8bit samples."); + } + assert(sizeof(char) == 1); + + numBytes = maxElems; + afterDataRead = dataRead + numBytes; + if (afterDataRead > header.data.data_len) + { + // Don't read more samples than are marked available in header + numBytes = header.data.data_len - dataRead; + assert(numBytes >= 0); + } + + numBytes = fread(buffer, 1, numBytes, fptr); + dataRead += numBytes; + + return numBytes; +} + + +int WavInFile::read(short *buffer, int maxElems) +{ + unsigned int afterDataRead; + int numBytes; + int numElems; + + if (header.format.bits_per_sample == 8) + { + // 8 bit format + char *temp = new char[maxElems]; + int i; + + numElems = read(temp, maxElems); + // convert from 8 to 16 bit + for (i = 0; i < numElems; i ++) + { + buffer[i] = temp[i] << 8; + } + delete[] temp; + } + else + { + // 16 bit format + assert(header.format.bits_per_sample == 16); + assert(sizeof(short) == 2); + + numBytes = maxElems * 2; + afterDataRead = dataRead + numBytes; + if (afterDataRead > header.data.data_len) + { + // Don't read more samples than are marked available in header + numBytes = header.data.data_len - dataRead; + assert(numBytes >= 0); + } + + numBytes = fread(buffer, 1, numBytes, fptr); + dataRead += numBytes; + numElems = numBytes / 2; + + // 16bit samples, swap byte order if necessary + _swap16Buffer((unsigned short *)buffer, numElems); + } + + return numElems; +} + + + +int WavInFile::read(float *buffer, int maxElems) +{ + short *temp = new short[maxElems]; + int num; + int i; + double fscale; + + num = read(temp, maxElems); + + fscale = 1.0 / 32768.0; + // convert to floats, scale to range [-1..+1[ + for (i = 0; i < num; i ++) + { + buffer[i] = (float)(fscale * (double)temp[i]); + } + + delete[] temp; + + return num; +} + + +int WavInFile::eof() const +{ + // return true if all data has been read or file eof has reached + return (dataRead == header.data.data_len || feof(fptr)); +} + + +void WavInFile::close() +{ + fclose(fptr); + fptr = NULL; +} + + + +// test if character code is between a white space ' ' and little 'z' +static int isAlpha(char c) +{ + return (c >= ' ' && c <= 'z') ? 1 : 0; +} + + +// test if all characters are between a white space ' ' and little 'z' +static int isAlphaStr(char *str) +{ + int c; + + c = str[0]; + while (c) + { + if (isAlpha(c) == 0) return 0; + str ++; + c = str[0]; + } + + return 1; +} + + +int WavInFile::readRIFFBlock() +{ + fread(&(header.riff), sizeof(WavRiff), 1, fptr); + + // swap 32bit data byte order if necessary + _swap32((unsigned int &)header.riff.package_len); + + // header.riff.riff_char should equal to 'RIFF'); + if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1; + // header.riff.wave should equal to 'WAVE' + if (memcmp(waveStr, header.riff.wave, 4) != 0) return -1; + + return 0; +} + + + + +int WavInFile::readHeaderBlock() +{ + char label[5]; + string sLabel; + + // lead label string + fread(label, 1, 4, fptr); + label[4] = 0; + + if (isAlphaStr(label) == 0) return -1; // not a valid label + + // Decode blocks according to their label + if (strcmp(label, fmtStr) == 0) + { + int nLen, nDump; + + // 'fmt ' block + memcpy(header.format.fmt, fmtStr, 4); + + // read length of the format field + fread(&nLen, sizeof(int), 1, fptr); + // swap byte order if necessary + _swap32((unsigned int &)nLen); // int format_len; + header.format.format_len = nLen; + + // calculate how much length differs from expected + nDump = nLen - (sizeof(header.format) - 8); + + // if format_len is larger than expected, read only as much data as we've space for + if (nDump > 0) + { + nLen = sizeof(header.format) - 8; + } + + // read data + fread(&(header.format.fixed), nLen, 1, fptr); + + // swap byte order if necessary + _swap16((unsigned short &)header.format.fixed); // short int fixed; + _swap16((unsigned short &)header.format.channel_number); // short int channel_number; + _swap32((unsigned int &)header.format.sample_rate); // int sample_rate; + _swap32((unsigned int &)header.format.byte_rate); // int byte_rate; + _swap16((unsigned short &)header.format.byte_per_sample); // short int byte_per_sample; + _swap16((unsigned short &)header.format.bits_per_sample); // short int bits_per_sample; + + // if format_len is larger than expected, skip the extra data + if (nDump > 0) + { + fseek(fptr, nDump, SEEK_CUR); + } + + return 0; + } + else if (strcmp(label, dataStr) == 0) + { + // 'data' block + memcpy(header.data.data_field, dataStr, 4); + fread(&(header.data.data_len), sizeof(uint), 1, fptr); + + // swap byte order if necessary + _swap32((unsigned int &)header.data.data_len); + + return 1; + } + else + { + uint len, i; + uint temp; + // unknown block + + // read length + fread(&len, sizeof(len), 1, fptr); + // scan through the block + for (i = 0; i < len; i ++) + { + fread(&temp, 1, 1, fptr); + if (feof(fptr)) return -1; // unexpected eof + } + } + return 0; +} + + +int WavInFile::readWavHeaders() +{ + int res; + + memset(&header, 0, sizeof(header)); + + res = readRIFFBlock(); + if (res) return 1; + // read header blocks until data block is found + do + { + // read header blocks + res = readHeaderBlock(); + if (res < 0) return 1; // error in file structure + } while (res == 0); + // check that all required tags are legal + return checkCharTags(); +} + + +uint WavInFile::getNumChannels() const +{ + return header.format.channel_number; +} + + +uint WavInFile::getNumBits() const +{ + return header.format.bits_per_sample; +} + + +uint WavInFile::getBytesPerSample() const +{ + return getNumChannels() * getNumBits() / 8; +} + + +uint WavInFile::getSampleRate() const +{ + return header.format.sample_rate; +} + + + +uint WavInFile::getDataSizeInBytes() const +{ + return header.data.data_len; +} + + +uint WavInFile::getNumSamples() const +{ + return header.data.data_len / header.format.byte_per_sample; +} + + +uint WavInFile::getLengthMS() const +{ + uint numSamples; + uint sampleRate; + + numSamples = getNumSamples(); + sampleRate = getSampleRate(); + + assert(numSamples < UINT_MAX / 1000); + return (1000 * numSamples / sampleRate); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Class WavOutFile +// + +WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels) +{ + bytesWritten = 0; + fptr = fopen(fileName, "wb"); + if (fptr == NULL) + { + string msg = "Error : Unable to open file \""; + msg += fileName; + msg += "\" for writing."; + //pmsg = msg.c_str; + throw runtime_error(msg); + } + + fillInHeader(sampleRate, bits, channels); + writeHeader(); +} + + + +WavOutFile::~WavOutFile() +{ + close(); +} + + + +void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels) +{ + // fill in the 'riff' part.. + + // copy string 'RIFF' to riff_char + memcpy(&(header.riff.riff_char), riffStr, 4); + // package_len unknown so far + header.riff.package_len = 0; + // copy string 'WAVE' to wave + memcpy(&(header.riff.wave), waveStr, 4); + + + // fill in the 'format' part.. + + // copy string 'fmt ' to fmt + memcpy(&(header.format.fmt), fmtStr, 4); + + header.format.format_len = 0x10; + header.format.fixed = 1; + header.format.channel_number = (short)channels; + header.format.sample_rate = sampleRate; + header.format.bits_per_sample = (short)bits; + header.format.byte_per_sample = (short)(bits * channels / 8); + header.format.byte_rate = header.format.byte_per_sample * sampleRate; + header.format.sample_rate = sampleRate; + + // fill in the 'data' part.. + + // copy string 'data' to data_field + memcpy(&(header.data.data_field), dataStr, 4); + // data_len unknown so far + header.data.data_len = 0; +} + + +void WavOutFile::finishHeader() +{ + // supplement the file length into the header structure + header.riff.package_len = bytesWritten + 36; + header.data.data_len = bytesWritten; + + writeHeader(); +} + + + +void WavOutFile::writeHeader() +{ + WavHeader hdrTemp; + + // swap byte order if necessary + hdrTemp = header; + _swap32((unsigned int &)hdrTemp.riff.package_len); + _swap32((unsigned int &)hdrTemp.format.format_len); + _swap16((unsigned short &)hdrTemp.format.fixed); + _swap16((unsigned short &)hdrTemp.format.channel_number); + _swap32((unsigned int &)hdrTemp.format.sample_rate); + _swap32((unsigned int &)hdrTemp.format.byte_rate); + _swap16((unsigned short &)hdrTemp.format.byte_per_sample); + _swap16((unsigned short &)hdrTemp.format.bits_per_sample); + _swap32((unsigned int &)hdrTemp.data.data_len); + + // write the supplemented header in the beginning of the file + fseek(fptr, 0, SEEK_SET); + fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr); + // jump back to the end of the file + fseek(fptr, 0, SEEK_END); +} + + + +void WavOutFile::close() +{ + finishHeader(); + fclose(fptr); + fptr = NULL; +} + + +void WavOutFile::write(const char *buffer, int numElems) +{ + int res; + + if (header.format.bits_per_sample != 8) + { + throw runtime_error("Error: WavOutFile::write(const char*, int) accepts only 8bit samples."); + } + assert(sizeof(char) == 1); + + res = fwrite(buffer, 1, numElems, fptr); + if (res != numElems) + { + throw runtime_error("Error while writing to a wav file."); + } + + bytesWritten += numElems; +} + + +void WavOutFile::write(const short *buffer, int numElems) +{ + int res; + + // 16 bit samples + if (numElems < 1) return; // nothing to do + + if (header.format.bits_per_sample == 8) + { + int i; + char *temp = new char[numElems]; + // convert from 16bit format to 8bit format + for (i = 0; i < numElems; i ++) + { + temp[i] = buffer[i] >> 8; + } + // write in 8bit format + write(temp, numElems); + delete[] temp; + } + else + { + // 16bit format + unsigned short *pTemp = new unsigned short[numElems]; + + assert(header.format.bits_per_sample == 16); + + // allocate temp buffer to swap byte order if necessary + memcpy(pTemp, buffer, numElems * 2); + _swap16Buffer(pTemp, numElems); + + res = fwrite(pTemp, 2, numElems, fptr); + + delete[] pTemp; + + if (res != numElems) + { + throw runtime_error("Error while writing to a wav file."); + } + bytesWritten += 2 * numElems; + } +} + + +void WavOutFile::write(const float *buffer, int numElems) +{ + int i; + short *temp = new short[numElems]; + int iTemp; + + // convert to 16 bit integer + for (i = 0; i < numElems; i ++) + { + // convert to integer + iTemp = (int)(32768.0f * buffer[i]); + + // saturate + if (iTemp < -32768) iTemp = -32768; + if (iTemp > 32767) iTemp = 32767; + temp[i] = (short)iTemp; + } + + write(temp, numElems); + + delete[] temp; +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/main.cpp b/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/main.cpp index 3a584f083..568b02921 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/main.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/example/SoundStretch/main.cpp @@ -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 -#include -#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: - 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 +#include +#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: - 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; +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/example/bpm/BPMDetect.cpp b/src/contrib/bpm_analyzer/soundtouch/source/example/bpm/BPMDetect.cpp index 7a57d010d..75fd5edc2 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/example/bpm/BPMDetect.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/example/bpm/BPMDetect.cpp @@ -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 -#include -#include -#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 +#include +#include +#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); +} diff --git a/src/contrib/bpm_analyzer/soundtouch/source/example/bpm/PeakFinder.cpp b/src/contrib/bpm_analyzer/soundtouch/source/example/bpm/PeakFinder.cpp index 77621e8a6..d99925384 100644 --- a/src/contrib/bpm_analyzer/soundtouch/source/example/bpm/PeakFinder.cpp +++ b/src/contrib/bpm_analyzer/soundtouch/source/example/bpm/PeakFinder.cpp @@ -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 -#include - -#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 +#include + +#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); +} + + diff --git a/src/contrib/cddadecoder/CddaDataStream.cpp b/src/contrib/cddadecoder/CddaDataStream.cpp index 5cc0f0906..919bded4d 100755 --- a/src/contrib/cddadecoder/CddaDataStream.cpp +++ b/src/contrib/cddadecoder/CddaDataStream.cpp @@ -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 -#include -#include - -#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 +#include +#include + +#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; +} diff --git a/src/contrib/cddadecoder/CddaDataStreamFactory.cpp b/src/contrib/cddadecoder/CddaDataStreamFactory.cpp index 1206c44a4..b53296a7a 100755 --- a/src/contrib/cddadecoder/CddaDataStreamFactory.cpp +++ b/src/contrib/cddadecoder/CddaDataStreamFactory.cpp @@ -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 -#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 +#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; } \ No newline at end of file diff --git a/src/contrib/cddadecoder/CddaDecoder.cpp b/src/contrib/cddadecoder/CddaDecoder.cpp index 573a987fa..e54986bb3 100644 --- a/src/contrib/cddadecoder/CddaDecoder.cpp +++ b/src/contrib/cddadecoder/CddaDecoder.cpp @@ -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; +} diff --git a/src/contrib/cddadecoder/CddaDecoderFactory.cpp b/src/contrib/cddadecoder/CddaDecoderFactory.cpp index 2dc3f77a5..bd9a28ce5 100644 --- a/src/contrib/cddadecoder/CddaDecoderFactory.cpp +++ b/src/contrib/cddadecoder/CddaDecoderFactory.cpp @@ -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 -#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 +#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"; +} diff --git a/src/contrib/cddadecoder/cddadecoder_plugin.cpp b/src/contrib/cddadecoder/cddadecoder_plugin.cpp index 03f756939..ab0f45f98 100644 --- a/src/contrib/cddadecoder/cddadecoder_plugin.cpp +++ b/src/contrib/cddadecoder/cddadecoder_plugin.cpp @@ -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 - -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 + +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(); +} diff --git a/src/contrib/cddadecoder/stdafx.cpp b/src/contrib/cddadecoder/stdafx.cpp index 4ad77dad7..83e24d2a8 100644 --- a/src/contrib/cddadecoder/stdafx.cpp +++ b/src/contrib/cddadecoder/stdafx.cpp @@ -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" \ No newline at end of file diff --git a/src/contrib/coreaudioout/coreaudioout_plugin.cpp b/src/contrib/coreaudioout/coreaudioout_plugin.cpp index 7c14f74ec..cd43970f7 100644 --- a/src/contrib/coreaudioout/coreaudioout_plugin.cpp +++ b/src/contrib/coreaudioout/coreaudioout_plugin.cpp @@ -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 -#include -#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 +#include +#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(); +} diff --git a/src/contrib/dsp_example_echo/DSPEcho.cpp b/src/contrib/dsp_example_echo/DSPEcho.cpp index fd882f802..c7acb387b 100644 --- a/src/contrib/dsp_example_echo/DSPEcho.cpp +++ b/src/contrib/dsp_example_echo/DSPEcho.cpp @@ -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;ichannels*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);iinternalBuffer[(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;ichannels*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);iinternalBuffer[(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; +} + diff --git a/src/contrib/dsp_example_echo/dsp_echo_plugin.cpp b/src/contrib/dsp_example_echo/dsp_echo_plugin.cpp index b56b4e045..b9f4ca6ea 100644 --- a/src/contrib/dsp_example_echo/dsp_echo_plugin.cpp +++ b/src/contrib/dsp_example_echo/dsp_echo_plugin.cpp @@ -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 -#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 +#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(); +} diff --git a/src/contrib/dsp_example_echo/pch.cpp b/src/contrib/dsp_example_echo/pch.cpp index ac4ae04c7..815f8a271 100644 --- a/src/contrib/dsp_example_echo/pch.cpp +++ b/src/contrib/dsp_example_echo/pch.cpp @@ -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" \ No newline at end of file diff --git a/src/contrib/flacdecoder/FlacDecoder.cpp b/src/contrib/flacdecoder/FlacDecoder.cpp index bdf0a2526..502c0c0be 100644 --- a/src/contrib/flacdecoder/FlacDecoder.cpp +++ b/src/contrib/flacdecoder/FlacDecoder.cpp @@ -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 -#include -#include - -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 +#include +#include + +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; +} diff --git a/src/contrib/flacdecoder/FlacDecoderFactory.cpp b/src/contrib/flacdecoder/FlacDecoderFactory.cpp index 9fdc05d64..d8d2186cf 100644 --- a/src/contrib/flacdecoder/FlacDecoderFactory.cpp +++ b/src/contrib/flacdecoder/FlacDecoderFactory.cpp @@ -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 -#include -#include - -#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 +#include +#include + +#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; +} diff --git a/src/contrib/flacdecoder/flacdecoder_plugin.cpp b/src/contrib/flacdecoder/flacdecoder_plugin.cpp index 9044ba2f2..f8a9ae5b0 100644 --- a/src/contrib/flacdecoder/flacdecoder_plugin.cpp +++ b/src/contrib/flacdecoder/flacdecoder_plugin.cpp @@ -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 -#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 +#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(); +} diff --git a/src/contrib/flacdecoder/stdafx.cpp b/src/contrib/flacdecoder/stdafx.cpp index 81baa09cb..e11ae5fce 100644 --- a/src/contrib/flacdecoder/stdafx.cpp +++ b/src/contrib/flacdecoder/stdafx.cpp @@ -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" diff --git a/src/contrib/m4adecoder/M4aDecoder.cpp b/src/contrib/m4adecoder/M4aDecoder.cpp index df6578a6d..d61a16ad8 100644 --- a/src/contrib/m4adecoder/M4aDecoder.cpp +++ b/src/contrib/m4adecoder/M4aDecoder.cpp @@ -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 - -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(userData); - return (uint32_t) stream->Read(buffer, length); -} - -static uint32_t streamSeekCallback(void *userData, uint64_t position) { - IDataStream *stream = static_cast(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(target->BufferPointer()), - static_cast(sampleBuffer), - sizeof(float) * frameInfo.samples); - - return true; -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "M4aDecoder.h" +#include + +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(userData); + return (uint32_t) stream->Read(buffer, length); +} + +static uint32_t streamSeekCallback(void *userData, uint64_t position) { + IDataStream *stream = static_cast(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(target->BufferPointer()), + static_cast(sampleBuffer), + sizeof(float) * frameInfo.samples); + + return true; +} diff --git a/src/contrib/m4adecoder/M4aDecoderFactory.cpp b/src/contrib/m4adecoder/M4aDecoderFactory.cpp index 9ed30741b..7b86ad1e8 100644 --- a/src/contrib/m4adecoder/M4aDecoderFactory.cpp +++ b/src/contrib/m4adecoder/M4aDecoderFactory.cpp @@ -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 - -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 + +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; +} + + + diff --git a/src/contrib/m4adecoder/stdafx.cpp b/src/contrib/m4adecoder/stdafx.cpp index 4ad77dad7..83e24d2a8 100644 --- a/src/contrib/m4adecoder/stdafx.cpp +++ b/src/contrib/m4adecoder/stdafx.cpp @@ -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" \ No newline at end of file diff --git a/src/contrib/mp3decoder/BaseDecoder.cpp b/src/contrib/mp3decoder/BaseDecoder.cpp index 8eefc4e46..b6c13bd13 100644 --- a/src/contrib/mp3decoder/BaseDecoder.cpp +++ b/src/contrib/mp3decoder/BaseDecoder.cpp @@ -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 -#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 +#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++; + + } } \ No newline at end of file diff --git a/src/contrib/mp3decoder/BitStream.cpp b/src/contrib/mp3decoder/BitStream.cpp index 1952b42a2..00dd0fb2d 100644 --- a/src/contrib/mp3decoder/BitStream.cpp +++ b/src/contrib/mp3decoder/BitStream.cpp @@ -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); +} diff --git a/src/contrib/mp3decoder/CRC.cpp b/src/contrib/mp3decoder/CRC.cpp index fe48de30a..e35711e56 100644 --- a/src/contrib/mp3decoder/CRC.cpp +++ b/src/contrib/mp3decoder/CRC.cpp @@ -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); +} diff --git a/src/contrib/mp3decoder/FrameSplitter.cpp b/src/contrib/mp3decoder/FrameSplitter.cpp index cb94368ab..401b8580c 100644 --- a/src/contrib/mp3decoder/FrameSplitter.cpp +++ b/src/contrib/mp3decoder/FrameSplitter.cpp @@ -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); +} diff --git a/src/contrib/mp3decoder/Header.cpp b/src/contrib/mp3decoder/Header.cpp index 4ae24f483..9f1ebe09f 100644 --- a/src/contrib/mp3decoder/Header.cpp +++ b/src/contrib/mp3decoder/Header.cpp @@ -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); +} diff --git a/src/contrib/mp3decoder/Layer3Decoder.cpp b/src/contrib/mp3decoder/Layer3Decoder.cpp index 2df1a88fb..3105898a5 100644 --- a/src/contrib/mp3decoder/Layer3Decoder.cpp +++ b/src/contrib/mp3decoder/Layer3Decoder.cpp @@ -1,2104 +1,2104 @@ - -#include -#include -#include "Layer3Decoder.h" - -static unsigned long g_huffman_table_1[7] = { - 0x00020001, 0x00000000, 0x00020001, 0x00000010, 0x00020001, - 0x00000001, 0x00000011, -}; - -static unsigned long g_huffman_table_2[17] = { - 0x00020001, 0x00000000, 0x00040001, 0x00020001, 0x00000010, - 0x00000001, 0x00020001, 0x00000011, 0x00040001, 0x00020001, - 0x00000020, 0x00000021, 0x00020001, 0x00000012, 0x00020001, - 0x00000002, 0x00000022, -}; - -static unsigned long g_huffman_table_3[17] = { - 0x00040001, 0x00020001, 0x00000000, 0x00000001, 0x00020001, - 0x00000011, 0x00020001, 0x00000010, 0x00040001, 0x00020001, - 0x00000020, 0x00000021, 0x00020001, 0x00000012, 0x00020001, - 0x00000002, 0x00000022, -}; - -static unsigned long g_huffman_table_5[31] = { - 0x00020001, 0x00000000, 0x00040001, 0x00020001, 0x00000010, - 0x00000001, 0x00020001, 0x00000011, 0x00080001, 0x00040001, - 0x00020001, 0x00000020, 0x00000002, 0x00020001, 0x00000021, - 0x00000012, 0x00080001, 0x00040001, 0x00020001, 0x00000022, - 0x00000030, 0x00020001, 0x00000003, 0x00000013, 0x00020001, - 0x00000031, 0x00020001, 0x00000032, 0x00020001, 0x00000023, - 0x00000033, -}; - -static unsigned long g_huffman_table_6[31] = { - 0x00060001, 0x00040001, 0x00020001, 0x00000000, 0x00000010, - 0x00000011, 0x00060001, 0x00020001, 0x00000001, 0x00020001, - 0x00000020, 0x00000021, 0x00060001, 0x00020001, 0x00000012, - 0x00020001, 0x00000002, 0x00000022, 0x00040001, 0x00020001, - 0x00000031, 0x00000013, 0x00040001, 0x00020001, 0x00000030, - 0x00000032, 0x00020001, 0x00000023, 0x00020001, 0x00000003, - 0x00000033, -}; - -static unsigned long g_huffman_table_7[71] = { - 0x00020001, 0x00000000, 0x00040001, 0x00020001, 0x00000010, - 0x00000001, 0x00080001, 0x00020001, 0x00000011, 0x00040001, - 0x00020001, 0x00000020, 0x00000002, 0x00000021, 0x00120001, - 0x00060001, 0x00020001, 0x00000012, 0x00020001, 0x00000022, - 0x00000030, 0x00040001, 0x00020001, 0x00000031, 0x00000013, - 0x00040001, 0x00020001, 0x00000003, 0x00000032, 0x00020001, - 0x00000023, 0x00000004, 0x000a0001, 0x00040001, 0x00020001, - 0x00000040, 0x00000041, 0x00020001, 0x00000014, 0x00020001, - 0x00000042, 0x00000024, 0x000c0001, 0x00060001, 0x00040001, - 0x00020001, 0x00000033, 0x00000043, 0x00000050, 0x00040001, - 0x00020001, 0x00000034, 0x00000005, 0x00000051, 0x00060001, - 0x00020001, 0x00000015, 0x00020001, 0x00000052, 0x00000025, - 0x00040001, 0x00020001, 0x00000044, 0x00000035, 0x00040001, - 0x00020001, 0x00000053, 0x00000054, 0x00020001, 0x00000045, - 0x00000055, -}; - -static unsigned long g_huffman_table_8[71] = { - 0x00060001, 0x00020001, 0x00000000, 0x00020001, 0x00000010, - 0x00000001, 0x00020001, 0x00000011, 0x00040001, 0x00020001, - 0x00000021, 0x00000012, 0x000e0001, 0x00040001, 0x00020001, - 0x00000020, 0x00000002, 0x00020001, 0x00000022, 0x00040001, - 0x00020001, 0x00000030, 0x00000003, 0x00020001, 0x00000031, - 0x00000013, 0x000e0001, 0x00080001, 0x00040001, 0x00020001, - 0x00000032, 0x00000023, 0x00020001, 0x00000040, 0x00000004, - 0x00020001, 0x00000041, 0x00020001, 0x00000014, 0x00000042, - 0x000c0001, 0x00060001, 0x00020001, 0x00000024, 0x00020001, - 0x00000033, 0x00000050, 0x00040001, 0x00020001, 0x00000043, - 0x00000034, 0x00000051, 0x00060001, 0x00020001, 0x00000015, - 0x00020001, 0x00000005, 0x00000052, 0x00060001, 0x00020001, - 0x00000025, 0x00020001, 0x00000044, 0x00000035, 0x00020001, - 0x00000053, 0x00020001, 0x00000045, 0x00020001, 0x00000054, - 0x00000055, -}; - -static unsigned long g_huffman_table_9[71] = { - 0x00080001, 0x00040001, 0x00020001, 0x00000000, 0x00000010, - 0x00020001, 0x00000001, 0x00000011, 0x000a0001, 0x00040001, - 0x00020001, 0x00000020, 0x00000021, 0x00020001, 0x00000012, - 0x00020001, 0x00000002, 0x00000022, 0x000c0001, 0x00060001, - 0x00040001, 0x00020001, 0x00000030, 0x00000003, 0x00000031, - 0x00020001, 0x00000013, 0x00020001, 0x00000032, 0x00000023, - 0x000c0001, 0x00040001, 0x00020001, 0x00000041, 0x00000014, - 0x00040001, 0x00020001, 0x00000040, 0x00000033, 0x00020001, - 0x00000042, 0x00000024, 0x000a0001, 0x00060001, 0x00040001, - 0x00020001, 0x00000004, 0x00000050, 0x00000043, 0x00020001, - 0x00000034, 0x00000051, 0x00080001, 0x00040001, 0x00020001, - 0x00000015, 0x00000052, 0x00020001, 0x00000025, 0x00000044, - 0x00060001, 0x00040001, 0x00020001, 0x00000005, 0x00000054, - 0x00000053, 0x00020001, 0x00000035, 0x00020001, 0x00000045, - 0x00000055, -}; - -static unsigned long g_huffman_table_10[127] = { - 0x00020001, 0x00000000, 0x00040001, 0x00020001, 0x00000010, - 0x00000001, 0x000a0001, 0x00020001, 0x00000011, 0x00040001, - 0x00020001, 0x00000020, 0x00000002, 0x00020001, 0x00000021, - 0x00000012, 0x001c0001, 0x00080001, 0x00040001, 0x00020001, - 0x00000022, 0x00000030, 0x00020001, 0x00000031, 0x00000013, - 0x00080001, 0x00040001, 0x00020001, 0x00000003, 0x00000032, - 0x00020001, 0x00000023, 0x00000040, 0x00040001, 0x00020001, - 0x00000041, 0x00000014, 0x00040001, 0x00020001, 0x00000004, - 0x00000033, 0x00020001, 0x00000042, 0x00000024, 0x001c0001, - 0x000a0001, 0x00060001, 0x00040001, 0x00020001, 0x00000050, - 0x00000005, 0x00000060, 0x00020001, 0x00000061, 0x00000016, - 0x000c0001, 0x00060001, 0x00040001, 0x00020001, 0x00000043, - 0x00000034, 0x00000051, 0x00020001, 0x00000015, 0x00020001, - 0x00000052, 0x00000025, 0x00040001, 0x00020001, 0x00000026, - 0x00000036, 0x00000071, 0x00140001, 0x00080001, 0x00020001, - 0x00000017, 0x00040001, 0x00020001, 0x00000044, 0x00000053, - 0x00000006, 0x00060001, 0x00040001, 0x00020001, 0x00000035, - 0x00000045, 0x00000062, 0x00020001, 0x00000070, 0x00020001, - 0x00000007, 0x00000064, 0x000e0001, 0x00040001, 0x00020001, - 0x00000072, 0x00000027, 0x00060001, 0x00020001, 0x00000063, - 0x00020001, 0x00000054, 0x00000055, 0x00020001, 0x00000046, - 0x00000073, 0x00080001, 0x00040001, 0x00020001, 0x00000037, - 0x00000065, 0x00020001, 0x00000056, 0x00000074, 0x00060001, - 0x00020001, 0x00000047, 0x00020001, 0x00000066, 0x00000075, - 0x00040001, 0x00020001, 0x00000057, 0x00000076, 0x00020001, - 0x00000067, 0x00000077, -}; - -static unsigned long g_huffman_table_11[127] = { - 0x00060001, 0x00020001, 0x00000000, 0x00020001, 0x00000010, - 0x00000001, 0x00080001, 0x00020001, 0x00000011, 0x00040001, - 0x00020001, 0x00000020, 0x00000002, 0x00000012, 0x00180001, - 0x00080001, 0x00020001, 0x00000021, 0x00020001, 0x00000022, - 0x00020001, 0x00000030, 0x00000003, 0x00040001, 0x00020001, - 0x00000031, 0x00000013, 0x00040001, 0x00020001, 0x00000032, - 0x00000023, 0x00040001, 0x00020001, 0x00000040, 0x00000004, - 0x00020001, 0x00000041, 0x00000014, 0x001e0001, 0x00100001, - 0x000a0001, 0x00040001, 0x00020001, 0x00000042, 0x00000024, - 0x00040001, 0x00020001, 0x00000033, 0x00000043, 0x00000050, - 0x00040001, 0x00020001, 0x00000034, 0x00000051, 0x00000061, - 0x00060001, 0x00020001, 0x00000016, 0x00020001, 0x00000006, - 0x00000026, 0x00020001, 0x00000062, 0x00020001, 0x00000015, - 0x00020001, 0x00000005, 0x00000052, 0x00100001, 0x000a0001, - 0x00060001, 0x00040001, 0x00020001, 0x00000025, 0x00000044, - 0x00000060, 0x00020001, 0x00000063, 0x00000036, 0x00040001, - 0x00020001, 0x00000070, 0x00000017, 0x00000071, 0x00100001, - 0x00060001, 0x00040001, 0x00020001, 0x00000007, 0x00000064, - 0x00000072, 0x00020001, 0x00000027, 0x00040001, 0x00020001, - 0x00000053, 0x00000035, 0x00020001, 0x00000054, 0x00000045, - 0x000a0001, 0x00040001, 0x00020001, 0x00000046, 0x00000073, - 0x00020001, 0x00000037, 0x00020001, 0x00000065, 0x00000056, - 0x000a0001, 0x00060001, 0x00040001, 0x00020001, 0x00000055, - 0x00000057, 0x00000074, 0x00020001, 0x00000047, 0x00000066, - 0x00040001, 0x00020001, 0x00000075, 0x00000076, 0x00020001, - 0x00000067, 0x00000077, -}; - -static unsigned long g_huffman_table_12[127] = { - 0x000c0001, 0x00040001, 0x00020001, 0x00000010, 0x00000001, - 0x00020001, 0x00000011, 0x00020001, 0x00000000, 0x00020001, - 0x00000020, 0x00000002, 0x00100001, 0x00040001, 0x00020001, - 0x00000021, 0x00000012, 0x00040001, 0x00020001, 0x00000022, - 0x00000031, 0x00020001, 0x00000013, 0x00020001, 0x00000030, - 0x00020001, 0x00000003, 0x00000040, 0x001a0001, 0x00080001, - 0x00040001, 0x00020001, 0x00000032, 0x00000023, 0x00020001, - 0x00000041, 0x00000033, 0x000a0001, 0x00040001, 0x00020001, - 0x00000014, 0x00000042, 0x00020001, 0x00000024, 0x00020001, - 0x00000004, 0x00000050, 0x00040001, 0x00020001, 0x00000043, - 0x00000034, 0x00020001, 0x00000051, 0x00000015, 0x001c0001, - 0x000e0001, 0x00080001, 0x00040001, 0x00020001, 0x00000052, - 0x00000025, 0x00020001, 0x00000053, 0x00000035, 0x00040001, - 0x00020001, 0x00000060, 0x00000016, 0x00000061, 0x00040001, - 0x00020001, 0x00000062, 0x00000026, 0x00060001, 0x00040001, - 0x00020001, 0x00000005, 0x00000006, 0x00000044, 0x00020001, - 0x00000054, 0x00000045, 0x00120001, 0x000a0001, 0x00040001, - 0x00020001, 0x00000063, 0x00000036, 0x00040001, 0x00020001, - 0x00000070, 0x00000007, 0x00000071, 0x00040001, 0x00020001, - 0x00000017, 0x00000064, 0x00020001, 0x00000046, 0x00000072, - 0x000a0001, 0x00060001, 0x00020001, 0x00000027, 0x00020001, - 0x00000055, 0x00000073, 0x00020001, 0x00000037, 0x00000056, - 0x00080001, 0x00040001, 0x00020001, 0x00000065, 0x00000074, - 0x00020001, 0x00000047, 0x00000066, 0x00040001, 0x00020001, - 0x00000075, 0x00000057, 0x00020001, 0x00000076, 0x00020001, - 0x00000067, 0x00000077, -}; - -static unsigned long g_huffman_table_13[511] = { - 0x00020001, 0x00000000, 0x00060001, 0x00020001, 0x00000010, - 0x00020001, 0x00000001, 0x00000011, 0x001c0001, 0x00080001, - 0x00040001, 0x00020001, 0x00000020, 0x00000002, 0x00020001, - 0x00000021, 0x00000012, 0x00080001, 0x00040001, 0x00020001, - 0x00000022, 0x00000030, 0x00020001, 0x00000003, 0x00000031, - 0x00060001, 0x00020001, 0x00000013, 0x00020001, 0x00000032, - 0x00000023, 0x00040001, 0x00020001, 0x00000040, 0x00000004, - 0x00000041, 0x00460001, 0x001c0001, 0x000e0001, 0x00060001, - 0x00020001, 0x00000014, 0x00020001, 0x00000033, 0x00000042, - 0x00040001, 0x00020001, 0x00000024, 0x00000050, 0x00020001, - 0x00000043, 0x00000034, 0x00040001, 0x00020001, 0x00000051, - 0x00000015, 0x00040001, 0x00020001, 0x00000005, 0x00000052, - 0x00020001, 0x00000025, 0x00020001, 0x00000044, 0x00000053, - 0x000e0001, 0x00080001, 0x00040001, 0x00020001, 0x00000060, - 0x00000006, 0x00020001, 0x00000061, 0x00000016, 0x00040001, - 0x00020001, 0x00000080, 0x00000008, 0x00000081, 0x00100001, - 0x00080001, 0x00040001, 0x00020001, 0x00000035, 0x00000062, - 0x00020001, 0x00000026, 0x00000054, 0x00040001, 0x00020001, - 0x00000045, 0x00000063, 0x00020001, 0x00000036, 0x00000070, - 0x00060001, 0x00040001, 0x00020001, 0x00000007, 0x00000055, - 0x00000071, 0x00020001, 0x00000017, 0x00020001, 0x00000027, - 0x00000037, 0x00480001, 0x00180001, 0x000c0001, 0x00040001, - 0x00020001, 0x00000018, 0x00000082, 0x00020001, 0x00000028, - 0x00040001, 0x00020001, 0x00000064, 0x00000046, 0x00000072, - 0x00080001, 0x00040001, 0x00020001, 0x00000084, 0x00000048, - 0x00020001, 0x00000090, 0x00000009, 0x00020001, 0x00000091, - 0x00000019, 0x00180001, 0x000e0001, 0x00080001, 0x00040001, - 0x00020001, 0x00000073, 0x00000065, 0x00020001, 0x00000056, - 0x00000074, 0x00040001, 0x00020001, 0x00000047, 0x00000066, - 0x00000083, 0x00060001, 0x00020001, 0x00000038, 0x00020001, - 0x00000075, 0x00000057, 0x00020001, 0x00000092, 0x00000029, - 0x000e0001, 0x00080001, 0x00040001, 0x00020001, 0x00000067, - 0x00000085, 0x00020001, 0x00000058, 0x00000039, 0x00020001, - 0x00000093, 0x00020001, 0x00000049, 0x00000086, 0x00060001, - 0x00020001, 0x000000a0, 0x00020001, 0x00000068, 0x0000000a, - 0x00020001, 0x000000a1, 0x0000001a, 0x00440001, 0x00180001, - 0x000c0001, 0x00040001, 0x00020001, 0x000000a2, 0x0000002a, - 0x00040001, 0x00020001, 0x00000095, 0x00000059, 0x00020001, - 0x000000a3, 0x0000003a, 0x00080001, 0x00040001, 0x00020001, - 0x0000004a, 0x00000096, 0x00020001, 0x000000b0, 0x0000000b, - 0x00020001, 0x000000b1, 0x0000001b, 0x00140001, 0x00080001, - 0x00020001, 0x000000b2, 0x00040001, 0x00020001, 0x00000076, - 0x00000077, 0x00000094, 0x00060001, 0x00040001, 0x00020001, - 0x00000087, 0x00000078, 0x000000a4, 0x00040001, 0x00020001, - 0x00000069, 0x000000a5, 0x0000002b, 0x000c0001, 0x00060001, - 0x00040001, 0x00020001, 0x0000005a, 0x00000088, 0x000000b3, - 0x00020001, 0x0000003b, 0x00020001, 0x00000079, 0x000000a6, - 0x00060001, 0x00040001, 0x00020001, 0x0000006a, 0x000000b4, - 0x000000c0, 0x00040001, 0x00020001, 0x0000000c, 0x00000098, - 0x000000c1, 0x003c0001, 0x00160001, 0x000a0001, 0x00060001, - 0x00020001, 0x0000001c, 0x00020001, 0x00000089, 0x000000b5, - 0x00020001, 0x0000005b, 0x000000c2, 0x00040001, 0x00020001, - 0x0000002c, 0x0000003c, 0x00040001, 0x00020001, 0x000000b6, - 0x0000006b, 0x00020001, 0x000000c4, 0x0000004c, 0x00100001, - 0x00080001, 0x00040001, 0x00020001, 0x000000a8, 0x0000008a, - 0x00020001, 0x000000d0, 0x0000000d, 0x00020001, 0x000000d1, - 0x00020001, 0x0000004b, 0x00020001, 0x00000097, 0x000000a7, - 0x000c0001, 0x00060001, 0x00020001, 0x000000c3, 0x00020001, - 0x0000007a, 0x00000099, 0x00040001, 0x00020001, 0x000000c5, - 0x0000005c, 0x000000b7, 0x00040001, 0x00020001, 0x0000001d, - 0x000000d2, 0x00020001, 0x0000002d, 0x00020001, 0x0000007b, - 0x000000d3, 0x00340001, 0x001c0001, 0x000c0001, 0x00040001, - 0x00020001, 0x0000003d, 0x000000c6, 0x00040001, 0x00020001, - 0x0000006c, 0x000000a9, 0x00020001, 0x0000009a, 0x000000d4, - 0x00080001, 0x00040001, 0x00020001, 0x000000b8, 0x0000008b, - 0x00020001, 0x0000004d, 0x000000c7, 0x00040001, 0x00020001, - 0x0000007c, 0x000000d5, 0x00020001, 0x0000005d, 0x000000e0, - 0x000a0001, 0x00040001, 0x00020001, 0x000000e1, 0x0000001e, - 0x00040001, 0x00020001, 0x0000000e, 0x0000002e, 0x000000e2, - 0x00080001, 0x00040001, 0x00020001, 0x000000e3, 0x0000006d, - 0x00020001, 0x0000008c, 0x000000e4, 0x00040001, 0x00020001, - 0x000000e5, 0x000000ba, 0x000000f0, 0x00260001, 0x00100001, - 0x00040001, 0x00020001, 0x000000f1, 0x0000001f, 0x00060001, - 0x00040001, 0x00020001, 0x000000aa, 0x0000009b, 0x000000b9, - 0x00020001, 0x0000003e, 0x00020001, 0x000000d6, 0x000000c8, - 0x000c0001, 0x00060001, 0x00020001, 0x0000004e, 0x00020001, - 0x000000d7, 0x0000007d, 0x00020001, 0x000000ab, 0x00020001, - 0x0000005e, 0x000000c9, 0x00060001, 0x00020001, 0x0000000f, - 0x00020001, 0x0000009c, 0x0000006e, 0x00020001, 0x000000f2, - 0x0000002f, 0x00200001, 0x00100001, 0x00060001, 0x00040001, - 0x00020001, 0x000000d8, 0x0000008d, 0x0000003f, 0x00060001, - 0x00020001, 0x000000f3, 0x00020001, 0x000000e6, 0x000000ca, - 0x00020001, 0x000000f4, 0x0000004f, 0x00080001, 0x00040001, - 0x00020001, 0x000000bb, 0x000000ac, 0x00020001, 0x000000e7, - 0x000000f5, 0x00040001, 0x00020001, 0x000000d9, 0x0000009d, - 0x00020001, 0x0000005f, 0x000000e8, 0x001e0001, 0x000c0001, - 0x00060001, 0x00020001, 0x0000006f, 0x00020001, 0x000000f6, - 0x000000cb, 0x00040001, 0x00020001, 0x000000bc, 0x000000ad, - 0x000000da, 0x00080001, 0x00020001, 0x000000f7, 0x00040001, - 0x00020001, 0x0000007e, 0x0000007f, 0x0000008e, 0x00060001, - 0x00040001, 0x00020001, 0x0000009e, 0x000000ae, 0x000000cc, - 0x00020001, 0x000000f8, 0x0000008f, 0x00120001, 0x00080001, - 0x00040001, 0x00020001, 0x000000db, 0x000000bd, 0x00020001, - 0x000000ea, 0x000000f9, 0x00040001, 0x00020001, 0x0000009f, - 0x000000eb, 0x00020001, 0x000000be, 0x00020001, 0x000000cd, - 0x000000fa, 0x000e0001, 0x00040001, 0x00020001, 0x000000dd, - 0x000000ec, 0x00060001, 0x00040001, 0x00020001, 0x000000e9, - 0x000000af, 0x000000dc, 0x00020001, 0x000000ce, 0x000000fb, - 0x00080001, 0x00040001, 0x00020001, 0x000000bf, 0x000000de, - 0x00020001, 0x000000cf, 0x000000ee, 0x00040001, 0x00020001, - 0x000000df, 0x000000ef, 0x00020001, 0x000000ff, 0x00020001, - 0x000000ed, 0x00020001, 0x000000fd, 0x00020001, 0x000000fc, - 0x000000fe, -}; - -static unsigned long g_huffman_table_15[511] = { - 0x00100001, 0x00060001, 0x00020001, 0x00000000, 0x00020001, - 0x00000010, 0x00000001, 0x00020001, 0x00000011, 0x00040001, - 0x00020001, 0x00000020, 0x00000002, 0x00020001, 0x00000021, - 0x00000012, 0x00320001, 0x00100001, 0x00060001, 0x00020001, - 0x00000022, 0x00020001, 0x00000030, 0x00000031, 0x00060001, - 0x00020001, 0x00000013, 0x00020001, 0x00000003, 0x00000040, - 0x00020001, 0x00000032, 0x00000023, 0x000e0001, 0x00060001, - 0x00040001, 0x00020001, 0x00000004, 0x00000014, 0x00000041, - 0x00040001, 0x00020001, 0x00000033, 0x00000042, 0x00020001, - 0x00000024, 0x00000043, 0x000a0001, 0x00060001, 0x00020001, - 0x00000034, 0x00020001, 0x00000050, 0x00000005, 0x00020001, - 0x00000051, 0x00000015, 0x00040001, 0x00020001, 0x00000052, - 0x00000025, 0x00040001, 0x00020001, 0x00000044, 0x00000053, - 0x00000061, 0x005a0001, 0x00240001, 0x00120001, 0x000a0001, - 0x00060001, 0x00020001, 0x00000035, 0x00020001, 0x00000060, - 0x00000006, 0x00020001, 0x00000016, 0x00000062, 0x00040001, - 0x00020001, 0x00000026, 0x00000054, 0x00020001, 0x00000045, - 0x00000063, 0x000a0001, 0x00060001, 0x00020001, 0x00000036, - 0x00020001, 0x00000070, 0x00000007, 0x00020001, 0x00000071, - 0x00000055, 0x00040001, 0x00020001, 0x00000017, 0x00000064, - 0x00020001, 0x00000072, 0x00000027, 0x00180001, 0x00100001, - 0x00080001, 0x00040001, 0x00020001, 0x00000046, 0x00000073, - 0x00020001, 0x00000037, 0x00000065, 0x00040001, 0x00020001, - 0x00000056, 0x00000080, 0x00020001, 0x00000008, 0x00000074, - 0x00040001, 0x00020001, 0x00000081, 0x00000018, 0x00020001, - 0x00000082, 0x00000028, 0x00100001, 0x00080001, 0x00040001, - 0x00020001, 0x00000047, 0x00000066, 0x00020001, 0x00000083, - 0x00000038, 0x00040001, 0x00020001, 0x00000075, 0x00000057, - 0x00020001, 0x00000084, 0x00000048, 0x00060001, 0x00040001, - 0x00020001, 0x00000090, 0x00000019, 0x00000091, 0x00040001, - 0x00020001, 0x00000092, 0x00000076, 0x00020001, 0x00000067, - 0x00000029, 0x005c0001, 0x00240001, 0x00120001, 0x000a0001, - 0x00040001, 0x00020001, 0x00000085, 0x00000058, 0x00040001, - 0x00020001, 0x00000009, 0x00000077, 0x00000093, 0x00040001, - 0x00020001, 0x00000039, 0x00000094, 0x00020001, 0x00000049, - 0x00000086, 0x000a0001, 0x00060001, 0x00020001, 0x00000068, - 0x00020001, 0x000000a0, 0x0000000a, 0x00020001, 0x000000a1, - 0x0000001a, 0x00040001, 0x00020001, 0x000000a2, 0x0000002a, - 0x00020001, 0x00000095, 0x00000059, 0x001a0001, 0x000e0001, - 0x00060001, 0x00020001, 0x000000a3, 0x00020001, 0x0000003a, - 0x00000087, 0x00040001, 0x00020001, 0x00000078, 0x000000a4, - 0x00020001, 0x0000004a, 0x00000096, 0x00060001, 0x00040001, - 0x00020001, 0x00000069, 0x000000b0, 0x000000b1, 0x00040001, - 0x00020001, 0x0000001b, 0x000000a5, 0x000000b2, 0x000e0001, - 0x00080001, 0x00040001, 0x00020001, 0x0000005a, 0x0000002b, - 0x00020001, 0x00000088, 0x00000097, 0x00020001, 0x000000b3, - 0x00020001, 0x00000079, 0x0000003b, 0x00080001, 0x00040001, - 0x00020001, 0x0000006a, 0x000000b4, 0x00020001, 0x0000004b, - 0x000000c1, 0x00040001, 0x00020001, 0x00000098, 0x00000089, - 0x00020001, 0x0000001c, 0x000000b5, 0x00500001, 0x00220001, - 0x00100001, 0x00060001, 0x00040001, 0x00020001, 0x0000005b, - 0x0000002c, 0x000000c2, 0x00060001, 0x00040001, 0x00020001, - 0x0000000b, 0x000000c0, 0x000000a6, 0x00020001, 0x000000a7, - 0x0000007a, 0x000a0001, 0x00040001, 0x00020001, 0x000000c3, - 0x0000003c, 0x00040001, 0x00020001, 0x0000000c, 0x00000099, - 0x000000b6, 0x00040001, 0x00020001, 0x0000006b, 0x000000c4, - 0x00020001, 0x0000004c, 0x000000a8, 0x00140001, 0x000a0001, - 0x00040001, 0x00020001, 0x0000008a, 0x000000c5, 0x00040001, - 0x00020001, 0x000000d0, 0x0000005c, 0x000000d1, 0x00040001, - 0x00020001, 0x000000b7, 0x0000007b, 0x00020001, 0x0000001d, - 0x00020001, 0x0000000d, 0x0000002d, 0x000c0001, 0x00040001, - 0x00020001, 0x000000d2, 0x000000d3, 0x00040001, 0x00020001, - 0x0000003d, 0x000000c6, 0x00020001, 0x0000006c, 0x000000a9, - 0x00060001, 0x00040001, 0x00020001, 0x0000009a, 0x000000b8, - 0x000000d4, 0x00040001, 0x00020001, 0x0000008b, 0x0000004d, - 0x00020001, 0x000000c7, 0x0000007c, 0x00440001, 0x00220001, - 0x00120001, 0x000a0001, 0x00040001, 0x00020001, 0x000000d5, - 0x0000005d, 0x00040001, 0x00020001, 0x000000e0, 0x0000000e, - 0x000000e1, 0x00040001, 0x00020001, 0x0000001e, 0x000000e2, - 0x00020001, 0x000000aa, 0x0000002e, 0x00080001, 0x00040001, - 0x00020001, 0x000000b9, 0x0000009b, 0x00020001, 0x000000e3, - 0x000000d6, 0x00040001, 0x00020001, 0x0000006d, 0x0000003e, - 0x00020001, 0x000000c8, 0x0000008c, 0x00100001, 0x00080001, - 0x00040001, 0x00020001, 0x000000e4, 0x0000004e, 0x00020001, - 0x000000d7, 0x0000007d, 0x00040001, 0x00020001, 0x000000e5, - 0x000000ba, 0x00020001, 0x000000ab, 0x0000005e, 0x00080001, - 0x00040001, 0x00020001, 0x000000c9, 0x0000009c, 0x00020001, - 0x000000f1, 0x0000001f, 0x00060001, 0x00040001, 0x00020001, - 0x000000f0, 0x0000006e, 0x000000f2, 0x00020001, 0x0000002f, - 0x000000e6, 0x00260001, 0x00120001, 0x00080001, 0x00040001, - 0x00020001, 0x000000d8, 0x000000f3, 0x00020001, 0x0000003f, - 0x000000f4, 0x00060001, 0x00020001, 0x0000004f, 0x00020001, - 0x0000008d, 0x000000d9, 0x00020001, 0x000000bb, 0x000000ca, - 0x00080001, 0x00040001, 0x00020001, 0x000000ac, 0x000000e7, - 0x00020001, 0x0000007e, 0x000000f5, 0x00080001, 0x00040001, - 0x00020001, 0x0000009d, 0x0000005f, 0x00020001, 0x000000e8, - 0x0000008e, 0x00020001, 0x000000f6, 0x000000cb, 0x00220001, - 0x00120001, 0x000a0001, 0x00060001, 0x00040001, 0x00020001, - 0x0000000f, 0x000000ae, 0x0000006f, 0x00020001, 0x000000bc, - 0x000000da, 0x00040001, 0x00020001, 0x000000ad, 0x000000f7, - 0x00020001, 0x0000007f, 0x000000e9, 0x00080001, 0x00040001, - 0x00020001, 0x0000009e, 0x000000cc, 0x00020001, 0x000000f8, - 0x0000008f, 0x00040001, 0x00020001, 0x000000db, 0x000000bd, - 0x00020001, 0x000000ea, 0x000000f9, 0x00100001, 0x00080001, - 0x00040001, 0x00020001, 0x0000009f, 0x000000dc, 0x00020001, - 0x000000cd, 0x000000eb, 0x00040001, 0x00020001, 0x000000be, - 0x000000fa, 0x00020001, 0x000000af, 0x000000dd, 0x000e0001, - 0x00060001, 0x00040001, 0x00020001, 0x000000ec, 0x000000ce, - 0x000000fb, 0x00040001, 0x00020001, 0x000000bf, 0x000000ed, - 0x00020001, 0x000000de, 0x000000fc, 0x00060001, 0x00040001, - 0x00020001, 0x000000cf, 0x000000fd, 0x000000ee, 0x00040001, - 0x00020001, 0x000000df, 0x000000fe, 0x00020001, 0x000000ef, - 0x000000ff, -}; - -static unsigned long g_huffman_table_16[511] = { - 0x00020001, 0x00000000, 0x00060001, 0x00020001, 0x00000010, - 0x00020001, 0x00000001, 0x00000011, 0x002a0001, 0x00080001, - 0x00040001, 0x00020001, 0x00000020, 0x00000002, 0x00020001, - 0x00000021, 0x00000012, 0x000a0001, 0x00060001, 0x00020001, - 0x00000022, 0x00020001, 0x00000030, 0x00000003, 0x00020001, - 0x00000031, 0x00000013, 0x000a0001, 0x00040001, 0x00020001, - 0x00000032, 0x00000023, 0x00040001, 0x00020001, 0x00000040, - 0x00000004, 0x00000041, 0x00060001, 0x00020001, 0x00000014, - 0x00020001, 0x00000033, 0x00000042, 0x00040001, 0x00020001, - 0x00000024, 0x00000050, 0x00020001, 0x00000043, 0x00000034, - 0x008a0001, 0x00280001, 0x00100001, 0x00060001, 0x00040001, - 0x00020001, 0x00000005, 0x00000015, 0x00000051, 0x00040001, - 0x00020001, 0x00000052, 0x00000025, 0x00040001, 0x00020001, - 0x00000044, 0x00000035, 0x00000053, 0x000a0001, 0x00060001, - 0x00040001, 0x00020001, 0x00000060, 0x00000006, 0x00000061, - 0x00020001, 0x00000016, 0x00000062, 0x00080001, 0x00040001, - 0x00020001, 0x00000026, 0x00000054, 0x00020001, 0x00000045, - 0x00000063, 0x00040001, 0x00020001, 0x00000036, 0x00000070, - 0x00000071, 0x00280001, 0x00120001, 0x00080001, 0x00020001, - 0x00000017, 0x00020001, 0x00000007, 0x00020001, 0x00000055, - 0x00000064, 0x00040001, 0x00020001, 0x00000072, 0x00000027, - 0x00040001, 0x00020001, 0x00000046, 0x00000065, 0x00000073, - 0x000a0001, 0x00060001, 0x00020001, 0x00000037, 0x00020001, - 0x00000056, 0x00000008, 0x00020001, 0x00000080, 0x00000081, - 0x00060001, 0x00020001, 0x00000018, 0x00020001, 0x00000074, - 0x00000047, 0x00020001, 0x00000082, 0x00020001, 0x00000028, - 0x00000066, 0x00180001, 0x000e0001, 0x00080001, 0x00040001, - 0x00020001, 0x00000083, 0x00000038, 0x00020001, 0x00000075, - 0x00000084, 0x00040001, 0x00020001, 0x00000048, 0x00000090, - 0x00000091, 0x00060001, 0x00020001, 0x00000019, 0x00020001, - 0x00000009, 0x00000076, 0x00020001, 0x00000092, 0x00000029, - 0x000e0001, 0x00080001, 0x00040001, 0x00020001, 0x00000085, - 0x00000058, 0x00020001, 0x00000093, 0x00000039, 0x00040001, - 0x00020001, 0x000000a0, 0x0000000a, 0x0000001a, 0x00080001, - 0x00020001, 0x000000a2, 0x00020001, 0x00000067, 0x00020001, - 0x00000057, 0x00000049, 0x00060001, 0x00020001, 0x00000094, - 0x00020001, 0x00000077, 0x00000086, 0x00020001, 0x000000a1, - 0x00020001, 0x00000068, 0x00000095, 0x00dc0001, 0x007e0001, - 0x00320001, 0x001a0001, 0x000c0001, 0x00060001, 0x00020001, - 0x0000002a, 0x00020001, 0x00000059, 0x0000003a, 0x00020001, - 0x000000a3, 0x00020001, 0x00000087, 0x00000078, 0x00080001, - 0x00040001, 0x00020001, 0x000000a4, 0x0000004a, 0x00020001, - 0x00000096, 0x00000069, 0x00040001, 0x00020001, 0x000000b0, - 0x0000000b, 0x000000b1, 0x000a0001, 0x00040001, 0x00020001, - 0x0000001b, 0x000000b2, 0x00020001, 0x0000002b, 0x00020001, - 0x000000a5, 0x0000005a, 0x00060001, 0x00020001, 0x000000b3, - 0x00020001, 0x000000a6, 0x0000006a, 0x00040001, 0x00020001, - 0x000000b4, 0x0000004b, 0x00020001, 0x0000000c, 0x000000c1, - 0x001e0001, 0x000e0001, 0x00060001, 0x00040001, 0x00020001, - 0x000000b5, 0x000000c2, 0x0000002c, 0x00040001, 0x00020001, - 0x000000a7, 0x000000c3, 0x00020001, 0x0000006b, 0x000000c4, - 0x00080001, 0x00020001, 0x0000001d, 0x00040001, 0x00020001, - 0x00000088, 0x00000097, 0x0000003b, 0x00040001, 0x00020001, - 0x000000d1, 0x000000d2, 0x00020001, 0x0000002d, 0x000000d3, - 0x00120001, 0x00060001, 0x00040001, 0x00020001, 0x0000001e, - 0x0000002e, 0x000000e2, 0x00060001, 0x00040001, 0x00020001, - 0x00000079, 0x00000098, 0x000000c0, 0x00020001, 0x0000001c, - 0x00020001, 0x00000089, 0x0000005b, 0x000e0001, 0x00060001, - 0x00020001, 0x0000003c, 0x00020001, 0x0000007a, 0x000000b6, - 0x00040001, 0x00020001, 0x0000004c, 0x00000099, 0x00020001, - 0x000000a8, 0x0000008a, 0x00060001, 0x00020001, 0x0000000d, - 0x00020001, 0x000000c5, 0x0000005c, 0x00040001, 0x00020001, - 0x0000003d, 0x000000c6, 0x00020001, 0x0000006c, 0x0000009a, - 0x00580001, 0x00560001, 0x00240001, 0x00100001, 0x00080001, - 0x00040001, 0x00020001, 0x0000008b, 0x0000004d, 0x00020001, - 0x000000c7, 0x0000007c, 0x00040001, 0x00020001, 0x000000d5, - 0x0000005d, 0x00020001, 0x000000e0, 0x0000000e, 0x00080001, - 0x00020001, 0x000000e3, 0x00040001, 0x00020001, 0x000000d0, - 0x000000b7, 0x0000007b, 0x00060001, 0x00040001, 0x00020001, - 0x000000a9, 0x000000b8, 0x000000d4, 0x00020001, 0x000000e1, - 0x00020001, 0x000000aa, 0x000000b9, 0x00180001, 0x000a0001, - 0x00060001, 0x00040001, 0x00020001, 0x0000009b, 0x000000d6, - 0x0000006d, 0x00020001, 0x0000003e, 0x000000c8, 0x00060001, - 0x00040001, 0x00020001, 0x0000008c, 0x000000e4, 0x0000004e, - 0x00040001, 0x00020001, 0x000000d7, 0x000000e5, 0x00020001, - 0x000000ba, 0x000000ab, 0x000c0001, 0x00040001, 0x00020001, - 0x0000009c, 0x000000e6, 0x00040001, 0x00020001, 0x0000006e, - 0x000000d8, 0x00020001, 0x0000008d, 0x000000bb, 0x00080001, - 0x00040001, 0x00020001, 0x000000e7, 0x0000009d, 0x00020001, - 0x000000e8, 0x0000008e, 0x00040001, 0x00020001, 0x000000cb, - 0x000000bc, 0x0000009e, 0x000000f1, 0x00020001, 0x0000001f, - 0x00020001, 0x0000000f, 0x0000002f, 0x00420001, 0x00380001, - 0x00020001, 0x000000f2, 0x00340001, 0x00320001, 0x00140001, - 0x00080001, 0x00020001, 0x000000bd, 0x00020001, 0x0000005e, - 0x00020001, 0x0000007d, 0x000000c9, 0x00060001, 0x00020001, - 0x000000ca, 0x00020001, 0x000000ac, 0x0000007e, 0x00040001, - 0x00020001, 0x000000da, 0x000000ad, 0x000000cc, 0x000a0001, - 0x00060001, 0x00020001, 0x000000ae, 0x00020001, 0x000000db, - 0x000000dc, 0x00020001, 0x000000cd, 0x000000be, 0x00060001, - 0x00040001, 0x00020001, 0x000000eb, 0x000000ed, 0x000000ee, - 0x00060001, 0x00040001, 0x00020001, 0x000000d9, 0x000000ea, - 0x000000e9, 0x00020001, 0x000000de, 0x00040001, 0x00020001, - 0x000000dd, 0x000000ec, 0x000000ce, 0x0000003f, 0x000000f0, - 0x00040001, 0x00020001, 0x000000f3, 0x000000f4, 0x00020001, - 0x0000004f, 0x00020001, 0x000000f5, 0x0000005f, 0x000a0001, - 0x00020001, 0x000000ff, 0x00040001, 0x00020001, 0x000000f6, - 0x0000006f, 0x00020001, 0x000000f7, 0x0000007f, 0x000c0001, - 0x00060001, 0x00020001, 0x0000008f, 0x00020001, 0x000000f8, - 0x000000f9, 0x00040001, 0x00020001, 0x0000009f, 0x000000fa, - 0x000000af, 0x00080001, 0x00040001, 0x00020001, 0x000000fb, - 0x000000bf, 0x00020001, 0x000000fc, 0x000000cf, 0x00040001, - 0x00020001, 0x000000fd, 0x000000df, 0x00020001, 0x000000fe, - 0x000000ef, -}; - -static unsigned long g_huffman_table_24[512] = { - 0x003c0001, 0x00080001, 0x00040001, 0x00020001, 0x00000000, - 0x00000010, 0x00020001, 0x00000001, 0x00000011, 0x000e0001, - 0x00060001, 0x00040001, 0x00020001, 0x00000020, 0x00000002, - 0x00000021, 0x00020001, 0x00000012, 0x00020001, 0x00000022, - 0x00020001, 0x00000030, 0x00000003, 0x000e0001, 0x00040001, - 0x00020001, 0x00000031, 0x00000013, 0x00040001, 0x00020001, - 0x00000032, 0x00000023, 0x00040001, 0x00020001, 0x00000040, - 0x00000004, 0x00000041, 0x00080001, 0x00040001, 0x00020001, - 0x00000014, 0x00000033, 0x00020001, 0x00000042, 0x00000024, - 0x00060001, 0x00040001, 0x00020001, 0x00000043, 0x00000034, - 0x00000051, 0x00060001, 0x00040001, 0x00020001, 0x00000050, - 0x00000005, 0x00000015, 0x00020001, 0x00000052, 0x00000025, - 0x00fa0001, 0x00620001, 0x00220001, 0x00120001, 0x000a0001, - 0x00040001, 0x00020001, 0x00000044, 0x00000053, 0x00020001, - 0x00000035, 0x00020001, 0x00000060, 0x00000006, 0x00040001, - 0x00020001, 0x00000061, 0x00000016, 0x00020001, 0x00000062, - 0x00000026, 0x00080001, 0x00040001, 0x00020001, 0x00000054, - 0x00000045, 0x00020001, 0x00000063, 0x00000036, 0x00040001, - 0x00020001, 0x00000071, 0x00000055, 0x00020001, 0x00000064, - 0x00000046, 0x00200001, 0x000e0001, 0x00060001, 0x00020001, - 0x00000072, 0x00020001, 0x00000027, 0x00000037, 0x00020001, - 0x00000073, 0x00040001, 0x00020001, 0x00000070, 0x00000007, - 0x00000017, 0x000a0001, 0x00040001, 0x00020001, 0x00000065, - 0x00000056, 0x00040001, 0x00020001, 0x00000080, 0x00000008, - 0x00000081, 0x00040001, 0x00020001, 0x00000074, 0x00000047, - 0x00020001, 0x00000018, 0x00000082, 0x00100001, 0x00080001, - 0x00040001, 0x00020001, 0x00000028, 0x00000066, 0x00020001, - 0x00000083, 0x00000038, 0x00040001, 0x00020001, 0x00000075, - 0x00000057, 0x00020001, 0x00000084, 0x00000048, 0x00080001, - 0x00040001, 0x00020001, 0x00000091, 0x00000019, 0x00020001, - 0x00000092, 0x00000076, 0x00040001, 0x00020001, 0x00000067, - 0x00000029, 0x00020001, 0x00000085, 0x00000058, 0x005c0001, - 0x00220001, 0x00100001, 0x00080001, 0x00040001, 0x00020001, - 0x00000093, 0x00000039, 0x00020001, 0x00000094, 0x00000049, - 0x00040001, 0x00020001, 0x00000077, 0x00000086, 0x00020001, - 0x00000068, 0x000000a1, 0x00080001, 0x00040001, 0x00020001, - 0x000000a2, 0x0000002a, 0x00020001, 0x00000095, 0x00000059, - 0x00040001, 0x00020001, 0x000000a3, 0x0000003a, 0x00020001, - 0x00000087, 0x00020001, 0x00000078, 0x0000004a, 0x00160001, - 0x000c0001, 0x00040001, 0x00020001, 0x000000a4, 0x00000096, - 0x00040001, 0x00020001, 0x00000069, 0x000000b1, 0x00020001, - 0x0000001b, 0x000000a5, 0x00060001, 0x00020001, 0x000000b2, - 0x00020001, 0x0000005a, 0x0000002b, 0x00020001, 0x00000088, - 0x000000b3, 0x00100001, 0x000a0001, 0x00060001, 0x00020001, - 0x00000090, 0x00020001, 0x00000009, 0x000000a0, 0x00020001, - 0x00000097, 0x00000079, 0x00040001, 0x00020001, 0x000000a6, - 0x0000006a, 0x000000b4, 0x000c0001, 0x00060001, 0x00020001, - 0x0000001a, 0x00020001, 0x0000000a, 0x000000b0, 0x00020001, - 0x0000003b, 0x00020001, 0x0000000b, 0x000000c0, 0x00040001, - 0x00020001, 0x0000004b, 0x000000c1, 0x00020001, 0x00000098, - 0x00000089, 0x00430001, 0x00220001, 0x00100001, 0x00080001, - 0x00040001, 0x00020001, 0x0000001c, 0x000000b5, 0x00020001, - 0x0000005b, 0x000000c2, 0x00040001, 0x00020001, 0x0000002c, - 0x000000a7, 0x00020001, 0x0000007a, 0x000000c3, 0x000a0001, - 0x00060001, 0x00020001, 0x0000003c, 0x00020001, 0x0000000c, - 0x000000d0, 0x00020001, 0x000000b6, 0x0000006b, 0x00040001, - 0x00020001, 0x000000c4, 0x0000004c, 0x00020001, 0x00000099, - 0x000000a8, 0x00100001, 0x00080001, 0x00040001, 0x00020001, - 0x0000008a, 0x000000c5, 0x00020001, 0x0000005c, 0x000000d1, - 0x00040001, 0x00020001, 0x000000b7, 0x0000007b, 0x00020001, - 0x0000001d, 0x000000d2, 0x00090001, 0x00040001, 0x00020001, - 0x0000002d, 0x000000d3, 0x00020001, 0x0000003d, 0x000000c6, - 0x005500fa, 0x00040001, 0x00020001, 0x0000006c, 0x000000a9, - 0x00020001, 0x0000009a, 0x000000d4, 0x00200001, 0x00100001, - 0x00080001, 0x00040001, 0x00020001, 0x000000b8, 0x0000008b, - 0x00020001, 0x0000004d, 0x000000c7, 0x00040001, 0x00020001, - 0x0000007c, 0x000000d5, 0x00020001, 0x0000005d, 0x000000e1, - 0x00080001, 0x00040001, 0x00020001, 0x0000001e, 0x000000e2, - 0x00020001, 0x000000aa, 0x000000b9, 0x00040001, 0x00020001, - 0x0000009b, 0x000000e3, 0x00020001, 0x000000d6, 0x0000006d, - 0x00140001, 0x000a0001, 0x00060001, 0x00020001, 0x0000003e, - 0x00020001, 0x0000002e, 0x0000004e, 0x00020001, 0x000000c8, - 0x0000008c, 0x00040001, 0x00020001, 0x000000e4, 0x000000d7, - 0x00040001, 0x00020001, 0x0000007d, 0x000000ab, 0x000000e5, - 0x000a0001, 0x00040001, 0x00020001, 0x000000ba, 0x0000005e, - 0x00020001, 0x000000c9, 0x00020001, 0x0000009c, 0x0000006e, - 0x00080001, 0x00020001, 0x000000e6, 0x00020001, 0x0000000d, - 0x00020001, 0x000000e0, 0x0000000e, 0x00040001, 0x00020001, - 0x000000d8, 0x0000008d, 0x00020001, 0x000000bb, 0x000000ca, - 0x004a0001, 0x00020001, 0x000000ff, 0x00400001, 0x003a0001, - 0x00200001, 0x00100001, 0x00080001, 0x00040001, 0x00020001, - 0x000000ac, 0x000000e7, 0x00020001, 0x0000007e, 0x000000d9, - 0x00040001, 0x00020001, 0x0000009d, 0x000000e8, 0x00020001, - 0x0000008e, 0x000000cb, 0x00080001, 0x00040001, 0x00020001, - 0x000000bc, 0x000000da, 0x00020001, 0x000000ad, 0x000000e9, - 0x00040001, 0x00020001, 0x0000009e, 0x000000cc, 0x00020001, - 0x000000db, 0x000000bd, 0x00100001, 0x00080001, 0x00040001, - 0x00020001, 0x000000ea, 0x000000ae, 0x00020001, 0x000000dc, - 0x000000cd, 0x00040001, 0x00020001, 0x000000eb, 0x000000be, - 0x00020001, 0x000000dd, 0x000000ec, 0x00080001, 0x00040001, - 0x00020001, 0x000000ce, 0x000000ed, 0x00020001, 0x000000de, - 0x000000ee, 0x0000000f, 0x00040001, 0x00020001, 0x000000f0, - 0x0000001f, 0x000000f1, 0x00040001, 0x00020001, 0x000000f2, - 0x0000002f, 0x00020001, 0x000000f3, 0x0000003f, 0x00120001, - 0x00080001, 0x00040001, 0x00020001, 0x000000f4, 0x0000004f, - 0x00020001, 0x000000f5, 0x0000005f, 0x00040001, 0x00020001, - 0x000000f6, 0x0000006f, 0x00020001, 0x000000f7, 0x00020001, - 0x0000007f, 0x0000008f, 0x000a0001, 0x00040001, 0x00020001, - 0x000000f8, 0x000000f9, 0x00040001, 0x00020001, 0x0000009f, - 0x000000af, 0x000000fa, 0x00080001, 0x00040001, 0x00020001, - 0x000000fb, 0x000000bf, 0x00020001, 0x000000fc, 0x000000cf, - 0x00040001, 0x00020001, 0x000000fd, 0x000000df, 0x00020001, - 0x000000fe, 0x000000ef, -}; - -static unsigned long g_huffman_table_32[31] = { - 0x00020001, 0x00000000, 0x00080001, 0x00040001, 0x00020001, - 0x00000008, 0x00000004, 0x00020001, 0x00000001, 0x00000002, - 0x00080001, 0x00040001, 0x00020001, 0x0000000c, 0x0000000a, - 0x00020001, 0x00000003, 0x00000006, 0x00060001, 0x00020001, - 0x00000009, 0x00020001, 0x00000005, 0x00000007, 0x00040001, - 0x00020001, 0x0000000e, 0x0000000d, 0x00020001, 0x0000000f, - 0x0000000b, -}; - -static unsigned long g_huffman_table_33[31] = { - 0x00100001, 0x00080001, 0x00040001, 0x00020001, 0x00000000, - 0x00000001, 0x00020001, 0x00000002, 0x00000003, 0x00040001, - 0x00020001, 0x00000004, 0x00000005, 0x00020001, 0x00000006, - 0x00000007, 0x00080001, 0x00040001, 0x00020001, 0x00000008, - 0x00000009, 0x00020001, 0x0000000a, 0x0000000b, 0x00040001, - 0x00020001, 0x0000000c, 0x0000000d, 0x00020001, 0x0000000e, - 0x0000000f, -}; - -typedef struct _HUFFMAN_ -{ - unsigned long * TableData; - unsigned long TreeLen; - unsigned long linbits; -} HuffmanData; - -static HuffmanData g_huffman_main [34] = -{ - { 0 , 0, 0 }, /* Table 0 */ - { g_huffman_table_1 , 7, 0 }, /* Table 1 */ - { g_huffman_table_2 , 17, 0 }, /* Table 2 */ - { g_huffman_table_3 , 17, 0 }, /* Table 3 */ - { 0 , 0, 0 }, /* Table 4 */ - { g_huffman_table_5 , 31, 0 }, /* Table 5 */ - { g_huffman_table_6 , 31, 0 }, /* Table 6 */ - { g_huffman_table_7 , 71, 0 }, /* Table 7 */ - { g_huffman_table_8 , 71, 0 }, /* Table 8 */ - { g_huffman_table_9 , 71, 0 }, /* Table 9 */ - { g_huffman_table_10, 127, 0 }, /* Table 10 */ - { g_huffman_table_11, 127, 0 }, /* Table 11 */ - { g_huffman_table_12, 127, 0 }, /* Table 12 */ - { g_huffman_table_13, 511, 0 }, /* Table 13 */ - { 0 , 0, 0 }, /* Table 14 */ - { g_huffman_table_15, 511, 0 }, /* Table 15 */ - { g_huffman_table_16, 511, 1 }, /* Table 16 */ - { g_huffman_table_16, 511, 2 }, /* Table 17 */ - { g_huffman_table_16, 511, 3 }, /* Table 18 */ - { g_huffman_table_16, 511, 4 }, /* Table 19 */ - { g_huffman_table_16, 511, 6 }, /* Table 20 */ - { g_huffman_table_16, 511, 8 }, /* Table 21 */ - { g_huffman_table_16, 511, 10 }, /* Table 22 */ - { g_huffman_table_16, 511, 13 }, /* Table 23 */ - { g_huffman_table_24, 512, 4 }, /* Table 24 */ - { g_huffman_table_24, 512, 5 }, /* Table 25 */ - { g_huffman_table_24, 512, 6 }, /* Table 26 */ - { g_huffman_table_24, 512, 7 }, /* Table 27 */ - { g_huffman_table_24, 512, 8 }, /* Table 28 */ - { g_huffman_table_24, 512, 9 }, /* Table 29 */ - { g_huffman_table_24, 512, 11 }, /* Table 30 */ - { g_huffman_table_24, 512, 13 }, /* Table 31 */ - { g_huffman_table_32, 31, 0 }, /* Table 32 */ - { g_huffman_table_33, 31, 0 }, /* Table 33 */ -}; - -const CLayer3Decoder::SBI CLayer3Decoder::sfBandIndex[3][3] = -{ - { // MPEG1 - { // 44.4 Khz - {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576}, - {0, 4, 8, 12, 16, 22, 30, 40, 52, 66, 84, 106, 136, 192 } - }, - { // 48 Khz - {0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576 }, - {0, 4, 8, 12, 16, 22, 28, 38, 50, 64, 80, 100, 126, 192 } - }, - { // 32Khz - {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576 }, - {0, 4, 8, 12, 16, 22, 30, 42, 58, 78, 104, 138, 180, 192} - } - }, - { // MPEG2 - { // 22.5khz - {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, - {0, 4, 8, 12, 18, 24, 32, 42, 56, 74, 100, 132, 174, 192} - }, - { // 24Khz - {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 332, 394, 464, 540, 576}, - {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 136, 180, 192} - }, - { // 16khz - {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, - {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192} - }, - }, - { // MPEG2.5 - { // 11.25Khz - {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, - {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192}, - }, - { // 12Khz - {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, - {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192}, - }, - { // 8Khz - {0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, 232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576}, - {0, 8, 16, 24, 36, 52, 72, 96, 124, 160, 162, 164, 166, 192}, - }, - } -}; - -const float CLayer3Decoder::ShortTwiddles[] = -{ - 0.866025403f, 0.500000000f, 1.931851653f, - 0.707106781f, 0.517638090f, 0.504314480f, - 0.541196100f, 0.630236207f, 0.821339815f, - 1.306562965f, 3.830648788f, 0.793353340f, - 0.608761429f, 0.923879532f, 0.382683432f, - 0.991444861f, 0.130526192f, 0.382683432f, - 0.608761429f, 0.793353340f, 0.923879532f, - 0.991444861f, 0.130526192f -}; - -const float CLayer3Decoder::NormalTwiddles[] = -{ - 5.736856623f, 1.931851653f, 1.183100792f, - 0.871723397f, 0.707106781f, 0.610387294f, - 0.551688959f, 0.517638090f, 0.501909918f, - -0.500476342f, -0.504314480f, -0.512139757f, - -0.524264562f, -0.541196100f, -0.563690973f, - -0.592844523f, -0.630236207f, -0.678170852f, - -0.740093616f, -0.821339815f, -0.930579498f, - -1.082840285f, -1.306562965f, -1.662754762f, - -2.310113158f, -3.830648788f, -11.46279281f -}; - -CLayer3Decoder::CLayer3Decoder() -{ - int i; - float ci[8] = { -0.6f, -0.535f, -0.33f, -0.185f, -0.095f, -0.041f, -0.0142f, -0.0037f }; - - for (i = 0; i < 8; i++) - { - Cs[i] = 1.0f / (float)sqrt(1.0 + ci[i]*ci[i]); - Ca[i] = ci[i] / (float)sqrt(1.0 + ci[i]*ci[i]); - } - - for(i = 0; i < 64; i++) - { - PowerTableMinus2[i] = (float)pow(2.0, -2.0 * i); - PowerTableMinus05[i] = (float)pow(2.0, -0.5 * i); - } - - for(i = 0; i < 256; i++) - GainTable[i] = (float)pow(2.0 , (0.25 * (i - 210.0))); - - // table for the MPEG1 intensity stereo position - // 7 == INVALID_POS so.... - for(i=0; i<16; i++) - { - TanPi12Table[i] = (float)tan(i * PI/12); - } - - // magic for the IMDCT stuff - int odd_i, two_odd_i, four_odd_i, eight_odd_i; - int j = 0; - for(i = 0; i < 9; i++) - { - odd_i = (i << 1) + 1; - two_odd_i = odd_i << 1; - four_odd_i = odd_i << 2; - IMDCT9x8Table[j++] = (float)cos(PI18 * odd_i); - IMDCT9x8Table[j++] = (float)cos(PI18 * two_odd_i); - - eight_odd_i = two_odd_i << 2; - IMDCT9x8Table[j++] = (float)cos(PI18 * (four_odd_i - odd_i)); - IMDCT9x8Table[j++] = (float)cos(PI18 * four_odd_i); - IMDCT9x8Table[j++] = (float)cos(PI18 * (four_odd_i + odd_i)); - IMDCT9x8Table[j++] = (float)cos(PI18 * (four_odd_i + two_odd_i)); - IMDCT9x8Table[j++] = (float)cos(PI18 * (eight_odd_i - odd_i)); - IMDCT9x8Table[j++] = (float)cos(PI18 * eight_odd_i); - } - - for(int ch=0;ch<2;ch++) - for(int j=0; j<576; j++) - prevblck[ch][j] = 0.0f; - - /* block_type 0 (normal window) */ - for(i = 0; i < 36; i++) - IMDCTwin[0][i] = (float)sin(PI36 * (i + 0.5)); - - /* block_type 1 (start block) */ - for(i = 0; i < 18; i++) - IMDCTwin[1][i] = (float)sin(PI36 * (i + 0.5)); - for(i = 18; i < 24; i++) - IMDCTwin[1][i] = 1.0f; - for(i = 24; i < 30; i++) - IMDCTwin[1][i] = (float)sin(PI12 * (i - 18 + 0.5)); - for(i = 30; i < 36; i++) - IMDCTwin[1][i] = 0.0f; - - /* block_type 2 (short block) */ - for(i = 0; i < 12; i++) - IMDCTwin[2][i] = (float)sin(PI12 * (i + 0.5)); - for(i = 12; i < 36; i++) - IMDCTwin[2][i] = 0.0f; - - /* block_type 3 (stop block) */ - for(i = 0; i < 6; i++) - IMDCTwin[3][i] = 0.0f; - for(i = 6; i < 12; i++) - IMDCTwin[3][i] = (float)sin(PI12 * (i - 6 + 0.5)); - for(i = 12; i < 18; i++) - IMDCTwin[3][i] = 1.0f; - for(i = 18; i < 36; i++) - IMDCTwin[3][i] = (float)sin(PI36 * (i + 0.5)); -} - -CLayer3Decoder::~CLayer3Decoder() -{ - -} - -void CLayer3Decoder::DecodeScalefactors(unsigned long ch, unsigned long gr) -{ - const unsigned int slentab1[16] = {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4}; - const unsigned int slentab2[16] = {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}; - - int sfb, window; - - for(sfb=0; sfb<21; sfb++) - { - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = 0; - } - - for(sfb=0; sfb<13; sfb++) - { - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][0] = 0; - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][1] = 0; - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][2] = 0; - } - - if(m_MpegVer == MPEG1) - { - unsigned int slen1 = slentab1[m_fr->m_SI.grinf[gr][ch].ScalefacCompress]; - unsigned int slen2 = slentab2[m_fr->m_SI.grinf[gr][ch].ScalefacCompress]; - - if( (m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag == 1) && (m_fr->m_SI.grinf[gr][ch].BlockType == 2)) - { - if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) - { - // this is the long block - for (sfb = 0; sfb < 8; sfb++) - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen1); - - for (sfb = 3; sfb < 6; sfb++) - for (window=0; window<3; window++) - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen1); - - for (sfb = 6; sfb < 12; sfb++) - for (window=0; window<3; window++) - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen2); - - for (sfb=12,window=0; window<3; window++) - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[12][window] = 0; - } - else - { // SHORT - // this is a short block... - for(sfb=0; sfb<6; sfb++) - { - for(window=0; window<3; window++) - { - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen1); - } - } - - for(sfb=6; sfb<12; sfb++) - { - for(window=0; window<3; window++) - { - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen2); - } - } - - for(window=0; window<3; window++) - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[12][window] = 0; - } - } - else - { - for(sfb=0; sfb<6; sfb++) - { - if((m_fr->m_SI.ScfSi[ch][0] == 0) || (gr == 0)) - { - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen1); - } - else - m_fr->m_SI.grinf[1][ch].Scalefac_Long[sfb] = m_fr->m_SI.grinf[0][ch].Scalefac_Long[sfb]; - } - - for(sfb=6; sfb<11; sfb++) - { - if((m_fr->m_SI.ScfSi[ch][1] == 0) || (gr == 0)) - { - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen1); - } - else - m_fr->m_SI.grinf[1][ch].Scalefac_Long[sfb] = m_fr->m_SI.grinf[0][ch].Scalefac_Long[sfb]; - } - - for(sfb=11; sfb<16; sfb++) - { - if((m_fr->m_SI.ScfSi[ch][2] == 0) || (gr == 0)) - { - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen2); - } - else - m_fr->m_SI.grinf[1][ch].Scalefac_Long[sfb] = m_fr->m_SI.grinf[0][ch].Scalefac_Long[sfb]; - } - - for(sfb=16; sfb<21; sfb++) - { - if((m_fr->m_SI.ScfSi[ch][3] == 0) || (gr == 0)) - { - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen2); - } - else - m_fr->m_SI.grinf[1][ch].Scalefac_Long[sfb] = m_fr->m_SI.grinf[0][ch].Scalefac_Long[sfb]; - } - - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[21] = 0; - } - } - else // MPEG 2 - { - int nfsbtable [2][3][3][4] = - { - { - { - {6, 5, 5, 5}, - {9, 9, 9, 9}, - {6, 9, 9, 9} - }, - { - {6, 5, 7, 3}, - {9, 9, 12, 6}, - {6, 9, 12, 6} - }, - { - {11, 10, 0, 0}, - {18, 18, 0, 0}, - {15, 18, 0, 0} - }, - }, - { - { - { 7, 7, 7, 0}, - {12, 12, 12, 0}, - { 6, 15, 12, 0} - }, - { - { 6, 6, 6, 3}, - {12, 9, 9, 6}, - { 6, 12, 9, 6} - }, - { - { 8, 8, 5, 0}, - {15, 12, 9, 0}, - { 6, 18, 9, 0} - }, - }, - }; - - unsigned int scalefac_comp; - - unsigned int slen[4] = {0,0,0,0}; - unsigned long index1, index2, index3; - - if( (m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag == 1) && (m_fr->m_SI.grinf[gr][ch].BlockType == 2)) - { - if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) - { - index3 = 2; - } - else - { - index3 = 1; - } - } - else - { - index3 = 0; - } - - scalefac_comp = m_fr->m_SI.grinf[gr][ch].ScalefacCompress; - - if(!( ((m_ModeExt == 1) || (m_ModeExt==3)) && (ch == 1))) - { - index1 = 0; - m_fr->m_SI.grinf[gr][ch].PreFlag = 0; - - - if (scalefac_comp>=500) - { - slen[0] = ((scalefac_comp-500)/ 3)%4; - slen[1] = ((scalefac_comp-500)/ 1)%3; - slen[2] = ((scalefac_comp-500)/ 1)%1; - slen[3] = ((scalefac_comp-500)/ 1)%1; - index2 = 2; - m_fr->m_SI.grinf[gr][ch].PreFlag = 1; - } - else if (scalefac_comp>=400) - { - slen[0] = ((scalefac_comp-400)/20)%5; - slen[1] = ((scalefac_comp-400)/ 4)%5; - slen[2] = ((scalefac_comp-400)/ 1)%4; - slen[3] = ((scalefac_comp-400)/ 1)%1; - index2 = 1; - } - else - { - slen[0] = ((scalefac_comp- 0)/80)%5; - slen[1] = ((scalefac_comp- 0)/16)%5; - slen[2] = ((scalefac_comp- 0)/ 4)%4; - slen[3] = ((scalefac_comp- 0)/ 1)%4; - index2 = 0; - } - } - else - { - index1 = 1; - - m_fr->m_SI.grinf[gr][ch].PreFlag = 0; - scalefac_comp>>=1; - - if (scalefac_comp>=244) - { - slen[0] = ((scalefac_comp-244)/ 3)%4; - slen[1] = ((scalefac_comp-244)/ 1)%3; - slen[2] = ((scalefac_comp-244)/ 1)%1; - slen[3] = ((scalefac_comp-244)/ 1)%1; - index2 = 2; - } - else if (scalefac_comp>=180) - { - slen[0] = ((scalefac_comp-180)/16)%4; - slen[1] = ((scalefac_comp-180)/ 4)%4; - slen[2] = ((scalefac_comp-180)/ 1)%4; - slen[3] = ((scalefac_comp-180)/ 1)%1; - index2 = 1; - } - else - { - slen[0] = ((scalefac_comp- 0)/36)%5; - slen[1] = ((scalefac_comp- 0)/ 6)%6; - slen[2] = ((scalefac_comp- 0)/ 1)%6; - slen[3] = ((scalefac_comp- 0)/ 1)%1; - index2 = 0; - } - } - - if( (m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag == 1) && (m_fr->m_SI.grinf[gr][ch].BlockType == 2)) - { - if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) - { - } - else - { - int sfb = 0; - int window = 0; - - for(int j=0; j<4; j++) - { - for(int i=0; i 0) - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen[j]); - else - m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = 0; - - window++; - if(window > 2) - { - if( (m_MpegVer != MPEG1) && ((m_ModeExt == 1) || (m_ModeExt==3)) ) - m_fr->m_SI.grinf[gr][ch].is_max[sfb] = (1<m_SI.grinf[gr][ch].Scalefac_Short[12][window] = 0; - } - } - else - { - int sfb = 0; - - for(int j=0; j<4; j++) - { - for(int i=0; i 0) - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen[j]); - else - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = 0; - - if( (m_MpegVer != MPEG1) && ((m_ModeExt == 1) || (m_ModeExt==3)) ) - m_fr->m_SI.grinf[gr][ch].is_max[sfb] = (1<m_SI.grinf[gr][ch].Scalefac_Long[21] = 0; - m_fr->m_SI.grinf[gr][ch].Scalefac_Long[22] = 0; - } - } -} - -bool inline CLayer3Decoder::HuffmanDecode(unsigned long TableNum, int * x, int * y, int * v, int * w) -{ - unsigned long point, error, bitsleft, treelen, linbits; - unsigned long *htptr; - - point = 0; - bitsleft = 32; - - /* Check for empty tables */ - if(g_huffman_main[TableNum].TreeLen == 0) - { - *x = *y = *v = *w = 0; - return true; - } - - treelen = g_huffman_main[TableNum].TreeLen; - linbits = g_huffman_main[TableNum].linbits; - htptr = g_huffman_main[TableNum].TableData; - - /* Start reading the Huffman code word, bit by bit */ - error = 1; - do - { - - /* Check if we've matched a code word */ - if((htptr[point] & 0xffff0000) == 0x00000000) - { - error = 0; - *x = (htptr[point] >> 4) & 0xf; - *y = htptr[point] & 0xf; - break; - } - - if(br.GetBits(1)) - { - /* Go right in tree */ - while((htptr[point] & 0xff) >= 250) - { - point += htptr[point] & 0xff; - } - point += htptr[point] & 0xff; - } - else - { - /* Go left in tree */ - while((htptr[point] >> 16) >= 250) - { - point += htptr[point] >> 16; - } - point += htptr[point] >> 16; - } - } while((--bitsleft > 0) && (point < treelen)); - - /* Check for error. */ - if(error) - { - *x = *y = *v = *w = 0; - return false; - } - - /* Process sign encodings for quadruples tables. */ - if(TableNum > 31) - { - *v = (*y >> 3) & 1; - *w = (*y >> 2) & 1; - *x = (*y >> 1) & 1; - *y = *y & 1; - - if(*v > 0) - if(br.GetBits(1)) - *v = -*v; - if(*w > 0) - if(br.GetBits(1)) - *w = -*w; - if(*x > 0) - if(br.GetBits(1)) - *x = -*x; - if(*y > 0) - if(br.GetBits(1)) - *y = -*y; - } - else - { - /* Get linbits */ - if((linbits > 0) && (*x == 15)) - { - *x += br.GetBits(linbits); - } - - /* Get sign bit */ - if(*x > 0) - { - if(br.GetBits(1)) - *x = -*x; - } - - /* Get linbits */ - if((linbits > 0) && (*y == 15)) - { - *y += br.GetBits(linbits); - } - - /* Get sign bit */ - if(*y > 0) - { - if(br.GetBits(1)) - *y = -*y; - } - } - - return true; -} - -bool CLayer3Decoder::ReadHuffman(unsigned long ch, unsigned long gr) -{ - unsigned long index = 0; - unsigned long Part23End; - unsigned long Region1; - unsigned long Region2; - unsigned long TableNumber; - - int x, y, v, w; - - - Part23End = m_Part2Start[ch] + m_fr->m_SI.grinf[gr][ch].Part23Length; - - if(m_MpegVer == MPEG1) - { - m_MixedBandLimit[ch] = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[8]; - } - else - { - m_MixedBandLimit[ch] = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[6]; - } - - if( (m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag) && (m_fr->m_SI.grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) ) - { - Region1 = m_MixedBandLimit[ch]; - Region2 = 576; - } - else - { - Region1 = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[m_fr->m_SI.grinf[gr][ch].Region0Count + 1]; - Region2 = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[m_fr->m_SI.grinf[gr][ch].Region0Count + m_fr->m_SI.grinf[gr][ch].Region1Count + 2]; - } - - if(m_fr->m_SI.grinf[gr][ch].Part23Length == 0) - { - for(index = 0; index < 576; index++) - { - is[ch][index] = 0; - } - - m_NonZero[ch] = 0; - - return true; - } - - while(index < (m_fr->m_SI.grinf[gr][ch].BigValues << 1)) - { - if(index < Region1) - { - TableNumber = m_fr->m_SI.grinf[gr][ch].TableSelect[0]; - } - else if(index < Region2) - { - TableNumber = m_fr->m_SI.grinf[gr][ch].TableSelect[1]; - } - else - { - TableNumber = m_fr->m_SI.grinf[gr][ch].TableSelect[2]; - } - - if(!HuffmanDecode(TableNumber, &x, &y, &v, &w)) - return false; - - is[ch][index++] = x; - is[ch][index++] = y; - } - - unsigned long pos = br.GetPos(); - - TableNumber = m_fr->m_SI.grinf[gr][ch].Count1Table_Select + 32; - while((index < 576) && (pos < Part23End)) - { - if(!HuffmanDecode(TableNumber, &x, &y, &v, &w)) - return false; - - is[ch][index++] = v; - is[ch][index++] = w; - is[ch][index++] = x; - is[ch][index++] = y; - - pos = br.GetPos(); - } - - - pos = br.GetPos(); - if(pos > Part23End) - { - index -= 4; - if(index < 0) - index = 0; - } - - m_NonZero[ch] = index; - - br.SetPos(Part23End); - - for(; index < 576; index++) - { - is[ch][index] = 0; - } - - return true; -} - -void CLayer3Decoder::DequantizeSample(int ch, int gr) -{ - static const int pretab[22] = { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0 }; - - unsigned long index = 0; - int window = 0; - int windowsize = 0; - int sfb = 0; - int startband = 0; - float txr = 0.0f; - float globscale = GainTable[ m_fr->m_SI.grinf[gr][ch].GlobalGain ]; - - - if(m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag && m_fr->m_SI.grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) - { - if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) - { - int endblock = 0; - // Mixed Block - - if(m_MpegVer == MPEG1) - { - endblock = 8; - } - else - { - endblock = 6; - } - - - for(sfb=0; sfbm_SI.grinf[gr][ch].ScalefacScale) * (m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] + (m_fr->m_SI.grinf[gr][ch].PreFlag * pretab[sfb]) )]; - xr[ch][index] = txr * ( pow( (float)fabs(is[ch][index]), (float)1.333333333333333) * ( is[ch][index] > 0 ? 1 : -1 ) ); - - index ++; // should be 36 at the end of all this! - } - } - - startband = 3; - } - - // Short Block - // xr[i] = sign(is[i]) * |is[i]|^(4/3) * 2 ^ 0.25 * (global_gain - 210 - 8 * subblock_gain[window]) * 2 ^ -(scalefac_multiplier * scalefac_s[band][window]) - - // short blocks are arranged as 3 windowsize windows per scalefactorband - // like this: 111 111 111 22 22 22 3 3 3 - // SFB1 SFB2 SFB3 ... etc - - for(sfb=startband; sfb < 13; sfb++) - { - windowsize = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb+1] - sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb]; - - for(window = 0; window<3; window++) - { - for(int i=0; im_SI.grinf[gr][ch].SubblockGain[window]] * PowerTableMinus05[(1 + m_fr->m_SI.grinf[gr][ch].ScalefacScale) * m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window]]; - /* apply the sign(is[i]) * |is[i]| ^ (4/3) formula */ - xr[ch][index] = txr * ( pow( (float)fabs(is[ch][index]), (float)1.333333333333333) * ( is[ch][index] > 0 ? 1 : -1 ) ); - index ++; - if(index >= m_NonZero[ch]) - break; - } - } - } - } - else - { - // Long Block - // xr[i] = sign(is[i]) * | is[i] |^(4/3) * 2 ^ 0.25 * (global_gain - 210) * 2 ^ -(scalefac_multiplier * (scalefac_l[band] + preflag * pretab[band])) - - - for(sfb=0; sfb<22; sfb++) - { - windowsize = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb+1] - sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb]; - - for(int i=0; im_SI.grinf[gr][ch].ScalefacScale) * (m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] + (m_fr->m_SI.grinf[gr][ch].PreFlag * pretab[sfb]) )]; - xr[ch][index] = txr * ( pow( (float)fabs(is[ch][index]), (float)1.333333333333333) * ( is[ch][index] > 0 ? 1 : -1 ) ); - index ++; - if(index >= m_NonZero[ch]) - break; - } - } - } - - for(; index<576; index++) - { - xr[ch][index] = 0.0; - } -} - -void CLayer3Decoder::Reorder(unsigned int ch, unsigned int gr) -{ - if(m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag && m_fr->m_SI.grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) - { - int ScaleFactorBand = 0; - int BandStart; - int BandSize; - int Window, src, dst; - int i; - - if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) - { - for(unsigned long index=0; index < m_MixedBandLimit[ch]; index++) - { - xrr[ch][index] = xr[ch][index]; - } - - - ScaleFactorBand = 4; - } - - /* short block, reorder everything */ - // this is a 3window block, we need to reorder it.... in something like this - // 111 222 333 444 555 666 777 888 999 101010 111111 121212 - // to - // 123123123 456456456 789789789 101112101112101112 - do - { - BandStart = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[ScaleFactorBand]; - BandSize = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[ScaleFactorBand+1] - BandStart; - - src = BandStart * 3; - - for(Window = 0; Window < 3; Window++) - { - dst = (BandStart * 3) + Window; - - for(i = 0; i < BandSize; i++) - { - xrr[ch][dst] = xr[ch][src]; - src++; - dst += 3; - } - } - - ScaleFactorBand++; - - } while(ScaleFactorBand < 13); - } - else - { - // long blocks - for(int index=0; index < 576; index++) - { - xrr[ch][index] = xr[ch][index]; - } - } -} - -void CLayer3Decoder::CalculateK(int index, int is_pos, int intensity_scale) -{ - // MPEG2/2.5 - - if(is_pos == 0) - { - kl[index] = 1.0; - kr[index] = 1.0; - } - else - { - - if(intensity_scale == 1) - { - if(is_pos%2==1) - { - kl[index] = (float)pow(2.0f, -( (is_pos+1) / (4)) ); - kr[index] = 1.0; - } - else - { - kl[index] = 1.0; - kr[index] = (float)pow(2.0f, -(is_pos/(4)) ); - } - } - else - { - if(is_pos%2==1) - { - kl[index] = (float)pow(2.0f, -( (is_pos+1) / (8)) ); - kr[index] = 1.0; - } - else - { - kl[index] = 1.0; - kr[index] = (float)pow(2.0f, -(is_pos/(8)) ); - } - } - } -} - -void CLayer3Decoder::Stereo(unsigned int gr) -{ - int index; - - if(m_Channels == 2) // dont bother with stereo processing unless its STEREO duuh - { - int MS_Stereo = (m_Mode == MODE_JOINT_STEREO) && (m_ModeExt & MODE_EXT_MS); - int I_Stereo = (m_Mode == MODE_JOINT_STEREO) && (m_ModeExt & MODE_EXT_IS); - - int is_pos; // intensity stereo positions - int is_valid[576]; - float is_ratio[576]; // intensity stereo left / right ratio - - int intensity_scale = (m_fr->m_SI.grinf[gr][0].ScalefacCompress % 2); - - if(I_Stereo) - { - for(index=0; index<576; index++) - is_valid[index] = 0; - - - if(m_fr->m_SI.grinf[gr][0].WindowSwitchingFlag && (m_fr->m_SI.grinf[gr][0].BlockType == BLOCKTYPE_3WIN)) - { - if(m_fr->m_SI.grinf[gr][0].MixedBlockFlag) - { -// printf("Mixed Block - Arrgh"); - } - else - { - unsigned long startband = 0; - while((unsigned long)(sfBandIndex[m_MpegVer][m_SampleFrequency].Short[startband]*3) < m_NonZero[1]) - startband ++; - - for(int sfb=startband; sfb<13; sfb++) - { - int windowsize = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb+1] - sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb]; - - for(int window=0; window<3; window++) - { - int sfb_start = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb]*3 + windowsize*window; - int sfb_stop = sfb_start + windowsize; - - for(int i = sfb_start; i < sfb_stop; i++) - { - is_pos = m_fr->m_SI.grinf[gr][1].Scalefac_Short[ sfb ][window]; - - if(m_MpegVer == MPEG1) - { - if (is_pos != 7) - { - is_ratio[ i ] = TanPi12Table[ is_pos ]; - is_valid[ i ] = 1; - } - } - else - { - if(is_pos != m_fr->m_SI.grinf[gr][1].is_max[ sfb ] ) - { - CalculateK(i, is_pos, intensity_scale); - is_valid[ i ] = 1; - } - } - } - } - } - } - } - else - { - int i; - - int startband = 0; - - while(sfBandIndex[m_MpegVer][m_SampleFrequency].Long[startband] < m_NonZero[1]) - startband ++; - - for(int sfb = startband; sfb<22; sfb++) - { - i = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb]; - - int windowsize = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb+1] - sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb]; - - for(int w=0; wm_SI.grinf[gr][1].Scalefac_Long[ sfb ]; - - if(m_MpegVer == MPEG1) - { - if (is_pos != 7) - { - is_ratio[ i ] = TanPi12Table[ is_pos ]; - is_valid[ i ] = 1; - } - } - else - { - if(is_pos != m_fr->m_SI.grinf[gr][1].is_max[ sfb ] ) - { - CalculateK(i, is_pos, intensity_scale); - is_valid[ i ] = 1; - } - } - - i++; - } - } - } - - // process the intensity stere stuff - for(index=0; index<576; index++) - { - float temp = xrr[0][index]; - - if( is_valid[index] == 0 ) - { - // NOT intensity Stereo Mode - if(MS_Stereo) - { - // MSStereo mode. we have to undo it... - xrr[0][index] = (temp + xrr[1][index]) / SQRT2; - xrr[1][index] = (temp - xrr[1][index]) / SQRT2; - } - } - else - { - // this IS an intensity stereo value - if(m_MpegVer == MPEG1) - { - xrr[0][index] = temp * (is_ratio[index] / (1.0f + is_ratio[index])); - xrr[1][index] = temp * (1.0f / (1.0f + is_ratio[index])); - } - else - { - xrr[0][index] = temp * kl[index]; - xrr[1][index] = temp * kr[index]; - } - } - } - } - else if(MS_Stereo) - { - // MSStereo mode. we have to undo it... - for(index=0; index<576; index++) - { - float temp = xrr[0][index]; - xrr[0][index] = (temp + xrr[1][index]) / SQRT2; - xrr[1][index] = (temp - xrr[1][index]) / SQRT2; - } - } - } -} - -void CLayer3Decoder::AntiAlias(unsigned int ch, unsigned int gr) -{ - int sb_amount; - - int index =0; - - if(m_fr->m_SI.grinf[gr][ch].BlockType != BLOCKTYPE_3WIN) - { - sb_amount = 558; // antialias the full stectrum - } - else - { - if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) - { // this is a long block, then a short block - sb_amount = m_MixedBandLimit[ch]; - } - else // No antialiasing for short blocks - { - return; - } - } - - int src_idx1; - int src_idx2; - - register float temp; - - for(int sb = 18; sb < sb_amount; sb+=18) - { - for(int i=0; i<8; i++) - { - src_idx1 = sb - 1 - i; - src_idx2 = sb + i; - - temp = xrr[ch][src_idx1]; - - xrr[ch][src_idx1] = (temp * Cs[i]) - (xrr[ch][src_idx2] * Ca[i]); - xrr[ch][src_idx2] = (xrr[ch][src_idx2] * Cs[i]) + (temp * Ca[i]); - } - } - -} - -void CLayer3Decoder::FreqencyInverse(int gr, int ch) -{ - int sb, ss; - - int offset; - - for(sb = 1; sb < 32; sb+=2) - { - offset = 32; - for(ss=1; ss<18; ss+=2) - { - xir[ch][offset+sb] = -xir[ch][offset+sb]; - offset += 64; - } - } -} - -void CLayer3Decoder::IMDCT(float *in, float *out, int block_type) -{ - if(block_type == 2) - { - float tmp[18]; - register float sum; - register float save; - register float pp1; - - int six_i = 6; - int window; - - for(int i=0; i<36; i++) - { - out[i] = 0.0; - } - - for(window=0; window<3; window++) - { - in[15+window] += in[12+window]; - in[12+window] += in[9+window]; - in[9+window] += in[6+window]; - in[6+window] += in[3+window]; - in[3+window] += in[window]; - - in[15+window] += in[9+window]; - in[9+window] += in[3+window]; - - pp1 = in[6+window] * ShortTwiddles[0]; - sum = in[window] + in[12+window] * ShortTwiddles[1]; - - tmp[1] = in[window] - in[12+window]; - tmp[0] = sum + pp1; - tmp[2] = sum - pp1; - - pp1 = in[9+window] * ShortTwiddles[0]; - sum = in[3+window] + in[15+window] * ShortTwiddles[1]; - - tmp[4] = in[3+window] - in[15+window]; - tmp[5] = sum + pp1; - tmp[3] = sum - pp1; - - tmp[3] *= ShortTwiddles[2]; - tmp[4] *= ShortTwiddles[3]; - tmp[5] *= ShortTwiddles[4]; - - save = tmp[0]; - tmp[0] += tmp[5]; - tmp[5] = save - tmp[5]; - - save = tmp[1]; - tmp[1] += tmp[4]; - tmp[4] = save - tmp[4]; - - save = tmp[2]; - tmp[2] += tmp[3]; - tmp[3] = save - tmp[3]; - - tmp[0] *= ShortTwiddles[5]; - tmp[1] *= ShortTwiddles[6]; - tmp[2] *= ShortTwiddles[7]; - tmp[3] *= ShortTwiddles[8]; - tmp[4] *= ShortTwiddles[9]; - tmp[5] *= ShortTwiddles[10]; - - tmp[6] = -tmp[2] * ShortTwiddles[15]; - tmp[7] = -tmp[1] * ShortTwiddles[13]; - tmp[8] = -tmp[0] * ShortTwiddles[11]; - tmp[9] = -tmp[0] * ShortTwiddles[12]; - tmp[10] = -tmp[1] * ShortTwiddles[14]; - tmp[11] = -tmp[2] * ShortTwiddles[16]; - - tmp[0] = tmp[3]; - tmp[1] = tmp[4] * ShortTwiddles[17]; - tmp[2] = tmp[5] * ShortTwiddles[18]; - - tmp[3] = -tmp[5] * ShortTwiddles[19]; - tmp[4] = -tmp[4] * ShortTwiddles[20]; - tmp[5] = -tmp[0] * ShortTwiddles[21]; - - tmp[0] *= ShortTwiddles[22]; - - out[six_i] += tmp[0]; - out[six_i + 1] += tmp[1]; - out[six_i + 2] += tmp[2]; - out[six_i + 3] += tmp[3]; - out[six_i + 4] += tmp[4]; - out[six_i + 5] += tmp[5]; - out[six_i + 6] += tmp[6]; - out[six_i + 7] += tmp[7]; - out[six_i + 8] += tmp[8]; - out[six_i + 9] += tmp[9]; - out[six_i + 10] += tmp[10]; - out[six_i + 11] += tmp[11]; - - six_i += 6; - } - } - else - { - float tmp[18]; - - int i, j; - register float sum; - register float sum2; - register float save; - - in[17] += in[16]; - in[16] += in[15]; - in[15] += in[14]; - in[14] += in[13]; - in[13] += in[12]; - in[12] += in[11]; - in[11] += in[10]; - in[10] += in[9]; - in[9] += in[8]; - in[8] += in[7]; - in[7] += in[6]; - in[6] += in[5]; - in[5] += in[4]; - in[4] += in[3]; - in[3] += in[2]; - in[2] += in[1]; - in[1] += in[0]; - - in[17] += in[15]; - in[15] += in[13]; - in[13] += in[11]; - in[11] += in[9]; - in[9] += in[7]; - in[7] += in[5]; - in[5] += in[3]; - in[3] += in[1]; - - j = 0; - i = 0; - - int b = 9; - do - { - sum = in[0]; - sum2 = in[1]; - - sum += in[2] * IMDCT9x8Table[j]; - sum2 += in[3] * IMDCT9x8Table[j]; - sum += in[4] * IMDCT9x8Table[j+1]; - sum2 += in[5] * IMDCT9x8Table[j+1]; - sum += in[6] * IMDCT9x8Table[j+2]; - sum2 += in[7] * IMDCT9x8Table[j+2]; - sum += in[8] * IMDCT9x8Table[j+3]; - sum2 += in[9] * IMDCT9x8Table[j+3]; - sum += in[10] * IMDCT9x8Table[j+4]; - sum2 += in[11] * IMDCT9x8Table[j+4]; - sum += in[12] * IMDCT9x8Table[j+5]; - sum2 += in[13] * IMDCT9x8Table[j+5]; - sum += in[14] * IMDCT9x8Table[j+6]; - sum2 += in[15] * IMDCT9x8Table[j+6]; - sum += in[16] * IMDCT9x8Table[j+7]; - sum2 += in[17] * IMDCT9x8Table[j+7]; - - tmp[i] = sum; - tmp[17-i] = sum2; - - j += 8; - i++; - - } while(--b); - - tmp[9] *= NormalTwiddles[0]; - tmp[10] *= NormalTwiddles[1]; - tmp[11] *= NormalTwiddles[2]; - tmp[12] *= NormalTwiddles[3]; - tmp[13] *= NormalTwiddles[4]; - tmp[14] *= NormalTwiddles[5]; - tmp[15] *= NormalTwiddles[6]; - tmp[16] *= NormalTwiddles[7]; - tmp[17] *= NormalTwiddles[8]; - - for(i = 0; i < 9; i++) - { - save = tmp[i]; - tmp[i] += tmp[17-i]; - tmp[17-i] = save - tmp[17-i]; - } - - tmp[0] *= NormalTwiddles[9]; - tmp[1] *= NormalTwiddles[10]; - tmp[2] *= NormalTwiddles[11]; - tmp[3] *= NormalTwiddles[12]; - tmp[4] *= NormalTwiddles[13]; - tmp[5] *= NormalTwiddles[14]; - tmp[6] *= NormalTwiddles[15]; - tmp[7] *= NormalTwiddles[16]; - tmp[8] *= NormalTwiddles[17]; - tmp[9] *= NormalTwiddles[18]; - tmp[10] *= NormalTwiddles[19]; - tmp[11] *= NormalTwiddles[20]; - tmp[12] *= NormalTwiddles[21]; - tmp[13] *= NormalTwiddles[22]; - tmp[14] *= NormalTwiddles[23]; - tmp[15] *= NormalTwiddles[24]; - tmp[16] *= NormalTwiddles[25]; - tmp[17] *= NormalTwiddles[26]; - - for(i = 0; i < 9; i++) - { - out[i] = -tmp[i+9] * IMDCTwin[block_type][i]; - out[i+9] = tmp[17-i] * IMDCTwin[block_type][i+9]; - out[i+18] = tmp[8-i] * IMDCTwin[block_type][i+18]; - out[i+27] = tmp[i] * IMDCTwin[block_type][i+27]; - } - } -} - -void CLayer3Decoder::Hybrid(int ch, float *xfrom, float *xto, int blocktype, int windowswitching, int mixedblock) -{ - float rawout[36]; - unsigned int bt; - int sb18 = 0; - int sb = 0; - - int x = 32; - - do - { - bt = (windowswitching && mixedblock && (sb18 < 36)) ? 0 : blocktype; - - IMDCT(&xfrom[sb18], rawout, bt); - - xto[sb] = rawout[ 0] + prevblck[ch][sb18+0]; - xto[sb+32] = rawout[ 1] + prevblck[ch][sb18+1]; - xto[sb+64] = rawout[ 2] + prevblck[ch][sb18+2]; - xto[sb+96] = rawout[ 3] + prevblck[ch][sb18+3]; - xto[sb+128] = rawout[ 4] + prevblck[ch][sb18+4]; - xto[sb+160] = rawout[ 5] + prevblck[ch][sb18+5]; - xto[sb+192] = rawout[ 6] + prevblck[ch][sb18+6]; - xto[sb+224] = rawout[ 7] + prevblck[ch][sb18+7]; - xto[sb+256] = rawout[ 8] + prevblck[ch][sb18+8]; - xto[sb+288] = rawout[ 9] + prevblck[ch][sb18+9]; - xto[sb+320] = rawout[10] + prevblck[ch][sb18+10]; - xto[sb+352] = rawout[11] + prevblck[ch][sb18+11]; - xto[sb+384] = rawout[12] + prevblck[ch][sb18+12]; - xto[sb+416] = rawout[13] + prevblck[ch][sb18+13]; - xto[sb+448] = rawout[14] + prevblck[ch][sb18+14]; - xto[sb+480] = rawout[15] + prevblck[ch][sb18+15]; - xto[sb+512] = rawout[16] + prevblck[ch][sb18+16]; - xto[sb+544] = rawout[17] + prevblck[ch][sb18+17]; - - prevblck[ch][sb18+0] = rawout[18]; - prevblck[ch][sb18+1] = rawout[19]; - prevblck[ch][sb18+2] = rawout[20]; - prevblck[ch][sb18+3] = rawout[21]; - prevblck[ch][sb18+4] = rawout[22]; - prevblck[ch][sb18+5] = rawout[23]; - prevblck[ch][sb18+6] = rawout[24]; - prevblck[ch][sb18+7] = rawout[25]; - prevblck[ch][sb18+8] = rawout[26]; - prevblck[ch][sb18+9] = rawout[27]; - prevblck[ch][sb18+10] = rawout[28]; - prevblck[ch][sb18+11] = rawout[29]; - prevblck[ch][sb18+12] = rawout[30]; - prevblck[ch][sb18+13] = rawout[31]; - prevblck[ch][sb18+14] = rawout[32]; - prevblck[ch][sb18+15] = rawout[33]; - prevblck[ch][sb18+16] = rawout[34]; - prevblck[ch][sb18+17] = rawout[35]; - - sb++; - sb18+=18; - - } while(--x); -} - -bool CLayer3Decoder::ProcessFrame(Frame * fr, float * PCMSamples, unsigned long * NumSamples) -{ - unsigned long ch, gr; - - m_fr = fr; - - if(NumSamples) - *NumSamples = 0; - - m_MpegVer = fr->m_Header.GetMpegVersion(); - m_Mode = fr->m_Header.GetMode(); - m_ModeExt = fr->m_Header.GetModeExtension(); - m_Channels = fr->m_Header.GetChannels(); - m_SampleFrequency = fr->m_Header.GetSampleFrequencyIndex(); - m_Granules = (m_MpegVer) == MPEG1 ? 2 : 1; - - br.FillBitReserve(m_fr->m_Data, m_fr->m_Header.GetDataSize()); - - if(!br.BackStep(m_fr->m_SI.MainDataBegin)) - { - return(false); - } - - if(!PCMSamples) - { - return(true); - } - - for(gr=0; grm_SI.grinf[gr][ch].BlockType, m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag, m_fr->m_SI.grinf[gr][ch].MixedBlockFlag); - FreqencyInverse(gr, ch); - } - - if(m_Channels == 1) - { - for(int ss=0; ss<576; ss+=32) - { - PerformSynthesis(&xir[0][ss], ((float*)PCMSamples)+*NumSamples, 0, 1); - - *NumSamples += 32; - } - } - else - { - for(int ss=0; ss<576; ss+=32) - { - PerformSynthesis(&xir[0][ss], ((float*)PCMSamples)+*NumSamples, 0, 2); - PerformSynthesis(&xir[1][ss], ((float*)PCMSamples)+*NumSamples, 1, 2); - - *NumSamples += 64; - } - } - } - - return(true); -} + +#include +#include +#include "Layer3Decoder.h" + +static unsigned long g_huffman_table_1[7] = { + 0x00020001, 0x00000000, 0x00020001, 0x00000010, 0x00020001, + 0x00000001, 0x00000011, +}; + +static unsigned long g_huffman_table_2[17] = { + 0x00020001, 0x00000000, 0x00040001, 0x00020001, 0x00000010, + 0x00000001, 0x00020001, 0x00000011, 0x00040001, 0x00020001, + 0x00000020, 0x00000021, 0x00020001, 0x00000012, 0x00020001, + 0x00000002, 0x00000022, +}; + +static unsigned long g_huffman_table_3[17] = { + 0x00040001, 0x00020001, 0x00000000, 0x00000001, 0x00020001, + 0x00000011, 0x00020001, 0x00000010, 0x00040001, 0x00020001, + 0x00000020, 0x00000021, 0x00020001, 0x00000012, 0x00020001, + 0x00000002, 0x00000022, +}; + +static unsigned long g_huffman_table_5[31] = { + 0x00020001, 0x00000000, 0x00040001, 0x00020001, 0x00000010, + 0x00000001, 0x00020001, 0x00000011, 0x00080001, 0x00040001, + 0x00020001, 0x00000020, 0x00000002, 0x00020001, 0x00000021, + 0x00000012, 0x00080001, 0x00040001, 0x00020001, 0x00000022, + 0x00000030, 0x00020001, 0x00000003, 0x00000013, 0x00020001, + 0x00000031, 0x00020001, 0x00000032, 0x00020001, 0x00000023, + 0x00000033, +}; + +static unsigned long g_huffman_table_6[31] = { + 0x00060001, 0x00040001, 0x00020001, 0x00000000, 0x00000010, + 0x00000011, 0x00060001, 0x00020001, 0x00000001, 0x00020001, + 0x00000020, 0x00000021, 0x00060001, 0x00020001, 0x00000012, + 0x00020001, 0x00000002, 0x00000022, 0x00040001, 0x00020001, + 0x00000031, 0x00000013, 0x00040001, 0x00020001, 0x00000030, + 0x00000032, 0x00020001, 0x00000023, 0x00020001, 0x00000003, + 0x00000033, +}; + +static unsigned long g_huffman_table_7[71] = { + 0x00020001, 0x00000000, 0x00040001, 0x00020001, 0x00000010, + 0x00000001, 0x00080001, 0x00020001, 0x00000011, 0x00040001, + 0x00020001, 0x00000020, 0x00000002, 0x00000021, 0x00120001, + 0x00060001, 0x00020001, 0x00000012, 0x00020001, 0x00000022, + 0x00000030, 0x00040001, 0x00020001, 0x00000031, 0x00000013, + 0x00040001, 0x00020001, 0x00000003, 0x00000032, 0x00020001, + 0x00000023, 0x00000004, 0x000a0001, 0x00040001, 0x00020001, + 0x00000040, 0x00000041, 0x00020001, 0x00000014, 0x00020001, + 0x00000042, 0x00000024, 0x000c0001, 0x00060001, 0x00040001, + 0x00020001, 0x00000033, 0x00000043, 0x00000050, 0x00040001, + 0x00020001, 0x00000034, 0x00000005, 0x00000051, 0x00060001, + 0x00020001, 0x00000015, 0x00020001, 0x00000052, 0x00000025, + 0x00040001, 0x00020001, 0x00000044, 0x00000035, 0x00040001, + 0x00020001, 0x00000053, 0x00000054, 0x00020001, 0x00000045, + 0x00000055, +}; + +static unsigned long g_huffman_table_8[71] = { + 0x00060001, 0x00020001, 0x00000000, 0x00020001, 0x00000010, + 0x00000001, 0x00020001, 0x00000011, 0x00040001, 0x00020001, + 0x00000021, 0x00000012, 0x000e0001, 0x00040001, 0x00020001, + 0x00000020, 0x00000002, 0x00020001, 0x00000022, 0x00040001, + 0x00020001, 0x00000030, 0x00000003, 0x00020001, 0x00000031, + 0x00000013, 0x000e0001, 0x00080001, 0x00040001, 0x00020001, + 0x00000032, 0x00000023, 0x00020001, 0x00000040, 0x00000004, + 0x00020001, 0x00000041, 0x00020001, 0x00000014, 0x00000042, + 0x000c0001, 0x00060001, 0x00020001, 0x00000024, 0x00020001, + 0x00000033, 0x00000050, 0x00040001, 0x00020001, 0x00000043, + 0x00000034, 0x00000051, 0x00060001, 0x00020001, 0x00000015, + 0x00020001, 0x00000005, 0x00000052, 0x00060001, 0x00020001, + 0x00000025, 0x00020001, 0x00000044, 0x00000035, 0x00020001, + 0x00000053, 0x00020001, 0x00000045, 0x00020001, 0x00000054, + 0x00000055, +}; + +static unsigned long g_huffman_table_9[71] = { + 0x00080001, 0x00040001, 0x00020001, 0x00000000, 0x00000010, + 0x00020001, 0x00000001, 0x00000011, 0x000a0001, 0x00040001, + 0x00020001, 0x00000020, 0x00000021, 0x00020001, 0x00000012, + 0x00020001, 0x00000002, 0x00000022, 0x000c0001, 0x00060001, + 0x00040001, 0x00020001, 0x00000030, 0x00000003, 0x00000031, + 0x00020001, 0x00000013, 0x00020001, 0x00000032, 0x00000023, + 0x000c0001, 0x00040001, 0x00020001, 0x00000041, 0x00000014, + 0x00040001, 0x00020001, 0x00000040, 0x00000033, 0x00020001, + 0x00000042, 0x00000024, 0x000a0001, 0x00060001, 0x00040001, + 0x00020001, 0x00000004, 0x00000050, 0x00000043, 0x00020001, + 0x00000034, 0x00000051, 0x00080001, 0x00040001, 0x00020001, + 0x00000015, 0x00000052, 0x00020001, 0x00000025, 0x00000044, + 0x00060001, 0x00040001, 0x00020001, 0x00000005, 0x00000054, + 0x00000053, 0x00020001, 0x00000035, 0x00020001, 0x00000045, + 0x00000055, +}; + +static unsigned long g_huffman_table_10[127] = { + 0x00020001, 0x00000000, 0x00040001, 0x00020001, 0x00000010, + 0x00000001, 0x000a0001, 0x00020001, 0x00000011, 0x00040001, + 0x00020001, 0x00000020, 0x00000002, 0x00020001, 0x00000021, + 0x00000012, 0x001c0001, 0x00080001, 0x00040001, 0x00020001, + 0x00000022, 0x00000030, 0x00020001, 0x00000031, 0x00000013, + 0x00080001, 0x00040001, 0x00020001, 0x00000003, 0x00000032, + 0x00020001, 0x00000023, 0x00000040, 0x00040001, 0x00020001, + 0x00000041, 0x00000014, 0x00040001, 0x00020001, 0x00000004, + 0x00000033, 0x00020001, 0x00000042, 0x00000024, 0x001c0001, + 0x000a0001, 0x00060001, 0x00040001, 0x00020001, 0x00000050, + 0x00000005, 0x00000060, 0x00020001, 0x00000061, 0x00000016, + 0x000c0001, 0x00060001, 0x00040001, 0x00020001, 0x00000043, + 0x00000034, 0x00000051, 0x00020001, 0x00000015, 0x00020001, + 0x00000052, 0x00000025, 0x00040001, 0x00020001, 0x00000026, + 0x00000036, 0x00000071, 0x00140001, 0x00080001, 0x00020001, + 0x00000017, 0x00040001, 0x00020001, 0x00000044, 0x00000053, + 0x00000006, 0x00060001, 0x00040001, 0x00020001, 0x00000035, + 0x00000045, 0x00000062, 0x00020001, 0x00000070, 0x00020001, + 0x00000007, 0x00000064, 0x000e0001, 0x00040001, 0x00020001, + 0x00000072, 0x00000027, 0x00060001, 0x00020001, 0x00000063, + 0x00020001, 0x00000054, 0x00000055, 0x00020001, 0x00000046, + 0x00000073, 0x00080001, 0x00040001, 0x00020001, 0x00000037, + 0x00000065, 0x00020001, 0x00000056, 0x00000074, 0x00060001, + 0x00020001, 0x00000047, 0x00020001, 0x00000066, 0x00000075, + 0x00040001, 0x00020001, 0x00000057, 0x00000076, 0x00020001, + 0x00000067, 0x00000077, +}; + +static unsigned long g_huffman_table_11[127] = { + 0x00060001, 0x00020001, 0x00000000, 0x00020001, 0x00000010, + 0x00000001, 0x00080001, 0x00020001, 0x00000011, 0x00040001, + 0x00020001, 0x00000020, 0x00000002, 0x00000012, 0x00180001, + 0x00080001, 0x00020001, 0x00000021, 0x00020001, 0x00000022, + 0x00020001, 0x00000030, 0x00000003, 0x00040001, 0x00020001, + 0x00000031, 0x00000013, 0x00040001, 0x00020001, 0x00000032, + 0x00000023, 0x00040001, 0x00020001, 0x00000040, 0x00000004, + 0x00020001, 0x00000041, 0x00000014, 0x001e0001, 0x00100001, + 0x000a0001, 0x00040001, 0x00020001, 0x00000042, 0x00000024, + 0x00040001, 0x00020001, 0x00000033, 0x00000043, 0x00000050, + 0x00040001, 0x00020001, 0x00000034, 0x00000051, 0x00000061, + 0x00060001, 0x00020001, 0x00000016, 0x00020001, 0x00000006, + 0x00000026, 0x00020001, 0x00000062, 0x00020001, 0x00000015, + 0x00020001, 0x00000005, 0x00000052, 0x00100001, 0x000a0001, + 0x00060001, 0x00040001, 0x00020001, 0x00000025, 0x00000044, + 0x00000060, 0x00020001, 0x00000063, 0x00000036, 0x00040001, + 0x00020001, 0x00000070, 0x00000017, 0x00000071, 0x00100001, + 0x00060001, 0x00040001, 0x00020001, 0x00000007, 0x00000064, + 0x00000072, 0x00020001, 0x00000027, 0x00040001, 0x00020001, + 0x00000053, 0x00000035, 0x00020001, 0x00000054, 0x00000045, + 0x000a0001, 0x00040001, 0x00020001, 0x00000046, 0x00000073, + 0x00020001, 0x00000037, 0x00020001, 0x00000065, 0x00000056, + 0x000a0001, 0x00060001, 0x00040001, 0x00020001, 0x00000055, + 0x00000057, 0x00000074, 0x00020001, 0x00000047, 0x00000066, + 0x00040001, 0x00020001, 0x00000075, 0x00000076, 0x00020001, + 0x00000067, 0x00000077, +}; + +static unsigned long g_huffman_table_12[127] = { + 0x000c0001, 0x00040001, 0x00020001, 0x00000010, 0x00000001, + 0x00020001, 0x00000011, 0x00020001, 0x00000000, 0x00020001, + 0x00000020, 0x00000002, 0x00100001, 0x00040001, 0x00020001, + 0x00000021, 0x00000012, 0x00040001, 0x00020001, 0x00000022, + 0x00000031, 0x00020001, 0x00000013, 0x00020001, 0x00000030, + 0x00020001, 0x00000003, 0x00000040, 0x001a0001, 0x00080001, + 0x00040001, 0x00020001, 0x00000032, 0x00000023, 0x00020001, + 0x00000041, 0x00000033, 0x000a0001, 0x00040001, 0x00020001, + 0x00000014, 0x00000042, 0x00020001, 0x00000024, 0x00020001, + 0x00000004, 0x00000050, 0x00040001, 0x00020001, 0x00000043, + 0x00000034, 0x00020001, 0x00000051, 0x00000015, 0x001c0001, + 0x000e0001, 0x00080001, 0x00040001, 0x00020001, 0x00000052, + 0x00000025, 0x00020001, 0x00000053, 0x00000035, 0x00040001, + 0x00020001, 0x00000060, 0x00000016, 0x00000061, 0x00040001, + 0x00020001, 0x00000062, 0x00000026, 0x00060001, 0x00040001, + 0x00020001, 0x00000005, 0x00000006, 0x00000044, 0x00020001, + 0x00000054, 0x00000045, 0x00120001, 0x000a0001, 0x00040001, + 0x00020001, 0x00000063, 0x00000036, 0x00040001, 0x00020001, + 0x00000070, 0x00000007, 0x00000071, 0x00040001, 0x00020001, + 0x00000017, 0x00000064, 0x00020001, 0x00000046, 0x00000072, + 0x000a0001, 0x00060001, 0x00020001, 0x00000027, 0x00020001, + 0x00000055, 0x00000073, 0x00020001, 0x00000037, 0x00000056, + 0x00080001, 0x00040001, 0x00020001, 0x00000065, 0x00000074, + 0x00020001, 0x00000047, 0x00000066, 0x00040001, 0x00020001, + 0x00000075, 0x00000057, 0x00020001, 0x00000076, 0x00020001, + 0x00000067, 0x00000077, +}; + +static unsigned long g_huffman_table_13[511] = { + 0x00020001, 0x00000000, 0x00060001, 0x00020001, 0x00000010, + 0x00020001, 0x00000001, 0x00000011, 0x001c0001, 0x00080001, + 0x00040001, 0x00020001, 0x00000020, 0x00000002, 0x00020001, + 0x00000021, 0x00000012, 0x00080001, 0x00040001, 0x00020001, + 0x00000022, 0x00000030, 0x00020001, 0x00000003, 0x00000031, + 0x00060001, 0x00020001, 0x00000013, 0x00020001, 0x00000032, + 0x00000023, 0x00040001, 0x00020001, 0x00000040, 0x00000004, + 0x00000041, 0x00460001, 0x001c0001, 0x000e0001, 0x00060001, + 0x00020001, 0x00000014, 0x00020001, 0x00000033, 0x00000042, + 0x00040001, 0x00020001, 0x00000024, 0x00000050, 0x00020001, + 0x00000043, 0x00000034, 0x00040001, 0x00020001, 0x00000051, + 0x00000015, 0x00040001, 0x00020001, 0x00000005, 0x00000052, + 0x00020001, 0x00000025, 0x00020001, 0x00000044, 0x00000053, + 0x000e0001, 0x00080001, 0x00040001, 0x00020001, 0x00000060, + 0x00000006, 0x00020001, 0x00000061, 0x00000016, 0x00040001, + 0x00020001, 0x00000080, 0x00000008, 0x00000081, 0x00100001, + 0x00080001, 0x00040001, 0x00020001, 0x00000035, 0x00000062, + 0x00020001, 0x00000026, 0x00000054, 0x00040001, 0x00020001, + 0x00000045, 0x00000063, 0x00020001, 0x00000036, 0x00000070, + 0x00060001, 0x00040001, 0x00020001, 0x00000007, 0x00000055, + 0x00000071, 0x00020001, 0x00000017, 0x00020001, 0x00000027, + 0x00000037, 0x00480001, 0x00180001, 0x000c0001, 0x00040001, + 0x00020001, 0x00000018, 0x00000082, 0x00020001, 0x00000028, + 0x00040001, 0x00020001, 0x00000064, 0x00000046, 0x00000072, + 0x00080001, 0x00040001, 0x00020001, 0x00000084, 0x00000048, + 0x00020001, 0x00000090, 0x00000009, 0x00020001, 0x00000091, + 0x00000019, 0x00180001, 0x000e0001, 0x00080001, 0x00040001, + 0x00020001, 0x00000073, 0x00000065, 0x00020001, 0x00000056, + 0x00000074, 0x00040001, 0x00020001, 0x00000047, 0x00000066, + 0x00000083, 0x00060001, 0x00020001, 0x00000038, 0x00020001, + 0x00000075, 0x00000057, 0x00020001, 0x00000092, 0x00000029, + 0x000e0001, 0x00080001, 0x00040001, 0x00020001, 0x00000067, + 0x00000085, 0x00020001, 0x00000058, 0x00000039, 0x00020001, + 0x00000093, 0x00020001, 0x00000049, 0x00000086, 0x00060001, + 0x00020001, 0x000000a0, 0x00020001, 0x00000068, 0x0000000a, + 0x00020001, 0x000000a1, 0x0000001a, 0x00440001, 0x00180001, + 0x000c0001, 0x00040001, 0x00020001, 0x000000a2, 0x0000002a, + 0x00040001, 0x00020001, 0x00000095, 0x00000059, 0x00020001, + 0x000000a3, 0x0000003a, 0x00080001, 0x00040001, 0x00020001, + 0x0000004a, 0x00000096, 0x00020001, 0x000000b0, 0x0000000b, + 0x00020001, 0x000000b1, 0x0000001b, 0x00140001, 0x00080001, + 0x00020001, 0x000000b2, 0x00040001, 0x00020001, 0x00000076, + 0x00000077, 0x00000094, 0x00060001, 0x00040001, 0x00020001, + 0x00000087, 0x00000078, 0x000000a4, 0x00040001, 0x00020001, + 0x00000069, 0x000000a5, 0x0000002b, 0x000c0001, 0x00060001, + 0x00040001, 0x00020001, 0x0000005a, 0x00000088, 0x000000b3, + 0x00020001, 0x0000003b, 0x00020001, 0x00000079, 0x000000a6, + 0x00060001, 0x00040001, 0x00020001, 0x0000006a, 0x000000b4, + 0x000000c0, 0x00040001, 0x00020001, 0x0000000c, 0x00000098, + 0x000000c1, 0x003c0001, 0x00160001, 0x000a0001, 0x00060001, + 0x00020001, 0x0000001c, 0x00020001, 0x00000089, 0x000000b5, + 0x00020001, 0x0000005b, 0x000000c2, 0x00040001, 0x00020001, + 0x0000002c, 0x0000003c, 0x00040001, 0x00020001, 0x000000b6, + 0x0000006b, 0x00020001, 0x000000c4, 0x0000004c, 0x00100001, + 0x00080001, 0x00040001, 0x00020001, 0x000000a8, 0x0000008a, + 0x00020001, 0x000000d0, 0x0000000d, 0x00020001, 0x000000d1, + 0x00020001, 0x0000004b, 0x00020001, 0x00000097, 0x000000a7, + 0x000c0001, 0x00060001, 0x00020001, 0x000000c3, 0x00020001, + 0x0000007a, 0x00000099, 0x00040001, 0x00020001, 0x000000c5, + 0x0000005c, 0x000000b7, 0x00040001, 0x00020001, 0x0000001d, + 0x000000d2, 0x00020001, 0x0000002d, 0x00020001, 0x0000007b, + 0x000000d3, 0x00340001, 0x001c0001, 0x000c0001, 0x00040001, + 0x00020001, 0x0000003d, 0x000000c6, 0x00040001, 0x00020001, + 0x0000006c, 0x000000a9, 0x00020001, 0x0000009a, 0x000000d4, + 0x00080001, 0x00040001, 0x00020001, 0x000000b8, 0x0000008b, + 0x00020001, 0x0000004d, 0x000000c7, 0x00040001, 0x00020001, + 0x0000007c, 0x000000d5, 0x00020001, 0x0000005d, 0x000000e0, + 0x000a0001, 0x00040001, 0x00020001, 0x000000e1, 0x0000001e, + 0x00040001, 0x00020001, 0x0000000e, 0x0000002e, 0x000000e2, + 0x00080001, 0x00040001, 0x00020001, 0x000000e3, 0x0000006d, + 0x00020001, 0x0000008c, 0x000000e4, 0x00040001, 0x00020001, + 0x000000e5, 0x000000ba, 0x000000f0, 0x00260001, 0x00100001, + 0x00040001, 0x00020001, 0x000000f1, 0x0000001f, 0x00060001, + 0x00040001, 0x00020001, 0x000000aa, 0x0000009b, 0x000000b9, + 0x00020001, 0x0000003e, 0x00020001, 0x000000d6, 0x000000c8, + 0x000c0001, 0x00060001, 0x00020001, 0x0000004e, 0x00020001, + 0x000000d7, 0x0000007d, 0x00020001, 0x000000ab, 0x00020001, + 0x0000005e, 0x000000c9, 0x00060001, 0x00020001, 0x0000000f, + 0x00020001, 0x0000009c, 0x0000006e, 0x00020001, 0x000000f2, + 0x0000002f, 0x00200001, 0x00100001, 0x00060001, 0x00040001, + 0x00020001, 0x000000d8, 0x0000008d, 0x0000003f, 0x00060001, + 0x00020001, 0x000000f3, 0x00020001, 0x000000e6, 0x000000ca, + 0x00020001, 0x000000f4, 0x0000004f, 0x00080001, 0x00040001, + 0x00020001, 0x000000bb, 0x000000ac, 0x00020001, 0x000000e7, + 0x000000f5, 0x00040001, 0x00020001, 0x000000d9, 0x0000009d, + 0x00020001, 0x0000005f, 0x000000e8, 0x001e0001, 0x000c0001, + 0x00060001, 0x00020001, 0x0000006f, 0x00020001, 0x000000f6, + 0x000000cb, 0x00040001, 0x00020001, 0x000000bc, 0x000000ad, + 0x000000da, 0x00080001, 0x00020001, 0x000000f7, 0x00040001, + 0x00020001, 0x0000007e, 0x0000007f, 0x0000008e, 0x00060001, + 0x00040001, 0x00020001, 0x0000009e, 0x000000ae, 0x000000cc, + 0x00020001, 0x000000f8, 0x0000008f, 0x00120001, 0x00080001, + 0x00040001, 0x00020001, 0x000000db, 0x000000bd, 0x00020001, + 0x000000ea, 0x000000f9, 0x00040001, 0x00020001, 0x0000009f, + 0x000000eb, 0x00020001, 0x000000be, 0x00020001, 0x000000cd, + 0x000000fa, 0x000e0001, 0x00040001, 0x00020001, 0x000000dd, + 0x000000ec, 0x00060001, 0x00040001, 0x00020001, 0x000000e9, + 0x000000af, 0x000000dc, 0x00020001, 0x000000ce, 0x000000fb, + 0x00080001, 0x00040001, 0x00020001, 0x000000bf, 0x000000de, + 0x00020001, 0x000000cf, 0x000000ee, 0x00040001, 0x00020001, + 0x000000df, 0x000000ef, 0x00020001, 0x000000ff, 0x00020001, + 0x000000ed, 0x00020001, 0x000000fd, 0x00020001, 0x000000fc, + 0x000000fe, +}; + +static unsigned long g_huffman_table_15[511] = { + 0x00100001, 0x00060001, 0x00020001, 0x00000000, 0x00020001, + 0x00000010, 0x00000001, 0x00020001, 0x00000011, 0x00040001, + 0x00020001, 0x00000020, 0x00000002, 0x00020001, 0x00000021, + 0x00000012, 0x00320001, 0x00100001, 0x00060001, 0x00020001, + 0x00000022, 0x00020001, 0x00000030, 0x00000031, 0x00060001, + 0x00020001, 0x00000013, 0x00020001, 0x00000003, 0x00000040, + 0x00020001, 0x00000032, 0x00000023, 0x000e0001, 0x00060001, + 0x00040001, 0x00020001, 0x00000004, 0x00000014, 0x00000041, + 0x00040001, 0x00020001, 0x00000033, 0x00000042, 0x00020001, + 0x00000024, 0x00000043, 0x000a0001, 0x00060001, 0x00020001, + 0x00000034, 0x00020001, 0x00000050, 0x00000005, 0x00020001, + 0x00000051, 0x00000015, 0x00040001, 0x00020001, 0x00000052, + 0x00000025, 0x00040001, 0x00020001, 0x00000044, 0x00000053, + 0x00000061, 0x005a0001, 0x00240001, 0x00120001, 0x000a0001, + 0x00060001, 0x00020001, 0x00000035, 0x00020001, 0x00000060, + 0x00000006, 0x00020001, 0x00000016, 0x00000062, 0x00040001, + 0x00020001, 0x00000026, 0x00000054, 0x00020001, 0x00000045, + 0x00000063, 0x000a0001, 0x00060001, 0x00020001, 0x00000036, + 0x00020001, 0x00000070, 0x00000007, 0x00020001, 0x00000071, + 0x00000055, 0x00040001, 0x00020001, 0x00000017, 0x00000064, + 0x00020001, 0x00000072, 0x00000027, 0x00180001, 0x00100001, + 0x00080001, 0x00040001, 0x00020001, 0x00000046, 0x00000073, + 0x00020001, 0x00000037, 0x00000065, 0x00040001, 0x00020001, + 0x00000056, 0x00000080, 0x00020001, 0x00000008, 0x00000074, + 0x00040001, 0x00020001, 0x00000081, 0x00000018, 0x00020001, + 0x00000082, 0x00000028, 0x00100001, 0x00080001, 0x00040001, + 0x00020001, 0x00000047, 0x00000066, 0x00020001, 0x00000083, + 0x00000038, 0x00040001, 0x00020001, 0x00000075, 0x00000057, + 0x00020001, 0x00000084, 0x00000048, 0x00060001, 0x00040001, + 0x00020001, 0x00000090, 0x00000019, 0x00000091, 0x00040001, + 0x00020001, 0x00000092, 0x00000076, 0x00020001, 0x00000067, + 0x00000029, 0x005c0001, 0x00240001, 0x00120001, 0x000a0001, + 0x00040001, 0x00020001, 0x00000085, 0x00000058, 0x00040001, + 0x00020001, 0x00000009, 0x00000077, 0x00000093, 0x00040001, + 0x00020001, 0x00000039, 0x00000094, 0x00020001, 0x00000049, + 0x00000086, 0x000a0001, 0x00060001, 0x00020001, 0x00000068, + 0x00020001, 0x000000a0, 0x0000000a, 0x00020001, 0x000000a1, + 0x0000001a, 0x00040001, 0x00020001, 0x000000a2, 0x0000002a, + 0x00020001, 0x00000095, 0x00000059, 0x001a0001, 0x000e0001, + 0x00060001, 0x00020001, 0x000000a3, 0x00020001, 0x0000003a, + 0x00000087, 0x00040001, 0x00020001, 0x00000078, 0x000000a4, + 0x00020001, 0x0000004a, 0x00000096, 0x00060001, 0x00040001, + 0x00020001, 0x00000069, 0x000000b0, 0x000000b1, 0x00040001, + 0x00020001, 0x0000001b, 0x000000a5, 0x000000b2, 0x000e0001, + 0x00080001, 0x00040001, 0x00020001, 0x0000005a, 0x0000002b, + 0x00020001, 0x00000088, 0x00000097, 0x00020001, 0x000000b3, + 0x00020001, 0x00000079, 0x0000003b, 0x00080001, 0x00040001, + 0x00020001, 0x0000006a, 0x000000b4, 0x00020001, 0x0000004b, + 0x000000c1, 0x00040001, 0x00020001, 0x00000098, 0x00000089, + 0x00020001, 0x0000001c, 0x000000b5, 0x00500001, 0x00220001, + 0x00100001, 0x00060001, 0x00040001, 0x00020001, 0x0000005b, + 0x0000002c, 0x000000c2, 0x00060001, 0x00040001, 0x00020001, + 0x0000000b, 0x000000c0, 0x000000a6, 0x00020001, 0x000000a7, + 0x0000007a, 0x000a0001, 0x00040001, 0x00020001, 0x000000c3, + 0x0000003c, 0x00040001, 0x00020001, 0x0000000c, 0x00000099, + 0x000000b6, 0x00040001, 0x00020001, 0x0000006b, 0x000000c4, + 0x00020001, 0x0000004c, 0x000000a8, 0x00140001, 0x000a0001, + 0x00040001, 0x00020001, 0x0000008a, 0x000000c5, 0x00040001, + 0x00020001, 0x000000d0, 0x0000005c, 0x000000d1, 0x00040001, + 0x00020001, 0x000000b7, 0x0000007b, 0x00020001, 0x0000001d, + 0x00020001, 0x0000000d, 0x0000002d, 0x000c0001, 0x00040001, + 0x00020001, 0x000000d2, 0x000000d3, 0x00040001, 0x00020001, + 0x0000003d, 0x000000c6, 0x00020001, 0x0000006c, 0x000000a9, + 0x00060001, 0x00040001, 0x00020001, 0x0000009a, 0x000000b8, + 0x000000d4, 0x00040001, 0x00020001, 0x0000008b, 0x0000004d, + 0x00020001, 0x000000c7, 0x0000007c, 0x00440001, 0x00220001, + 0x00120001, 0x000a0001, 0x00040001, 0x00020001, 0x000000d5, + 0x0000005d, 0x00040001, 0x00020001, 0x000000e0, 0x0000000e, + 0x000000e1, 0x00040001, 0x00020001, 0x0000001e, 0x000000e2, + 0x00020001, 0x000000aa, 0x0000002e, 0x00080001, 0x00040001, + 0x00020001, 0x000000b9, 0x0000009b, 0x00020001, 0x000000e3, + 0x000000d6, 0x00040001, 0x00020001, 0x0000006d, 0x0000003e, + 0x00020001, 0x000000c8, 0x0000008c, 0x00100001, 0x00080001, + 0x00040001, 0x00020001, 0x000000e4, 0x0000004e, 0x00020001, + 0x000000d7, 0x0000007d, 0x00040001, 0x00020001, 0x000000e5, + 0x000000ba, 0x00020001, 0x000000ab, 0x0000005e, 0x00080001, + 0x00040001, 0x00020001, 0x000000c9, 0x0000009c, 0x00020001, + 0x000000f1, 0x0000001f, 0x00060001, 0x00040001, 0x00020001, + 0x000000f0, 0x0000006e, 0x000000f2, 0x00020001, 0x0000002f, + 0x000000e6, 0x00260001, 0x00120001, 0x00080001, 0x00040001, + 0x00020001, 0x000000d8, 0x000000f3, 0x00020001, 0x0000003f, + 0x000000f4, 0x00060001, 0x00020001, 0x0000004f, 0x00020001, + 0x0000008d, 0x000000d9, 0x00020001, 0x000000bb, 0x000000ca, + 0x00080001, 0x00040001, 0x00020001, 0x000000ac, 0x000000e7, + 0x00020001, 0x0000007e, 0x000000f5, 0x00080001, 0x00040001, + 0x00020001, 0x0000009d, 0x0000005f, 0x00020001, 0x000000e8, + 0x0000008e, 0x00020001, 0x000000f6, 0x000000cb, 0x00220001, + 0x00120001, 0x000a0001, 0x00060001, 0x00040001, 0x00020001, + 0x0000000f, 0x000000ae, 0x0000006f, 0x00020001, 0x000000bc, + 0x000000da, 0x00040001, 0x00020001, 0x000000ad, 0x000000f7, + 0x00020001, 0x0000007f, 0x000000e9, 0x00080001, 0x00040001, + 0x00020001, 0x0000009e, 0x000000cc, 0x00020001, 0x000000f8, + 0x0000008f, 0x00040001, 0x00020001, 0x000000db, 0x000000bd, + 0x00020001, 0x000000ea, 0x000000f9, 0x00100001, 0x00080001, + 0x00040001, 0x00020001, 0x0000009f, 0x000000dc, 0x00020001, + 0x000000cd, 0x000000eb, 0x00040001, 0x00020001, 0x000000be, + 0x000000fa, 0x00020001, 0x000000af, 0x000000dd, 0x000e0001, + 0x00060001, 0x00040001, 0x00020001, 0x000000ec, 0x000000ce, + 0x000000fb, 0x00040001, 0x00020001, 0x000000bf, 0x000000ed, + 0x00020001, 0x000000de, 0x000000fc, 0x00060001, 0x00040001, + 0x00020001, 0x000000cf, 0x000000fd, 0x000000ee, 0x00040001, + 0x00020001, 0x000000df, 0x000000fe, 0x00020001, 0x000000ef, + 0x000000ff, +}; + +static unsigned long g_huffman_table_16[511] = { + 0x00020001, 0x00000000, 0x00060001, 0x00020001, 0x00000010, + 0x00020001, 0x00000001, 0x00000011, 0x002a0001, 0x00080001, + 0x00040001, 0x00020001, 0x00000020, 0x00000002, 0x00020001, + 0x00000021, 0x00000012, 0x000a0001, 0x00060001, 0x00020001, + 0x00000022, 0x00020001, 0x00000030, 0x00000003, 0x00020001, + 0x00000031, 0x00000013, 0x000a0001, 0x00040001, 0x00020001, + 0x00000032, 0x00000023, 0x00040001, 0x00020001, 0x00000040, + 0x00000004, 0x00000041, 0x00060001, 0x00020001, 0x00000014, + 0x00020001, 0x00000033, 0x00000042, 0x00040001, 0x00020001, + 0x00000024, 0x00000050, 0x00020001, 0x00000043, 0x00000034, + 0x008a0001, 0x00280001, 0x00100001, 0x00060001, 0x00040001, + 0x00020001, 0x00000005, 0x00000015, 0x00000051, 0x00040001, + 0x00020001, 0x00000052, 0x00000025, 0x00040001, 0x00020001, + 0x00000044, 0x00000035, 0x00000053, 0x000a0001, 0x00060001, + 0x00040001, 0x00020001, 0x00000060, 0x00000006, 0x00000061, + 0x00020001, 0x00000016, 0x00000062, 0x00080001, 0x00040001, + 0x00020001, 0x00000026, 0x00000054, 0x00020001, 0x00000045, + 0x00000063, 0x00040001, 0x00020001, 0x00000036, 0x00000070, + 0x00000071, 0x00280001, 0x00120001, 0x00080001, 0x00020001, + 0x00000017, 0x00020001, 0x00000007, 0x00020001, 0x00000055, + 0x00000064, 0x00040001, 0x00020001, 0x00000072, 0x00000027, + 0x00040001, 0x00020001, 0x00000046, 0x00000065, 0x00000073, + 0x000a0001, 0x00060001, 0x00020001, 0x00000037, 0x00020001, + 0x00000056, 0x00000008, 0x00020001, 0x00000080, 0x00000081, + 0x00060001, 0x00020001, 0x00000018, 0x00020001, 0x00000074, + 0x00000047, 0x00020001, 0x00000082, 0x00020001, 0x00000028, + 0x00000066, 0x00180001, 0x000e0001, 0x00080001, 0x00040001, + 0x00020001, 0x00000083, 0x00000038, 0x00020001, 0x00000075, + 0x00000084, 0x00040001, 0x00020001, 0x00000048, 0x00000090, + 0x00000091, 0x00060001, 0x00020001, 0x00000019, 0x00020001, + 0x00000009, 0x00000076, 0x00020001, 0x00000092, 0x00000029, + 0x000e0001, 0x00080001, 0x00040001, 0x00020001, 0x00000085, + 0x00000058, 0x00020001, 0x00000093, 0x00000039, 0x00040001, + 0x00020001, 0x000000a0, 0x0000000a, 0x0000001a, 0x00080001, + 0x00020001, 0x000000a2, 0x00020001, 0x00000067, 0x00020001, + 0x00000057, 0x00000049, 0x00060001, 0x00020001, 0x00000094, + 0x00020001, 0x00000077, 0x00000086, 0x00020001, 0x000000a1, + 0x00020001, 0x00000068, 0x00000095, 0x00dc0001, 0x007e0001, + 0x00320001, 0x001a0001, 0x000c0001, 0x00060001, 0x00020001, + 0x0000002a, 0x00020001, 0x00000059, 0x0000003a, 0x00020001, + 0x000000a3, 0x00020001, 0x00000087, 0x00000078, 0x00080001, + 0x00040001, 0x00020001, 0x000000a4, 0x0000004a, 0x00020001, + 0x00000096, 0x00000069, 0x00040001, 0x00020001, 0x000000b0, + 0x0000000b, 0x000000b1, 0x000a0001, 0x00040001, 0x00020001, + 0x0000001b, 0x000000b2, 0x00020001, 0x0000002b, 0x00020001, + 0x000000a5, 0x0000005a, 0x00060001, 0x00020001, 0x000000b3, + 0x00020001, 0x000000a6, 0x0000006a, 0x00040001, 0x00020001, + 0x000000b4, 0x0000004b, 0x00020001, 0x0000000c, 0x000000c1, + 0x001e0001, 0x000e0001, 0x00060001, 0x00040001, 0x00020001, + 0x000000b5, 0x000000c2, 0x0000002c, 0x00040001, 0x00020001, + 0x000000a7, 0x000000c3, 0x00020001, 0x0000006b, 0x000000c4, + 0x00080001, 0x00020001, 0x0000001d, 0x00040001, 0x00020001, + 0x00000088, 0x00000097, 0x0000003b, 0x00040001, 0x00020001, + 0x000000d1, 0x000000d2, 0x00020001, 0x0000002d, 0x000000d3, + 0x00120001, 0x00060001, 0x00040001, 0x00020001, 0x0000001e, + 0x0000002e, 0x000000e2, 0x00060001, 0x00040001, 0x00020001, + 0x00000079, 0x00000098, 0x000000c0, 0x00020001, 0x0000001c, + 0x00020001, 0x00000089, 0x0000005b, 0x000e0001, 0x00060001, + 0x00020001, 0x0000003c, 0x00020001, 0x0000007a, 0x000000b6, + 0x00040001, 0x00020001, 0x0000004c, 0x00000099, 0x00020001, + 0x000000a8, 0x0000008a, 0x00060001, 0x00020001, 0x0000000d, + 0x00020001, 0x000000c5, 0x0000005c, 0x00040001, 0x00020001, + 0x0000003d, 0x000000c6, 0x00020001, 0x0000006c, 0x0000009a, + 0x00580001, 0x00560001, 0x00240001, 0x00100001, 0x00080001, + 0x00040001, 0x00020001, 0x0000008b, 0x0000004d, 0x00020001, + 0x000000c7, 0x0000007c, 0x00040001, 0x00020001, 0x000000d5, + 0x0000005d, 0x00020001, 0x000000e0, 0x0000000e, 0x00080001, + 0x00020001, 0x000000e3, 0x00040001, 0x00020001, 0x000000d0, + 0x000000b7, 0x0000007b, 0x00060001, 0x00040001, 0x00020001, + 0x000000a9, 0x000000b8, 0x000000d4, 0x00020001, 0x000000e1, + 0x00020001, 0x000000aa, 0x000000b9, 0x00180001, 0x000a0001, + 0x00060001, 0x00040001, 0x00020001, 0x0000009b, 0x000000d6, + 0x0000006d, 0x00020001, 0x0000003e, 0x000000c8, 0x00060001, + 0x00040001, 0x00020001, 0x0000008c, 0x000000e4, 0x0000004e, + 0x00040001, 0x00020001, 0x000000d7, 0x000000e5, 0x00020001, + 0x000000ba, 0x000000ab, 0x000c0001, 0x00040001, 0x00020001, + 0x0000009c, 0x000000e6, 0x00040001, 0x00020001, 0x0000006e, + 0x000000d8, 0x00020001, 0x0000008d, 0x000000bb, 0x00080001, + 0x00040001, 0x00020001, 0x000000e7, 0x0000009d, 0x00020001, + 0x000000e8, 0x0000008e, 0x00040001, 0x00020001, 0x000000cb, + 0x000000bc, 0x0000009e, 0x000000f1, 0x00020001, 0x0000001f, + 0x00020001, 0x0000000f, 0x0000002f, 0x00420001, 0x00380001, + 0x00020001, 0x000000f2, 0x00340001, 0x00320001, 0x00140001, + 0x00080001, 0x00020001, 0x000000bd, 0x00020001, 0x0000005e, + 0x00020001, 0x0000007d, 0x000000c9, 0x00060001, 0x00020001, + 0x000000ca, 0x00020001, 0x000000ac, 0x0000007e, 0x00040001, + 0x00020001, 0x000000da, 0x000000ad, 0x000000cc, 0x000a0001, + 0x00060001, 0x00020001, 0x000000ae, 0x00020001, 0x000000db, + 0x000000dc, 0x00020001, 0x000000cd, 0x000000be, 0x00060001, + 0x00040001, 0x00020001, 0x000000eb, 0x000000ed, 0x000000ee, + 0x00060001, 0x00040001, 0x00020001, 0x000000d9, 0x000000ea, + 0x000000e9, 0x00020001, 0x000000de, 0x00040001, 0x00020001, + 0x000000dd, 0x000000ec, 0x000000ce, 0x0000003f, 0x000000f0, + 0x00040001, 0x00020001, 0x000000f3, 0x000000f4, 0x00020001, + 0x0000004f, 0x00020001, 0x000000f5, 0x0000005f, 0x000a0001, + 0x00020001, 0x000000ff, 0x00040001, 0x00020001, 0x000000f6, + 0x0000006f, 0x00020001, 0x000000f7, 0x0000007f, 0x000c0001, + 0x00060001, 0x00020001, 0x0000008f, 0x00020001, 0x000000f8, + 0x000000f9, 0x00040001, 0x00020001, 0x0000009f, 0x000000fa, + 0x000000af, 0x00080001, 0x00040001, 0x00020001, 0x000000fb, + 0x000000bf, 0x00020001, 0x000000fc, 0x000000cf, 0x00040001, + 0x00020001, 0x000000fd, 0x000000df, 0x00020001, 0x000000fe, + 0x000000ef, +}; + +static unsigned long g_huffman_table_24[512] = { + 0x003c0001, 0x00080001, 0x00040001, 0x00020001, 0x00000000, + 0x00000010, 0x00020001, 0x00000001, 0x00000011, 0x000e0001, + 0x00060001, 0x00040001, 0x00020001, 0x00000020, 0x00000002, + 0x00000021, 0x00020001, 0x00000012, 0x00020001, 0x00000022, + 0x00020001, 0x00000030, 0x00000003, 0x000e0001, 0x00040001, + 0x00020001, 0x00000031, 0x00000013, 0x00040001, 0x00020001, + 0x00000032, 0x00000023, 0x00040001, 0x00020001, 0x00000040, + 0x00000004, 0x00000041, 0x00080001, 0x00040001, 0x00020001, + 0x00000014, 0x00000033, 0x00020001, 0x00000042, 0x00000024, + 0x00060001, 0x00040001, 0x00020001, 0x00000043, 0x00000034, + 0x00000051, 0x00060001, 0x00040001, 0x00020001, 0x00000050, + 0x00000005, 0x00000015, 0x00020001, 0x00000052, 0x00000025, + 0x00fa0001, 0x00620001, 0x00220001, 0x00120001, 0x000a0001, + 0x00040001, 0x00020001, 0x00000044, 0x00000053, 0x00020001, + 0x00000035, 0x00020001, 0x00000060, 0x00000006, 0x00040001, + 0x00020001, 0x00000061, 0x00000016, 0x00020001, 0x00000062, + 0x00000026, 0x00080001, 0x00040001, 0x00020001, 0x00000054, + 0x00000045, 0x00020001, 0x00000063, 0x00000036, 0x00040001, + 0x00020001, 0x00000071, 0x00000055, 0x00020001, 0x00000064, + 0x00000046, 0x00200001, 0x000e0001, 0x00060001, 0x00020001, + 0x00000072, 0x00020001, 0x00000027, 0x00000037, 0x00020001, + 0x00000073, 0x00040001, 0x00020001, 0x00000070, 0x00000007, + 0x00000017, 0x000a0001, 0x00040001, 0x00020001, 0x00000065, + 0x00000056, 0x00040001, 0x00020001, 0x00000080, 0x00000008, + 0x00000081, 0x00040001, 0x00020001, 0x00000074, 0x00000047, + 0x00020001, 0x00000018, 0x00000082, 0x00100001, 0x00080001, + 0x00040001, 0x00020001, 0x00000028, 0x00000066, 0x00020001, + 0x00000083, 0x00000038, 0x00040001, 0x00020001, 0x00000075, + 0x00000057, 0x00020001, 0x00000084, 0x00000048, 0x00080001, + 0x00040001, 0x00020001, 0x00000091, 0x00000019, 0x00020001, + 0x00000092, 0x00000076, 0x00040001, 0x00020001, 0x00000067, + 0x00000029, 0x00020001, 0x00000085, 0x00000058, 0x005c0001, + 0x00220001, 0x00100001, 0x00080001, 0x00040001, 0x00020001, + 0x00000093, 0x00000039, 0x00020001, 0x00000094, 0x00000049, + 0x00040001, 0x00020001, 0x00000077, 0x00000086, 0x00020001, + 0x00000068, 0x000000a1, 0x00080001, 0x00040001, 0x00020001, + 0x000000a2, 0x0000002a, 0x00020001, 0x00000095, 0x00000059, + 0x00040001, 0x00020001, 0x000000a3, 0x0000003a, 0x00020001, + 0x00000087, 0x00020001, 0x00000078, 0x0000004a, 0x00160001, + 0x000c0001, 0x00040001, 0x00020001, 0x000000a4, 0x00000096, + 0x00040001, 0x00020001, 0x00000069, 0x000000b1, 0x00020001, + 0x0000001b, 0x000000a5, 0x00060001, 0x00020001, 0x000000b2, + 0x00020001, 0x0000005a, 0x0000002b, 0x00020001, 0x00000088, + 0x000000b3, 0x00100001, 0x000a0001, 0x00060001, 0x00020001, + 0x00000090, 0x00020001, 0x00000009, 0x000000a0, 0x00020001, + 0x00000097, 0x00000079, 0x00040001, 0x00020001, 0x000000a6, + 0x0000006a, 0x000000b4, 0x000c0001, 0x00060001, 0x00020001, + 0x0000001a, 0x00020001, 0x0000000a, 0x000000b0, 0x00020001, + 0x0000003b, 0x00020001, 0x0000000b, 0x000000c0, 0x00040001, + 0x00020001, 0x0000004b, 0x000000c1, 0x00020001, 0x00000098, + 0x00000089, 0x00430001, 0x00220001, 0x00100001, 0x00080001, + 0x00040001, 0x00020001, 0x0000001c, 0x000000b5, 0x00020001, + 0x0000005b, 0x000000c2, 0x00040001, 0x00020001, 0x0000002c, + 0x000000a7, 0x00020001, 0x0000007a, 0x000000c3, 0x000a0001, + 0x00060001, 0x00020001, 0x0000003c, 0x00020001, 0x0000000c, + 0x000000d0, 0x00020001, 0x000000b6, 0x0000006b, 0x00040001, + 0x00020001, 0x000000c4, 0x0000004c, 0x00020001, 0x00000099, + 0x000000a8, 0x00100001, 0x00080001, 0x00040001, 0x00020001, + 0x0000008a, 0x000000c5, 0x00020001, 0x0000005c, 0x000000d1, + 0x00040001, 0x00020001, 0x000000b7, 0x0000007b, 0x00020001, + 0x0000001d, 0x000000d2, 0x00090001, 0x00040001, 0x00020001, + 0x0000002d, 0x000000d3, 0x00020001, 0x0000003d, 0x000000c6, + 0x005500fa, 0x00040001, 0x00020001, 0x0000006c, 0x000000a9, + 0x00020001, 0x0000009a, 0x000000d4, 0x00200001, 0x00100001, + 0x00080001, 0x00040001, 0x00020001, 0x000000b8, 0x0000008b, + 0x00020001, 0x0000004d, 0x000000c7, 0x00040001, 0x00020001, + 0x0000007c, 0x000000d5, 0x00020001, 0x0000005d, 0x000000e1, + 0x00080001, 0x00040001, 0x00020001, 0x0000001e, 0x000000e2, + 0x00020001, 0x000000aa, 0x000000b9, 0x00040001, 0x00020001, + 0x0000009b, 0x000000e3, 0x00020001, 0x000000d6, 0x0000006d, + 0x00140001, 0x000a0001, 0x00060001, 0x00020001, 0x0000003e, + 0x00020001, 0x0000002e, 0x0000004e, 0x00020001, 0x000000c8, + 0x0000008c, 0x00040001, 0x00020001, 0x000000e4, 0x000000d7, + 0x00040001, 0x00020001, 0x0000007d, 0x000000ab, 0x000000e5, + 0x000a0001, 0x00040001, 0x00020001, 0x000000ba, 0x0000005e, + 0x00020001, 0x000000c9, 0x00020001, 0x0000009c, 0x0000006e, + 0x00080001, 0x00020001, 0x000000e6, 0x00020001, 0x0000000d, + 0x00020001, 0x000000e0, 0x0000000e, 0x00040001, 0x00020001, + 0x000000d8, 0x0000008d, 0x00020001, 0x000000bb, 0x000000ca, + 0x004a0001, 0x00020001, 0x000000ff, 0x00400001, 0x003a0001, + 0x00200001, 0x00100001, 0x00080001, 0x00040001, 0x00020001, + 0x000000ac, 0x000000e7, 0x00020001, 0x0000007e, 0x000000d9, + 0x00040001, 0x00020001, 0x0000009d, 0x000000e8, 0x00020001, + 0x0000008e, 0x000000cb, 0x00080001, 0x00040001, 0x00020001, + 0x000000bc, 0x000000da, 0x00020001, 0x000000ad, 0x000000e9, + 0x00040001, 0x00020001, 0x0000009e, 0x000000cc, 0x00020001, + 0x000000db, 0x000000bd, 0x00100001, 0x00080001, 0x00040001, + 0x00020001, 0x000000ea, 0x000000ae, 0x00020001, 0x000000dc, + 0x000000cd, 0x00040001, 0x00020001, 0x000000eb, 0x000000be, + 0x00020001, 0x000000dd, 0x000000ec, 0x00080001, 0x00040001, + 0x00020001, 0x000000ce, 0x000000ed, 0x00020001, 0x000000de, + 0x000000ee, 0x0000000f, 0x00040001, 0x00020001, 0x000000f0, + 0x0000001f, 0x000000f1, 0x00040001, 0x00020001, 0x000000f2, + 0x0000002f, 0x00020001, 0x000000f3, 0x0000003f, 0x00120001, + 0x00080001, 0x00040001, 0x00020001, 0x000000f4, 0x0000004f, + 0x00020001, 0x000000f5, 0x0000005f, 0x00040001, 0x00020001, + 0x000000f6, 0x0000006f, 0x00020001, 0x000000f7, 0x00020001, + 0x0000007f, 0x0000008f, 0x000a0001, 0x00040001, 0x00020001, + 0x000000f8, 0x000000f9, 0x00040001, 0x00020001, 0x0000009f, + 0x000000af, 0x000000fa, 0x00080001, 0x00040001, 0x00020001, + 0x000000fb, 0x000000bf, 0x00020001, 0x000000fc, 0x000000cf, + 0x00040001, 0x00020001, 0x000000fd, 0x000000df, 0x00020001, + 0x000000fe, 0x000000ef, +}; + +static unsigned long g_huffman_table_32[31] = { + 0x00020001, 0x00000000, 0x00080001, 0x00040001, 0x00020001, + 0x00000008, 0x00000004, 0x00020001, 0x00000001, 0x00000002, + 0x00080001, 0x00040001, 0x00020001, 0x0000000c, 0x0000000a, + 0x00020001, 0x00000003, 0x00000006, 0x00060001, 0x00020001, + 0x00000009, 0x00020001, 0x00000005, 0x00000007, 0x00040001, + 0x00020001, 0x0000000e, 0x0000000d, 0x00020001, 0x0000000f, + 0x0000000b, +}; + +static unsigned long g_huffman_table_33[31] = { + 0x00100001, 0x00080001, 0x00040001, 0x00020001, 0x00000000, + 0x00000001, 0x00020001, 0x00000002, 0x00000003, 0x00040001, + 0x00020001, 0x00000004, 0x00000005, 0x00020001, 0x00000006, + 0x00000007, 0x00080001, 0x00040001, 0x00020001, 0x00000008, + 0x00000009, 0x00020001, 0x0000000a, 0x0000000b, 0x00040001, + 0x00020001, 0x0000000c, 0x0000000d, 0x00020001, 0x0000000e, + 0x0000000f, +}; + +typedef struct _HUFFMAN_ +{ + unsigned long * TableData; + unsigned long TreeLen; + unsigned long linbits; +} HuffmanData; + +static HuffmanData g_huffman_main [34] = +{ + { 0 , 0, 0 }, /* Table 0 */ + { g_huffman_table_1 , 7, 0 }, /* Table 1 */ + { g_huffman_table_2 , 17, 0 }, /* Table 2 */ + { g_huffman_table_3 , 17, 0 }, /* Table 3 */ + { 0 , 0, 0 }, /* Table 4 */ + { g_huffman_table_5 , 31, 0 }, /* Table 5 */ + { g_huffman_table_6 , 31, 0 }, /* Table 6 */ + { g_huffman_table_7 , 71, 0 }, /* Table 7 */ + { g_huffman_table_8 , 71, 0 }, /* Table 8 */ + { g_huffman_table_9 , 71, 0 }, /* Table 9 */ + { g_huffman_table_10, 127, 0 }, /* Table 10 */ + { g_huffman_table_11, 127, 0 }, /* Table 11 */ + { g_huffman_table_12, 127, 0 }, /* Table 12 */ + { g_huffman_table_13, 511, 0 }, /* Table 13 */ + { 0 , 0, 0 }, /* Table 14 */ + { g_huffman_table_15, 511, 0 }, /* Table 15 */ + { g_huffman_table_16, 511, 1 }, /* Table 16 */ + { g_huffman_table_16, 511, 2 }, /* Table 17 */ + { g_huffman_table_16, 511, 3 }, /* Table 18 */ + { g_huffman_table_16, 511, 4 }, /* Table 19 */ + { g_huffman_table_16, 511, 6 }, /* Table 20 */ + { g_huffman_table_16, 511, 8 }, /* Table 21 */ + { g_huffman_table_16, 511, 10 }, /* Table 22 */ + { g_huffman_table_16, 511, 13 }, /* Table 23 */ + { g_huffman_table_24, 512, 4 }, /* Table 24 */ + { g_huffman_table_24, 512, 5 }, /* Table 25 */ + { g_huffman_table_24, 512, 6 }, /* Table 26 */ + { g_huffman_table_24, 512, 7 }, /* Table 27 */ + { g_huffman_table_24, 512, 8 }, /* Table 28 */ + { g_huffman_table_24, 512, 9 }, /* Table 29 */ + { g_huffman_table_24, 512, 11 }, /* Table 30 */ + { g_huffman_table_24, 512, 13 }, /* Table 31 */ + { g_huffman_table_32, 31, 0 }, /* Table 32 */ + { g_huffman_table_33, 31, 0 }, /* Table 33 */ +}; + +const CLayer3Decoder::SBI CLayer3Decoder::sfBandIndex[3][3] = +{ + { // MPEG1 + { // 44.4 Khz + {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576}, + {0, 4, 8, 12, 16, 22, 30, 40, 52, 66, 84, 106, 136, 192 } + }, + { // 48 Khz + {0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576 }, + {0, 4, 8, 12, 16, 22, 28, 38, 50, 64, 80, 100, 126, 192 } + }, + { // 32Khz + {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576 }, + {0, 4, 8, 12, 16, 22, 30, 42, 58, 78, 104, 138, 180, 192} + } + }, + { // MPEG2 + { // 22.5khz + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {0, 4, 8, 12, 18, 24, 32, 42, 56, 74, 100, 132, 174, 192} + }, + { // 24Khz + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 332, 394, 464, 540, 576}, + {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 136, 180, 192} + }, + { // 16khz + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192} + }, + }, + { // MPEG2.5 + { // 11.25Khz + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192}, + }, + { // 12Khz + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192}, + }, + { // 8Khz + {0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, 232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576}, + {0, 8, 16, 24, 36, 52, 72, 96, 124, 160, 162, 164, 166, 192}, + }, + } +}; + +const float CLayer3Decoder::ShortTwiddles[] = +{ + 0.866025403f, 0.500000000f, 1.931851653f, + 0.707106781f, 0.517638090f, 0.504314480f, + 0.541196100f, 0.630236207f, 0.821339815f, + 1.306562965f, 3.830648788f, 0.793353340f, + 0.608761429f, 0.923879532f, 0.382683432f, + 0.991444861f, 0.130526192f, 0.382683432f, + 0.608761429f, 0.793353340f, 0.923879532f, + 0.991444861f, 0.130526192f +}; + +const float CLayer3Decoder::NormalTwiddles[] = +{ + 5.736856623f, 1.931851653f, 1.183100792f, + 0.871723397f, 0.707106781f, 0.610387294f, + 0.551688959f, 0.517638090f, 0.501909918f, + -0.500476342f, -0.504314480f, -0.512139757f, + -0.524264562f, -0.541196100f, -0.563690973f, + -0.592844523f, -0.630236207f, -0.678170852f, + -0.740093616f, -0.821339815f, -0.930579498f, + -1.082840285f, -1.306562965f, -1.662754762f, + -2.310113158f, -3.830648788f, -11.46279281f +}; + +CLayer3Decoder::CLayer3Decoder() +{ + int i; + float ci[8] = { -0.6f, -0.535f, -0.33f, -0.185f, -0.095f, -0.041f, -0.0142f, -0.0037f }; + + for (i = 0; i < 8; i++) + { + Cs[i] = 1.0f / (float)sqrt(1.0 + ci[i]*ci[i]); + Ca[i] = ci[i] / (float)sqrt(1.0 + ci[i]*ci[i]); + } + + for(i = 0; i < 64; i++) + { + PowerTableMinus2[i] = (float)pow(2.0, -2.0 * i); + PowerTableMinus05[i] = (float)pow(2.0, -0.5 * i); + } + + for(i = 0; i < 256; i++) + GainTable[i] = (float)pow(2.0 , (0.25 * (i - 210.0))); + + // table for the MPEG1 intensity stereo position + // 7 == INVALID_POS so.... + for(i=0; i<16; i++) + { + TanPi12Table[i] = (float)tan(i * PI/12); + } + + // magic for the IMDCT stuff + int odd_i, two_odd_i, four_odd_i, eight_odd_i; + int j = 0; + for(i = 0; i < 9; i++) + { + odd_i = (i << 1) + 1; + two_odd_i = odd_i << 1; + four_odd_i = odd_i << 2; + IMDCT9x8Table[j++] = (float)cos(PI18 * odd_i); + IMDCT9x8Table[j++] = (float)cos(PI18 * two_odd_i); + + eight_odd_i = two_odd_i << 2; + IMDCT9x8Table[j++] = (float)cos(PI18 * (four_odd_i - odd_i)); + IMDCT9x8Table[j++] = (float)cos(PI18 * four_odd_i); + IMDCT9x8Table[j++] = (float)cos(PI18 * (four_odd_i + odd_i)); + IMDCT9x8Table[j++] = (float)cos(PI18 * (four_odd_i + two_odd_i)); + IMDCT9x8Table[j++] = (float)cos(PI18 * (eight_odd_i - odd_i)); + IMDCT9x8Table[j++] = (float)cos(PI18 * eight_odd_i); + } + + for(int ch=0;ch<2;ch++) + for(int j=0; j<576; j++) + prevblck[ch][j] = 0.0f; + + /* block_type 0 (normal window) */ + for(i = 0; i < 36; i++) + IMDCTwin[0][i] = (float)sin(PI36 * (i + 0.5)); + + /* block_type 1 (start block) */ + for(i = 0; i < 18; i++) + IMDCTwin[1][i] = (float)sin(PI36 * (i + 0.5)); + for(i = 18; i < 24; i++) + IMDCTwin[1][i] = 1.0f; + for(i = 24; i < 30; i++) + IMDCTwin[1][i] = (float)sin(PI12 * (i - 18 + 0.5)); + for(i = 30; i < 36; i++) + IMDCTwin[1][i] = 0.0f; + + /* block_type 2 (short block) */ + for(i = 0; i < 12; i++) + IMDCTwin[2][i] = (float)sin(PI12 * (i + 0.5)); + for(i = 12; i < 36; i++) + IMDCTwin[2][i] = 0.0f; + + /* block_type 3 (stop block) */ + for(i = 0; i < 6; i++) + IMDCTwin[3][i] = 0.0f; + for(i = 6; i < 12; i++) + IMDCTwin[3][i] = (float)sin(PI12 * (i - 6 + 0.5)); + for(i = 12; i < 18; i++) + IMDCTwin[3][i] = 1.0f; + for(i = 18; i < 36; i++) + IMDCTwin[3][i] = (float)sin(PI36 * (i + 0.5)); +} + +CLayer3Decoder::~CLayer3Decoder() +{ + +} + +void CLayer3Decoder::DecodeScalefactors(unsigned long ch, unsigned long gr) +{ + const unsigned int slentab1[16] = {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4}; + const unsigned int slentab2[16] = {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}; + + int sfb, window; + + for(sfb=0; sfb<21; sfb++) + { + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = 0; + } + + for(sfb=0; sfb<13; sfb++) + { + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][0] = 0; + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][1] = 0; + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][2] = 0; + } + + if(m_MpegVer == MPEG1) + { + unsigned int slen1 = slentab1[m_fr->m_SI.grinf[gr][ch].ScalefacCompress]; + unsigned int slen2 = slentab2[m_fr->m_SI.grinf[gr][ch].ScalefacCompress]; + + if( (m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag == 1) && (m_fr->m_SI.grinf[gr][ch].BlockType == 2)) + { + if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) + { + // this is the long block + for (sfb = 0; sfb < 8; sfb++) + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen1); + + for (sfb = 3; sfb < 6; sfb++) + for (window=0; window<3; window++) + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen1); + + for (sfb = 6; sfb < 12; sfb++) + for (window=0; window<3; window++) + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen2); + + for (sfb=12,window=0; window<3; window++) + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[12][window] = 0; + } + else + { // SHORT + // this is a short block... + for(sfb=0; sfb<6; sfb++) + { + for(window=0; window<3; window++) + { + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen1); + } + } + + for(sfb=6; sfb<12; sfb++) + { + for(window=0; window<3; window++) + { + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen2); + } + } + + for(window=0; window<3; window++) + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[12][window] = 0; + } + } + else + { + for(sfb=0; sfb<6; sfb++) + { + if((m_fr->m_SI.ScfSi[ch][0] == 0) || (gr == 0)) + { + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen1); + } + else + m_fr->m_SI.grinf[1][ch].Scalefac_Long[sfb] = m_fr->m_SI.grinf[0][ch].Scalefac_Long[sfb]; + } + + for(sfb=6; sfb<11; sfb++) + { + if((m_fr->m_SI.ScfSi[ch][1] == 0) || (gr == 0)) + { + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen1); + } + else + m_fr->m_SI.grinf[1][ch].Scalefac_Long[sfb] = m_fr->m_SI.grinf[0][ch].Scalefac_Long[sfb]; + } + + for(sfb=11; sfb<16; sfb++) + { + if((m_fr->m_SI.ScfSi[ch][2] == 0) || (gr == 0)) + { + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen2); + } + else + m_fr->m_SI.grinf[1][ch].Scalefac_Long[sfb] = m_fr->m_SI.grinf[0][ch].Scalefac_Long[sfb]; + } + + for(sfb=16; sfb<21; sfb++) + { + if((m_fr->m_SI.ScfSi[ch][3] == 0) || (gr == 0)) + { + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen2); + } + else + m_fr->m_SI.grinf[1][ch].Scalefac_Long[sfb] = m_fr->m_SI.grinf[0][ch].Scalefac_Long[sfb]; + } + + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[21] = 0; + } + } + else // MPEG 2 + { + int nfsbtable [2][3][3][4] = + { + { + { + {6, 5, 5, 5}, + {9, 9, 9, 9}, + {6, 9, 9, 9} + }, + { + {6, 5, 7, 3}, + {9, 9, 12, 6}, + {6, 9, 12, 6} + }, + { + {11, 10, 0, 0}, + {18, 18, 0, 0}, + {15, 18, 0, 0} + }, + }, + { + { + { 7, 7, 7, 0}, + {12, 12, 12, 0}, + { 6, 15, 12, 0} + }, + { + { 6, 6, 6, 3}, + {12, 9, 9, 6}, + { 6, 12, 9, 6} + }, + { + { 8, 8, 5, 0}, + {15, 12, 9, 0}, + { 6, 18, 9, 0} + }, + }, + }; + + unsigned int scalefac_comp; + + unsigned int slen[4] = {0,0,0,0}; + unsigned long index1, index2, index3; + + if( (m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag == 1) && (m_fr->m_SI.grinf[gr][ch].BlockType == 2)) + { + if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) + { + index3 = 2; + } + else + { + index3 = 1; + } + } + else + { + index3 = 0; + } + + scalefac_comp = m_fr->m_SI.grinf[gr][ch].ScalefacCompress; + + if(!( ((m_ModeExt == 1) || (m_ModeExt==3)) && (ch == 1))) + { + index1 = 0; + m_fr->m_SI.grinf[gr][ch].PreFlag = 0; + + + if (scalefac_comp>=500) + { + slen[0] = ((scalefac_comp-500)/ 3)%4; + slen[1] = ((scalefac_comp-500)/ 1)%3; + slen[2] = ((scalefac_comp-500)/ 1)%1; + slen[3] = ((scalefac_comp-500)/ 1)%1; + index2 = 2; + m_fr->m_SI.grinf[gr][ch].PreFlag = 1; + } + else if (scalefac_comp>=400) + { + slen[0] = ((scalefac_comp-400)/20)%5; + slen[1] = ((scalefac_comp-400)/ 4)%5; + slen[2] = ((scalefac_comp-400)/ 1)%4; + slen[3] = ((scalefac_comp-400)/ 1)%1; + index2 = 1; + } + else + { + slen[0] = ((scalefac_comp- 0)/80)%5; + slen[1] = ((scalefac_comp- 0)/16)%5; + slen[2] = ((scalefac_comp- 0)/ 4)%4; + slen[3] = ((scalefac_comp- 0)/ 1)%4; + index2 = 0; + } + } + else + { + index1 = 1; + + m_fr->m_SI.grinf[gr][ch].PreFlag = 0; + scalefac_comp>>=1; + + if (scalefac_comp>=244) + { + slen[0] = ((scalefac_comp-244)/ 3)%4; + slen[1] = ((scalefac_comp-244)/ 1)%3; + slen[2] = ((scalefac_comp-244)/ 1)%1; + slen[3] = ((scalefac_comp-244)/ 1)%1; + index2 = 2; + } + else if (scalefac_comp>=180) + { + slen[0] = ((scalefac_comp-180)/16)%4; + slen[1] = ((scalefac_comp-180)/ 4)%4; + slen[2] = ((scalefac_comp-180)/ 1)%4; + slen[3] = ((scalefac_comp-180)/ 1)%1; + index2 = 1; + } + else + { + slen[0] = ((scalefac_comp- 0)/36)%5; + slen[1] = ((scalefac_comp- 0)/ 6)%6; + slen[2] = ((scalefac_comp- 0)/ 1)%6; + slen[3] = ((scalefac_comp- 0)/ 1)%1; + index2 = 0; + } + } + + if( (m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag == 1) && (m_fr->m_SI.grinf[gr][ch].BlockType == 2)) + { + if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) + { + } + else + { + int sfb = 0; + int window = 0; + + for(int j=0; j<4; j++) + { + for(int i=0; i 0) + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = br.GetBits(slen[j]); + else + m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window] = 0; + + window++; + if(window > 2) + { + if( (m_MpegVer != MPEG1) && ((m_ModeExt == 1) || (m_ModeExt==3)) ) + m_fr->m_SI.grinf[gr][ch].is_max[sfb] = (1<m_SI.grinf[gr][ch].Scalefac_Short[12][window] = 0; + } + } + else + { + int sfb = 0; + + for(int j=0; j<4; j++) + { + for(int i=0; i 0) + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = br.GetBits(slen[j]); + else + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] = 0; + + if( (m_MpegVer != MPEG1) && ((m_ModeExt == 1) || (m_ModeExt==3)) ) + m_fr->m_SI.grinf[gr][ch].is_max[sfb] = (1<m_SI.grinf[gr][ch].Scalefac_Long[21] = 0; + m_fr->m_SI.grinf[gr][ch].Scalefac_Long[22] = 0; + } + } +} + +bool inline CLayer3Decoder::HuffmanDecode(unsigned long TableNum, int * x, int * y, int * v, int * w) +{ + unsigned long point, error, bitsleft, treelen, linbits; + unsigned long *htptr; + + point = 0; + bitsleft = 32; + + /* Check for empty tables */ + if(g_huffman_main[TableNum].TreeLen == 0) + { + *x = *y = *v = *w = 0; + return true; + } + + treelen = g_huffman_main[TableNum].TreeLen; + linbits = g_huffman_main[TableNum].linbits; + htptr = g_huffman_main[TableNum].TableData; + + /* Start reading the Huffman code word, bit by bit */ + error = 1; + do + { + + /* Check if we've matched a code word */ + if((htptr[point] & 0xffff0000) == 0x00000000) + { + error = 0; + *x = (htptr[point] >> 4) & 0xf; + *y = htptr[point] & 0xf; + break; + } + + if(br.GetBits(1)) + { + /* Go right in tree */ + while((htptr[point] & 0xff) >= 250) + { + point += htptr[point] & 0xff; + } + point += htptr[point] & 0xff; + } + else + { + /* Go left in tree */ + while((htptr[point] >> 16) >= 250) + { + point += htptr[point] >> 16; + } + point += htptr[point] >> 16; + } + } while((--bitsleft > 0) && (point < treelen)); + + /* Check for error. */ + if(error) + { + *x = *y = *v = *w = 0; + return false; + } + + /* Process sign encodings for quadruples tables. */ + if(TableNum > 31) + { + *v = (*y >> 3) & 1; + *w = (*y >> 2) & 1; + *x = (*y >> 1) & 1; + *y = *y & 1; + + if(*v > 0) + if(br.GetBits(1)) + *v = -*v; + if(*w > 0) + if(br.GetBits(1)) + *w = -*w; + if(*x > 0) + if(br.GetBits(1)) + *x = -*x; + if(*y > 0) + if(br.GetBits(1)) + *y = -*y; + } + else + { + /* Get linbits */ + if((linbits > 0) && (*x == 15)) + { + *x += br.GetBits(linbits); + } + + /* Get sign bit */ + if(*x > 0) + { + if(br.GetBits(1)) + *x = -*x; + } + + /* Get linbits */ + if((linbits > 0) && (*y == 15)) + { + *y += br.GetBits(linbits); + } + + /* Get sign bit */ + if(*y > 0) + { + if(br.GetBits(1)) + *y = -*y; + } + } + + return true; +} + +bool CLayer3Decoder::ReadHuffman(unsigned long ch, unsigned long gr) +{ + unsigned long index = 0; + unsigned long Part23End; + unsigned long Region1; + unsigned long Region2; + unsigned long TableNumber; + + int x, y, v, w; + + + Part23End = m_Part2Start[ch] + m_fr->m_SI.grinf[gr][ch].Part23Length; + + if(m_MpegVer == MPEG1) + { + m_MixedBandLimit[ch] = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[8]; + } + else + { + m_MixedBandLimit[ch] = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[6]; + } + + if( (m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag) && (m_fr->m_SI.grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) ) + { + Region1 = m_MixedBandLimit[ch]; + Region2 = 576; + } + else + { + Region1 = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[m_fr->m_SI.grinf[gr][ch].Region0Count + 1]; + Region2 = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[m_fr->m_SI.grinf[gr][ch].Region0Count + m_fr->m_SI.grinf[gr][ch].Region1Count + 2]; + } + + if(m_fr->m_SI.grinf[gr][ch].Part23Length == 0) + { + for(index = 0; index < 576; index++) + { + is[ch][index] = 0; + } + + m_NonZero[ch] = 0; + + return true; + } + + while(index < (m_fr->m_SI.grinf[gr][ch].BigValues << 1)) + { + if(index < Region1) + { + TableNumber = m_fr->m_SI.grinf[gr][ch].TableSelect[0]; + } + else if(index < Region2) + { + TableNumber = m_fr->m_SI.grinf[gr][ch].TableSelect[1]; + } + else + { + TableNumber = m_fr->m_SI.grinf[gr][ch].TableSelect[2]; + } + + if(!HuffmanDecode(TableNumber, &x, &y, &v, &w)) + return false; + + is[ch][index++] = x; + is[ch][index++] = y; + } + + unsigned long pos = br.GetPos(); + + TableNumber = m_fr->m_SI.grinf[gr][ch].Count1Table_Select + 32; + while((index < 576) && (pos < Part23End)) + { + if(!HuffmanDecode(TableNumber, &x, &y, &v, &w)) + return false; + + is[ch][index++] = v; + is[ch][index++] = w; + is[ch][index++] = x; + is[ch][index++] = y; + + pos = br.GetPos(); + } + + + pos = br.GetPos(); + if(pos > Part23End) + { + index -= 4; + if(index < 0) + index = 0; + } + + m_NonZero[ch] = index; + + br.SetPos(Part23End); + + for(; index < 576; index++) + { + is[ch][index] = 0; + } + + return true; +} + +void CLayer3Decoder::DequantizeSample(int ch, int gr) +{ + static const int pretab[22] = { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0 }; + + unsigned long index = 0; + int window = 0; + int windowsize = 0; + int sfb = 0; + int startband = 0; + float txr = 0.0f; + float globscale = GainTable[ m_fr->m_SI.grinf[gr][ch].GlobalGain ]; + + + if(m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag && m_fr->m_SI.grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) + { + if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) + { + int endblock = 0; + // Mixed Block + + if(m_MpegVer == MPEG1) + { + endblock = 8; + } + else + { + endblock = 6; + } + + + for(sfb=0; sfbm_SI.grinf[gr][ch].ScalefacScale) * (m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] + (m_fr->m_SI.grinf[gr][ch].PreFlag * pretab[sfb]) )]; + xr[ch][index] = txr * ( pow( (float)fabs(is[ch][index]), (float)1.333333333333333) * ( is[ch][index] > 0 ? 1 : -1 ) ); + + index ++; // should be 36 at the end of all this! + } + } + + startband = 3; + } + + // Short Block + // xr[i] = sign(is[i]) * |is[i]|^(4/3) * 2 ^ 0.25 * (global_gain - 210 - 8 * subblock_gain[window]) * 2 ^ -(scalefac_multiplier * scalefac_s[band][window]) + + // short blocks are arranged as 3 windowsize windows per scalefactorband + // like this: 111 111 111 22 22 22 3 3 3 + // SFB1 SFB2 SFB3 ... etc + + for(sfb=startband; sfb < 13; sfb++) + { + windowsize = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb+1] - sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb]; + + for(window = 0; window<3; window++) + { + for(int i=0; im_SI.grinf[gr][ch].SubblockGain[window]] * PowerTableMinus05[(1 + m_fr->m_SI.grinf[gr][ch].ScalefacScale) * m_fr->m_SI.grinf[gr][ch].Scalefac_Short[sfb][window]]; + /* apply the sign(is[i]) * |is[i]| ^ (4/3) formula */ + xr[ch][index] = txr * ( pow( (float)fabs(is[ch][index]), (float)1.333333333333333) * ( is[ch][index] > 0 ? 1 : -1 ) ); + index ++; + if(index >= m_NonZero[ch]) + break; + } + } + } + } + else + { + // Long Block + // xr[i] = sign(is[i]) * | is[i] |^(4/3) * 2 ^ 0.25 * (global_gain - 210) * 2 ^ -(scalefac_multiplier * (scalefac_l[band] + preflag * pretab[band])) + + + for(sfb=0; sfb<22; sfb++) + { + windowsize = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb+1] - sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb]; + + for(int i=0; im_SI.grinf[gr][ch].ScalefacScale) * (m_fr->m_SI.grinf[gr][ch].Scalefac_Long[sfb] + (m_fr->m_SI.grinf[gr][ch].PreFlag * pretab[sfb]) )]; + xr[ch][index] = txr * ( pow( (float)fabs(is[ch][index]), (float)1.333333333333333) * ( is[ch][index] > 0 ? 1 : -1 ) ); + index ++; + if(index >= m_NonZero[ch]) + break; + } + } + } + + for(; index<576; index++) + { + xr[ch][index] = 0.0; + } +} + +void CLayer3Decoder::Reorder(unsigned int ch, unsigned int gr) +{ + if(m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag && m_fr->m_SI.grinf[gr][ch].BlockType == BLOCKTYPE_3WIN) + { + int ScaleFactorBand = 0; + int BandStart; + int BandSize; + int Window, src, dst; + int i; + + if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) + { + for(unsigned long index=0; index < m_MixedBandLimit[ch]; index++) + { + xrr[ch][index] = xr[ch][index]; + } + + + ScaleFactorBand = 4; + } + + /* short block, reorder everything */ + // this is a 3window block, we need to reorder it.... in something like this + // 111 222 333 444 555 666 777 888 999 101010 111111 121212 + // to + // 123123123 456456456 789789789 101112101112101112 + do + { + BandStart = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[ScaleFactorBand]; + BandSize = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[ScaleFactorBand+1] - BandStart; + + src = BandStart * 3; + + for(Window = 0; Window < 3; Window++) + { + dst = (BandStart * 3) + Window; + + for(i = 0; i < BandSize; i++) + { + xrr[ch][dst] = xr[ch][src]; + src++; + dst += 3; + } + } + + ScaleFactorBand++; + + } while(ScaleFactorBand < 13); + } + else + { + // long blocks + for(int index=0; index < 576; index++) + { + xrr[ch][index] = xr[ch][index]; + } + } +} + +void CLayer3Decoder::CalculateK(int index, int is_pos, int intensity_scale) +{ + // MPEG2/2.5 + + if(is_pos == 0) + { + kl[index] = 1.0; + kr[index] = 1.0; + } + else + { + + if(intensity_scale == 1) + { + if(is_pos%2==1) + { + kl[index] = (float)pow(2.0f, -( (is_pos+1) / (4)) ); + kr[index] = 1.0; + } + else + { + kl[index] = 1.0; + kr[index] = (float)pow(2.0f, -(is_pos/(4)) ); + } + } + else + { + if(is_pos%2==1) + { + kl[index] = (float)pow(2.0f, -( (is_pos+1) / (8)) ); + kr[index] = 1.0; + } + else + { + kl[index] = 1.0; + kr[index] = (float)pow(2.0f, -(is_pos/(8)) ); + } + } + } +} + +void CLayer3Decoder::Stereo(unsigned int gr) +{ + int index; + + if(m_Channels == 2) // dont bother with stereo processing unless its STEREO duuh + { + int MS_Stereo = (m_Mode == MODE_JOINT_STEREO) && (m_ModeExt & MODE_EXT_MS); + int I_Stereo = (m_Mode == MODE_JOINT_STEREO) && (m_ModeExt & MODE_EXT_IS); + + int is_pos; // intensity stereo positions + int is_valid[576]; + float is_ratio[576]; // intensity stereo left / right ratio + + int intensity_scale = (m_fr->m_SI.grinf[gr][0].ScalefacCompress % 2); + + if(I_Stereo) + { + for(index=0; index<576; index++) + is_valid[index] = 0; + + + if(m_fr->m_SI.grinf[gr][0].WindowSwitchingFlag && (m_fr->m_SI.grinf[gr][0].BlockType == BLOCKTYPE_3WIN)) + { + if(m_fr->m_SI.grinf[gr][0].MixedBlockFlag) + { +// printf("Mixed Block - Arrgh"); + } + else + { + unsigned long startband = 0; + while((unsigned long)(sfBandIndex[m_MpegVer][m_SampleFrequency].Short[startband]*3) < m_NonZero[1]) + startband ++; + + for(int sfb=startband; sfb<13; sfb++) + { + int windowsize = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb+1] - sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb]; + + for(int window=0; window<3; window++) + { + int sfb_start = sfBandIndex[m_MpegVer][m_SampleFrequency].Short[sfb]*3 + windowsize*window; + int sfb_stop = sfb_start + windowsize; + + for(int i = sfb_start; i < sfb_stop; i++) + { + is_pos = m_fr->m_SI.grinf[gr][1].Scalefac_Short[ sfb ][window]; + + if(m_MpegVer == MPEG1) + { + if (is_pos != 7) + { + is_ratio[ i ] = TanPi12Table[ is_pos ]; + is_valid[ i ] = 1; + } + } + else + { + if(is_pos != m_fr->m_SI.grinf[gr][1].is_max[ sfb ] ) + { + CalculateK(i, is_pos, intensity_scale); + is_valid[ i ] = 1; + } + } + } + } + } + } + } + else + { + int i; + + int startband = 0; + + while(sfBandIndex[m_MpegVer][m_SampleFrequency].Long[startband] < m_NonZero[1]) + startband ++; + + for(int sfb = startband; sfb<22; sfb++) + { + i = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb]; + + int windowsize = sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb+1] - sfBandIndex[m_MpegVer][m_SampleFrequency].Long[sfb]; + + for(int w=0; wm_SI.grinf[gr][1].Scalefac_Long[ sfb ]; + + if(m_MpegVer == MPEG1) + { + if (is_pos != 7) + { + is_ratio[ i ] = TanPi12Table[ is_pos ]; + is_valid[ i ] = 1; + } + } + else + { + if(is_pos != m_fr->m_SI.grinf[gr][1].is_max[ sfb ] ) + { + CalculateK(i, is_pos, intensity_scale); + is_valid[ i ] = 1; + } + } + + i++; + } + } + } + + // process the intensity stere stuff + for(index=0; index<576; index++) + { + float temp = xrr[0][index]; + + if( is_valid[index] == 0 ) + { + // NOT intensity Stereo Mode + if(MS_Stereo) + { + // MSStereo mode. we have to undo it... + xrr[0][index] = (temp + xrr[1][index]) / SQRT2; + xrr[1][index] = (temp - xrr[1][index]) / SQRT2; + } + } + else + { + // this IS an intensity stereo value + if(m_MpegVer == MPEG1) + { + xrr[0][index] = temp * (is_ratio[index] / (1.0f + is_ratio[index])); + xrr[1][index] = temp * (1.0f / (1.0f + is_ratio[index])); + } + else + { + xrr[0][index] = temp * kl[index]; + xrr[1][index] = temp * kr[index]; + } + } + } + } + else if(MS_Stereo) + { + // MSStereo mode. we have to undo it... + for(index=0; index<576; index++) + { + float temp = xrr[0][index]; + xrr[0][index] = (temp + xrr[1][index]) / SQRT2; + xrr[1][index] = (temp - xrr[1][index]) / SQRT2; + } + } + } +} + +void CLayer3Decoder::AntiAlias(unsigned int ch, unsigned int gr) +{ + int sb_amount; + + int index =0; + + if(m_fr->m_SI.grinf[gr][ch].BlockType != BLOCKTYPE_3WIN) + { + sb_amount = 558; // antialias the full stectrum + } + else + { + if(m_fr->m_SI.grinf[gr][ch].MixedBlockFlag) + { // this is a long block, then a short block + sb_amount = m_MixedBandLimit[ch]; + } + else // No antialiasing for short blocks + { + return; + } + } + + int src_idx1; + int src_idx2; + + register float temp; + + for(int sb = 18; sb < sb_amount; sb+=18) + { + for(int i=0; i<8; i++) + { + src_idx1 = sb - 1 - i; + src_idx2 = sb + i; + + temp = xrr[ch][src_idx1]; + + xrr[ch][src_idx1] = (temp * Cs[i]) - (xrr[ch][src_idx2] * Ca[i]); + xrr[ch][src_idx2] = (xrr[ch][src_idx2] * Cs[i]) + (temp * Ca[i]); + } + } + +} + +void CLayer3Decoder::FreqencyInverse(int gr, int ch) +{ + int sb, ss; + + int offset; + + for(sb = 1; sb < 32; sb+=2) + { + offset = 32; + for(ss=1; ss<18; ss+=2) + { + xir[ch][offset+sb] = -xir[ch][offset+sb]; + offset += 64; + } + } +} + +void CLayer3Decoder::IMDCT(float *in, float *out, int block_type) +{ + if(block_type == 2) + { + float tmp[18]; + register float sum; + register float save; + register float pp1; + + int six_i = 6; + int window; + + for(int i=0; i<36; i++) + { + out[i] = 0.0; + } + + for(window=0; window<3; window++) + { + in[15+window] += in[12+window]; + in[12+window] += in[9+window]; + in[9+window] += in[6+window]; + in[6+window] += in[3+window]; + in[3+window] += in[window]; + + in[15+window] += in[9+window]; + in[9+window] += in[3+window]; + + pp1 = in[6+window] * ShortTwiddles[0]; + sum = in[window] + in[12+window] * ShortTwiddles[1]; + + tmp[1] = in[window] - in[12+window]; + tmp[0] = sum + pp1; + tmp[2] = sum - pp1; + + pp1 = in[9+window] * ShortTwiddles[0]; + sum = in[3+window] + in[15+window] * ShortTwiddles[1]; + + tmp[4] = in[3+window] - in[15+window]; + tmp[5] = sum + pp1; + tmp[3] = sum - pp1; + + tmp[3] *= ShortTwiddles[2]; + tmp[4] *= ShortTwiddles[3]; + tmp[5] *= ShortTwiddles[4]; + + save = tmp[0]; + tmp[0] += tmp[5]; + tmp[5] = save - tmp[5]; + + save = tmp[1]; + tmp[1] += tmp[4]; + tmp[4] = save - tmp[4]; + + save = tmp[2]; + tmp[2] += tmp[3]; + tmp[3] = save - tmp[3]; + + tmp[0] *= ShortTwiddles[5]; + tmp[1] *= ShortTwiddles[6]; + tmp[2] *= ShortTwiddles[7]; + tmp[3] *= ShortTwiddles[8]; + tmp[4] *= ShortTwiddles[9]; + tmp[5] *= ShortTwiddles[10]; + + tmp[6] = -tmp[2] * ShortTwiddles[15]; + tmp[7] = -tmp[1] * ShortTwiddles[13]; + tmp[8] = -tmp[0] * ShortTwiddles[11]; + tmp[9] = -tmp[0] * ShortTwiddles[12]; + tmp[10] = -tmp[1] * ShortTwiddles[14]; + tmp[11] = -tmp[2] * ShortTwiddles[16]; + + tmp[0] = tmp[3]; + tmp[1] = tmp[4] * ShortTwiddles[17]; + tmp[2] = tmp[5] * ShortTwiddles[18]; + + tmp[3] = -tmp[5] * ShortTwiddles[19]; + tmp[4] = -tmp[4] * ShortTwiddles[20]; + tmp[5] = -tmp[0] * ShortTwiddles[21]; + + tmp[0] *= ShortTwiddles[22]; + + out[six_i] += tmp[0]; + out[six_i + 1] += tmp[1]; + out[six_i + 2] += tmp[2]; + out[six_i + 3] += tmp[3]; + out[six_i + 4] += tmp[4]; + out[six_i + 5] += tmp[5]; + out[six_i + 6] += tmp[6]; + out[six_i + 7] += tmp[7]; + out[six_i + 8] += tmp[8]; + out[six_i + 9] += tmp[9]; + out[six_i + 10] += tmp[10]; + out[six_i + 11] += tmp[11]; + + six_i += 6; + } + } + else + { + float tmp[18]; + + int i, j; + register float sum; + register float sum2; + register float save; + + in[17] += in[16]; + in[16] += in[15]; + in[15] += in[14]; + in[14] += in[13]; + in[13] += in[12]; + in[12] += in[11]; + in[11] += in[10]; + in[10] += in[9]; + in[9] += in[8]; + in[8] += in[7]; + in[7] += in[6]; + in[6] += in[5]; + in[5] += in[4]; + in[4] += in[3]; + in[3] += in[2]; + in[2] += in[1]; + in[1] += in[0]; + + in[17] += in[15]; + in[15] += in[13]; + in[13] += in[11]; + in[11] += in[9]; + in[9] += in[7]; + in[7] += in[5]; + in[5] += in[3]; + in[3] += in[1]; + + j = 0; + i = 0; + + int b = 9; + do + { + sum = in[0]; + sum2 = in[1]; + + sum += in[2] * IMDCT9x8Table[j]; + sum2 += in[3] * IMDCT9x8Table[j]; + sum += in[4] * IMDCT9x8Table[j+1]; + sum2 += in[5] * IMDCT9x8Table[j+1]; + sum += in[6] * IMDCT9x8Table[j+2]; + sum2 += in[7] * IMDCT9x8Table[j+2]; + sum += in[8] * IMDCT9x8Table[j+3]; + sum2 += in[9] * IMDCT9x8Table[j+3]; + sum += in[10] * IMDCT9x8Table[j+4]; + sum2 += in[11] * IMDCT9x8Table[j+4]; + sum += in[12] * IMDCT9x8Table[j+5]; + sum2 += in[13] * IMDCT9x8Table[j+5]; + sum += in[14] * IMDCT9x8Table[j+6]; + sum2 += in[15] * IMDCT9x8Table[j+6]; + sum += in[16] * IMDCT9x8Table[j+7]; + sum2 += in[17] * IMDCT9x8Table[j+7]; + + tmp[i] = sum; + tmp[17-i] = sum2; + + j += 8; + i++; + + } while(--b); + + tmp[9] *= NormalTwiddles[0]; + tmp[10] *= NormalTwiddles[1]; + tmp[11] *= NormalTwiddles[2]; + tmp[12] *= NormalTwiddles[3]; + tmp[13] *= NormalTwiddles[4]; + tmp[14] *= NormalTwiddles[5]; + tmp[15] *= NormalTwiddles[6]; + tmp[16] *= NormalTwiddles[7]; + tmp[17] *= NormalTwiddles[8]; + + for(i = 0; i < 9; i++) + { + save = tmp[i]; + tmp[i] += tmp[17-i]; + tmp[17-i] = save - tmp[17-i]; + } + + tmp[0] *= NormalTwiddles[9]; + tmp[1] *= NormalTwiddles[10]; + tmp[2] *= NormalTwiddles[11]; + tmp[3] *= NormalTwiddles[12]; + tmp[4] *= NormalTwiddles[13]; + tmp[5] *= NormalTwiddles[14]; + tmp[6] *= NormalTwiddles[15]; + tmp[7] *= NormalTwiddles[16]; + tmp[8] *= NormalTwiddles[17]; + tmp[9] *= NormalTwiddles[18]; + tmp[10] *= NormalTwiddles[19]; + tmp[11] *= NormalTwiddles[20]; + tmp[12] *= NormalTwiddles[21]; + tmp[13] *= NormalTwiddles[22]; + tmp[14] *= NormalTwiddles[23]; + tmp[15] *= NormalTwiddles[24]; + tmp[16] *= NormalTwiddles[25]; + tmp[17] *= NormalTwiddles[26]; + + for(i = 0; i < 9; i++) + { + out[i] = -tmp[i+9] * IMDCTwin[block_type][i]; + out[i+9] = tmp[17-i] * IMDCTwin[block_type][i+9]; + out[i+18] = tmp[8-i] * IMDCTwin[block_type][i+18]; + out[i+27] = tmp[i] * IMDCTwin[block_type][i+27]; + } + } +} + +void CLayer3Decoder::Hybrid(int ch, float *xfrom, float *xto, int blocktype, int windowswitching, int mixedblock) +{ + float rawout[36]; + unsigned int bt; + int sb18 = 0; + int sb = 0; + + int x = 32; + + do + { + bt = (windowswitching && mixedblock && (sb18 < 36)) ? 0 : blocktype; + + IMDCT(&xfrom[sb18], rawout, bt); + + xto[sb] = rawout[ 0] + prevblck[ch][sb18+0]; + xto[sb+32] = rawout[ 1] + prevblck[ch][sb18+1]; + xto[sb+64] = rawout[ 2] + prevblck[ch][sb18+2]; + xto[sb+96] = rawout[ 3] + prevblck[ch][sb18+3]; + xto[sb+128] = rawout[ 4] + prevblck[ch][sb18+4]; + xto[sb+160] = rawout[ 5] + prevblck[ch][sb18+5]; + xto[sb+192] = rawout[ 6] + prevblck[ch][sb18+6]; + xto[sb+224] = rawout[ 7] + prevblck[ch][sb18+7]; + xto[sb+256] = rawout[ 8] + prevblck[ch][sb18+8]; + xto[sb+288] = rawout[ 9] + prevblck[ch][sb18+9]; + xto[sb+320] = rawout[10] + prevblck[ch][sb18+10]; + xto[sb+352] = rawout[11] + prevblck[ch][sb18+11]; + xto[sb+384] = rawout[12] + prevblck[ch][sb18+12]; + xto[sb+416] = rawout[13] + prevblck[ch][sb18+13]; + xto[sb+448] = rawout[14] + prevblck[ch][sb18+14]; + xto[sb+480] = rawout[15] + prevblck[ch][sb18+15]; + xto[sb+512] = rawout[16] + prevblck[ch][sb18+16]; + xto[sb+544] = rawout[17] + prevblck[ch][sb18+17]; + + prevblck[ch][sb18+0] = rawout[18]; + prevblck[ch][sb18+1] = rawout[19]; + prevblck[ch][sb18+2] = rawout[20]; + prevblck[ch][sb18+3] = rawout[21]; + prevblck[ch][sb18+4] = rawout[22]; + prevblck[ch][sb18+5] = rawout[23]; + prevblck[ch][sb18+6] = rawout[24]; + prevblck[ch][sb18+7] = rawout[25]; + prevblck[ch][sb18+8] = rawout[26]; + prevblck[ch][sb18+9] = rawout[27]; + prevblck[ch][sb18+10] = rawout[28]; + prevblck[ch][sb18+11] = rawout[29]; + prevblck[ch][sb18+12] = rawout[30]; + prevblck[ch][sb18+13] = rawout[31]; + prevblck[ch][sb18+14] = rawout[32]; + prevblck[ch][sb18+15] = rawout[33]; + prevblck[ch][sb18+16] = rawout[34]; + prevblck[ch][sb18+17] = rawout[35]; + + sb++; + sb18+=18; + + } while(--x); +} + +bool CLayer3Decoder::ProcessFrame(Frame * fr, float * PCMSamples, unsigned long * NumSamples) +{ + unsigned long ch, gr; + + m_fr = fr; + + if(NumSamples) + *NumSamples = 0; + + m_MpegVer = fr->m_Header.GetMpegVersion(); + m_Mode = fr->m_Header.GetMode(); + m_ModeExt = fr->m_Header.GetModeExtension(); + m_Channels = fr->m_Header.GetChannels(); + m_SampleFrequency = fr->m_Header.GetSampleFrequencyIndex(); + m_Granules = (m_MpegVer) == MPEG1 ? 2 : 1; + + br.FillBitReserve(m_fr->m_Data, m_fr->m_Header.GetDataSize()); + + if(!br.BackStep(m_fr->m_SI.MainDataBegin)) + { + return(false); + } + + if(!PCMSamples) + { + return(true); + } + + for(gr=0; grm_SI.grinf[gr][ch].BlockType, m_fr->m_SI.grinf[gr][ch].WindowSwitchingFlag, m_fr->m_SI.grinf[gr][ch].MixedBlockFlag); + FreqencyInverse(gr, ch); + } + + if(m_Channels == 1) + { + for(int ss=0; ss<576; ss+=32) + { + PerformSynthesis(&xir[0][ss], ((float*)PCMSamples)+*NumSamples, 0, 1); + + *NumSamples += 32; + } + } + else + { + for(int ss=0; ss<576; ss+=32) + { + PerformSynthesis(&xir[0][ss], ((float*)PCMSamples)+*NumSamples, 0, 2); + PerformSynthesis(&xir[1][ss], ((float*)PCMSamples)+*NumSamples, 1, 2); + + *NumSamples += 64; + } + } + } + + return(true); +} diff --git a/src/contrib/mp3decoder/Mp3Decoder.cpp b/src/contrib/mp3decoder/Mp3Decoder.cpp index e08ecc7a3..fd6495059 100644 --- a/src/contrib/mp3decoder/Mp3Decoder.cpp +++ b/src/contrib/mp3decoder/Mp3Decoder.cpp @@ -1,280 +1,280 @@ -#include "StdAfx.h" -#include -#include -#include -#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 +#include +#include +#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; +} diff --git a/src/contrib/mp3decoder/Mp3DecoderFactory.cpp b/src/contrib/mp3decoder/Mp3DecoderFactory.cpp index 9926c9a29..74fb7d50e 100644 --- a/src/contrib/mp3decoder/Mp3DecoderFactory.cpp +++ b/src/contrib/mp3decoder/Mp3DecoderFactory.cpp @@ -1,71 +1,71 @@ -////////////////////////////////////////////////////////////////////////////// -// Copyright � 2007, 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 "boost/algorithm/string.hpp" -#include "boost/filesystem.hpp" - -#include "Mp3DecoderFactory.h" -#include "Mp3Decoder.h" - -#include - -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 � 2007, 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 "boost/algorithm/string.hpp" +#include "boost/filesystem.hpp" + +#include "Mp3DecoderFactory.h" +#include "Mp3Decoder.h" + +#include + +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; +} diff --git a/src/contrib/mp3decoder/SideInfo.cpp b/src/contrib/mp3decoder/SideInfo.cpp index 3bbbfb7c5..82f872a97 100644 --- a/src/contrib/mp3decoder/SideInfo.cpp +++ b/src/contrib/mp3decoder/SideInfo.cpp @@ -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; chGetChannels(); 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; chGetChannels(); 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; chGetChannels(); 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; chGetChannels(); 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; chGetChannels(); 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; chGetChannels(); 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); +} diff --git a/src/contrib/mp3decoder/mp3decoder_plugin.cpp b/src/contrib/mp3decoder/mp3decoder_plugin.cpp index df821dcc7..b3da11f44 100644 --- a/src/contrib/mp3decoder/mp3decoder_plugin.cpp +++ b/src/contrib/mp3decoder/mp3decoder_plugin.cpp @@ -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 -#include -#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 +#include +#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(); +} diff --git a/src/contrib/mp3decoder/stdafx.cpp b/src/contrib/mp3decoder/stdafx.cpp index 6ef56ea92..38853f257 100644 --- a/src/contrib/mp3decoder/stdafx.cpp +++ b/src/contrib/mp3decoder/stdafx.cpp @@ -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 diff --git a/src/contrib/mpg123decoder/Mpg123Decoder.cpp b/src/contrib/mpg123decoder/Mpg123Decoder.cpp index adc872c66..98f32ed2d 100644 --- a/src/contrib/mpg123decoder/Mpg123Decoder.cpp +++ b/src/contrib/mpg123decoder/Mpg123Decoder.cpp @@ -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 - -#define STREAM_FEED_SIZE 2048 * 8 -#define DEBUG 0 - -#if DEBUG > 0 -#include -#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 + +#define STREAM_FEED_SIZE 2048 * 8 +#define DEBUG 0 + +#if DEBUG > 0 +#include +#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; +} diff --git a/src/contrib/mpg123decoder/Mpg123DecoderFactory.cpp b/src/contrib/mpg123decoder/Mpg123DecoderFactory.cpp index 5e2dfce28..45bf351dd 100644 --- a/src/contrib/mpg123decoder/Mpg123DecoderFactory.cpp +++ b/src/contrib/mpg123decoder/Mpg123DecoderFactory.cpp @@ -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 -#include -#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 +#include +#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; +} diff --git a/src/contrib/mpg123decoder/mpg123decoder_plugin.cpp b/src/contrib/mpg123decoder/mpg123decoder_plugin.cpp index d7c47ff24..17de3dd62 100644 --- a/src/contrib/mpg123decoder/mpg123decoder_plugin.cpp +++ b/src/contrib/mpg123decoder/mpg123decoder_plugin.cpp @@ -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 -#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 +#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(); +} diff --git a/src/contrib/mpg123decoder/stdafx.cpp b/src/contrib/mpg123decoder/stdafx.cpp index 81baa09cb..e11ae5fce 100644 --- a/src/contrib/mpg123decoder/stdafx.cpp +++ b/src/contrib/mpg123decoder/stdafx.cpp @@ -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" diff --git a/src/contrib/oggdecoder/OggDecoder.cpp b/src/contrib/oggdecoder/OggDecoder.cpp index 99782a624..1a2d27206 100644 --- a/src/contrib/oggdecoder/OggDecoder.cpp +++ b/src/contrib/oggdecoder/OggDecoder.cpp @@ -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 - -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 + +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; +} diff --git a/src/contrib/oggdecoder/OggDecoderFactory.cpp b/src/contrib/oggdecoder/OggDecoderFactory.cpp index b0e114f76..e07d3dcb8 100644 --- a/src/contrib/oggdecoder/OggDecoderFactory.cpp +++ b/src/contrib/oggdecoder/OggDecoderFactory.cpp @@ -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 -#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 +#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; +} diff --git a/src/contrib/oggdecoder/oggdecoder_plugin.cpp b/src/contrib/oggdecoder/oggdecoder_plugin.cpp index bab63b623..4f58821d3 100644 --- a/src/contrib/oggdecoder/oggdecoder_plugin.cpp +++ b/src/contrib/oggdecoder/oggdecoder_plugin.cpp @@ -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 -#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 +#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(); +} diff --git a/src/contrib/oggdecoder/stdafx.cpp b/src/contrib/oggdecoder/stdafx.cpp index 4ad77dad7..83e24d2a8 100644 --- a/src/contrib/oggdecoder/stdafx.cpp +++ b/src/contrib/oggdecoder/stdafx.cpp @@ -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" \ No newline at end of file diff --git a/src/contrib/stdout/stdout_plugin.cpp b/src/contrib/stdout/stdout_plugin.cpp index 346b11c5e..73b810fbe 100644 --- a/src/contrib/stdout/stdout_plugin.cpp +++ b/src/contrib/stdout/stdout_plugin.cpp @@ -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 -#include - -#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 +#include + +#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(); + } +} diff --git a/src/contrib/taglib_plugin/TaglibMetadataReader.cpp b/src/contrib/taglib_plugin/TaglibMetadataReader.cpp index 92110ed5a..268176112 100644 --- a/src/contrib/taglib_plugin/TaglibMetadataReader.cpp +++ b/src/contrib/taglib_plugin/TaglibMetadataReader.cpp @@ -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 - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#else - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#endif - -#include -#include -#include -#include -#include - -#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 - -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 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 (*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(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 splitValues; - - boost::algorithm::split(splitValues, value, boost::algorithm::is_any_of("/")); - - std::vector::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 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +#include +#include +#include +#include +#include + +#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 + +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 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 (*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(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 splitValues; + + boost::algorithm::split(splitValues, value, boost::algorithm::is_any_of("/")); + + std::vector::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); + } + } +} diff --git a/src/contrib/taglib_plugin/stdafx.cpp b/src/contrib/taglib_plugin/stdafx.cpp index 81baa09cb..e11ae5fce 100644 --- a/src/contrib/taglib_plugin/stdafx.cpp +++ b/src/contrib/taglib_plugin/stdafx.cpp @@ -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" diff --git a/src/contrib/taglib_plugin/taglib_plugin.cpp b/src/contrib/taglib_plugin/taglib_plugin.cpp index 075b621ce..3a454d5a8 100644 --- a/src/contrib/taglib_plugin/taglib_plugin.cpp +++ b/src/contrib/taglib_plugin/taglib_plugin.cpp @@ -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(); +} diff --git a/src/contrib/waveout/WaveOut.cpp b/src/contrib/waveout/WaveOut.cpp index ae590c233..5b38a67c1 100644 --- a/src/contrib/waveout/WaveOut.cpp +++ b/src/contrib/waveout/WaveOut.cpp @@ -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 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 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; +} diff --git a/src/contrib/waveout/WaveOutBuffer.cpp b/src/contrib/waveout/WaveOutBuffer.cpp index f9dffbd13..621f7bb0b 100644 --- a/src/contrib/waveout/WaveOutBuffer.cpp +++ b/src/contrib/waveout/WaveOutBuffer.cpp @@ -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 - -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 + +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; +} + diff --git a/src/contrib/waveout/pch.cpp b/src/contrib/waveout/pch.cpp index c83de61e8..9e3cc1e2e 100644 --- a/src/contrib/waveout/pch.cpp +++ b/src/contrib/waveout/pch.cpp @@ -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" \ No newline at end of file diff --git a/src/contrib/waveout/waveout_plugin.cpp b/src/contrib/waveout/waveout_plugin.cpp index 33560c939..612fd4165 100644 --- a/src/contrib/waveout/waveout_plugin.cpp +++ b/src/contrib/waveout/waveout_plugin.cpp @@ -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 -#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 +#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(); +} diff --git a/src/core/audio/Player.cpp b/src/core/audio/Player.cpp index 678ba85ee..b603cb8f8 100644 --- a/src/core/audio/Player.cpp +++ b/src/core/audio/Player.cpp @@ -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 -#include -#include -#include - -#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 OutputVector; - - OutputVector outputs = musik::core::PluginFactory::Instance().QueryInterface< - IOutput, musik::core::PluginFactory::DestroyDeleter >("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 +#include +#include +#include + +#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 OutputVector; + + OutputVector outputs = musik::core::PluginFactory::Instance().QueryInterface< + IOutput, musik::core::PluginFactory::DestroyDeleter >("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); + } +} diff --git a/src/core/audio/Stream.cpp b/src/core/audio/Stream.cpp index 2ea2955c1..2461d5b00 100644 --- a/src/core/audio/Stream.cpp +++ b/src/core/audio/Stream.cpp @@ -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 -#include -#include -#include - -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 Deleter; - this->dsps = PluginFactory::Instance().QueryInterface("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 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 typedef Deleter; - - this->decoderFactories = PluginFactory::Instance() - .QueryInterface("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 +#include +#include +#include + +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 Deleter; + this->dsps = PluginFactory::Instance().QueryInterface("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 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 typedef Deleter; + + this->decoderFactories = PluginFactory::Instance() + .QueryInterface("GetDecoderFactory"); +} diff --git a/src/core/db/CachedStatement.cpp b/src/core/db/CachedStatement.cpp index 3dc487d02..3984746ea 100644 --- a/src/core/db/CachedStatement.cpp +++ b/src/core/db/CachedStatement.cpp @@ -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 -#include -#include -#include - -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 +#include +#include +#include + +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; +} + diff --git a/src/core/db/Connection.cpp b/src/core/db/Connection.cpp index a763352f3..e43dd594f 100644 --- a/src/core/db/Connection.cpp +++ b/src/core/db/Connection.cpp @@ -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 -#include -#include -#include - -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(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 +#include +#include +#include + +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(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); +} diff --git a/src/core/db/ScopedTransaction.cpp b/src/core/db/ScopedTransaction.cpp index c3a77a0e4..2206cede3 100644 --- a/src/core/db/ScopedTransaction.cpp +++ b/src/core/db/ScopedTransaction.cpp @@ -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 -#include - -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 +#include + +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"); + } + } +} + + diff --git a/src/core/db/Statement.cpp b/src/core/db/Statement.cpp index 8bb2621d6..1e29f8bf9 100644 --- a/src/core/db/Statement.cpp +++ b/src/core/db/Statement.cpp @@ -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 -#include -#include - -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 +#include +#include + +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""; +} diff --git a/src/core/io/DataStreamFactory.cpp b/src/core/io/DataStreamFactory.cpp index 78de85bc1..05c5c0680 100644 --- a/src/core/io/DataStreamFactory.cpp +++ b/src/core/io/DataStreamFactory.cpp @@ -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 -#include -#include -#include - -using namespace musik::core::io; - -DataStreamFactory::DataStreamFactory() { - typedef IDataStreamFactory PluginType; - typedef musik::core::PluginFactory::DestroyDeleter Deleter; - - this->dataStreamFactories = musik::core::PluginFactory::Instance() - .QueryInterface("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 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 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 +#include +#include +#include + +using namespace musik::core::io; + +DataStreamFactory::DataStreamFactory() { + typedef IDataStreamFactory PluginType; + typedef musik::core::PluginFactory::DestroyDeleter Deleter; + + this->dataStreamFactories = musik::core::PluginFactory::Instance() + .QueryInterface("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 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 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; +} diff --git a/src/core/io/LocalFileStream.cpp b/src/core/io/LocalFileStream.cpp index 030ceef60..45e1ae985 100644 --- a/src/core/io/LocalFileStream.cpp +++ b/src/core/io/LocalFileStream.cpp @@ -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 -#include -#include -#include -#include - -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 +#include +#include +#include +#include + +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(); +} diff --git a/src/core/library/Indexer.cpp b/src/core/library/Indexer.cpp index 2f0bec050..cce3be083 100644 --- a/src/core/library/Indexer.cpp +++ b/src/core/library/Indexer.cpp @@ -30,661 +30,661 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "pch.hpp" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static const std::string TAG = "Indexer"; - -using namespace musik::core; -using namespace musik::core::metadata; -using namespace musik::core::audio; - -static std::string normalizeDir(std::string path) { - path = boost::filesystem::path(path).make_preferred().string(); - - std::string sep(1, boost::filesystem::path::preferred_separator); - if (path.substr(path.size() - 1, 1) != sep) { - path += sep; - } - - return path; -} - -static std::string normalizePath(const std::string& path) { - return boost::filesystem::path(path).make_preferred().string(); -} - -Indexer::Indexer(const std::string& libraryPath, const std::string& dbFilename) -: thread(NULL) -, status(0) -, restart(false) -, filesIndexed(0) -, filesSaved(0) { - this->dbFilename = dbFilename; - this->libraryPath = libraryPath; - this->thread = new boost::thread(boost::bind(&Indexer::ThreadLoop, this)); -} - -////////////////////////////////////////// -///\brief -///Destructor -/// -///Exits and joins threads -////////////////////////////////////////// -Indexer::~Indexer(){ - if (this->thread) { - this->Exit(); - this->thread->join(); - delete this->thread; - this->thread = NULL; - } -} - -////////////////////////////////////////// -///\brief -///Restart the sync -/// -///\param bNewRestart -///Should if be restarted or not -////////////////////////////////////////// -void Indexer::Synchronize(bool restart) { - boost::mutex::scoped_lock lock(this->exitMutex); - this->restart = restart; - this->Notify(); -} - -////////////////////////////////////////// -///\brief -///Should the sync be restarted? -////////////////////////////////////////// -bool Indexer::Restarted() { - boost::mutex::scoped_lock lock(this->exitMutex); - return this->restart; -} - -////////////////////////////////////////// -///\brief -///Add a new path to the Indexer. -/// -///\param sPath -///Path to add -/// -///\remarks -///If the path already exists it will not be added. -////////////////////////////////////////// -void Indexer::AddPath(const std::string& path) { - Indexer::AddRemoveContext context; - context.add = true; - context.path = normalizeDir(path); - - { - boost::mutex::scoped_lock lock(this->exitMutex); - this->addRemoveQueue.push_back(context); - } - - this->Synchronize(); -} - -////////////////////////////////////////// -///\brief -///Remove a path from the Indexer -/// -///\param sPath -///Path to remove -////////////////////////////////////////// -void Indexer::RemovePath(const std::string& path) { - Indexer::AddRemoveContext context; - context.add = false; - context.path = normalizeDir(path); - - { - boost::mutex::scoped_lock lock(this->exitMutex); - this->addRemoveQueue.push_back(context); - } - - this->Synchronize(); -} - -////////////////////////////////////////// -///\brief -///Main method for doing the synchronization. -////////////////////////////////////////// -void Indexer::SynchronizeInternal() { - /* load all of the metadata (tag) reader plugins */ - typedef PluginFactory::DestroyDeleter MetadataDeleter; - typedef PluginFactory::DestroyDeleter DecoderDeleter; - - this->metadataReaders = PluginFactory::Instance() - .QueryInterface("GetMetadataReader"); - - this->audioDecoders = PluginFactory::Instance() - .QueryInterface("GetDecoderFactory"); - - { - boost::mutex::scoped_lock lock(this->progressMutex); - this->filesIndexed = 0; - } - - this->ProcessAddRemoveQueue(); - - /* get sync paths and ids from the db */ - - std::vector paths; - std::vector pathIds; - - { - db::Statement stmt("SELECT id, path FROM paths", this->dbConnection); - - while (stmt.Step() == db::Row) { - try { - DBID id = stmt.ColumnInt(0); - std::string path = stmt.ColumnText(1); - boost::filesystem::path dir(path); - - if (boost::filesystem::exists(dir)) { - paths.push_back(path); - pathIds.push_back(id); - } - } - catch(...) { - } - } - } - - /* add new files */ - - { - boost::mutex::scoped_lock lock(this->progressMutex); - this->status = 2; - this->filesSaved = 0; - } - - for(std::size_t i = 0; i < paths.size(); ++i) { - std::string path = paths[i]; - this->SyncDirectory(path, path, pathIds[i]); - } - - /* remove undesired entries from db (files themselves will remain) */ - - musik::debug::info(TAG, "cleanup 1/2"); - - { - boost::mutex::scoped_lock lock(this->progressMutex); - this->status = 3; - } - - if (!this->Restarted() && !this->Exited()) { - this->SyncDelete(); - } - - /* cleanup -- remove stale artists, albums, genres, etc */ - - musik::debug::info(TAG, "cleanup 2/2"); - - { - boost::mutex::scoped_lock lock(this->progressMutex); - this->status = 4; - } - - - if(!this->Restarted() && !this->Exited()){ - this->SyncCleanup(); - } - - /* optimize */ - - musik::debug::info(TAG, "optimizing"); - - { - boost::mutex::scoped_lock lock(this->progressMutex); - this->status = 5; - } - - if(!this->Restarted() && !this->Exited()){ - this->SyncOptimize(); - } - - /* unload reader DLLs*/ - - this->metadataReaders.clear(); - - { - boost::mutex::scoped_lock lock(this->progressMutex); - this->status = 0; - } - - musik::debug::info(TAG, "done!"); -} - -void Indexer::SyncDirectory( - const std::string &syncRoot, - const std::string ¤tPath, - DBID pathId) -{ - if (this->Exited() || this->Restarted()) { - return; - } - - std::string normalizedSyncRoot = normalizeDir(syncRoot); - std::string normalizedCurrentPath = normalizeDir(currentPath); - std::string leaf = boost::filesystem::path(currentPath).leaf().string(); /* trailing subdir in currentPath */ - - /* start recursive filesystem scan */ - - try { /* boost::filesystem may throw */ - - /* for each file in the current path... */ - - boost::filesystem::path path(currentPath); - boost::filesystem::directory_iterator end; - boost::filesystem::directory_iterator file(path); - - std::string pathIdStr = boost::lexical_cast(pathId); - - for( ; file != end && !this->Exited() && !this->Restarted(); file++) { - if (is_directory(file->status())) { - /* recursion here */ - musik::debug::info(TAG, "scanning " + file->path().string()); - this->SyncDirectory(syncRoot, file->path().string(), pathId); - } - else { - ++this->filesIndexed; - - musik::core::IndexerTrack track(0); - - /* get cached filesize, parts, size, etc */ - if (track.NeedsToBeIndexed(file->path(), this->dbConnection)) { - bool saveToDb = false; - - /* read the tag from the plugin */ - typedef MetadataReaderList::iterator Iterator; - Iterator it = this->metadataReaders.begin(); - while (it != this->metadataReaders.end()) { - if ((*it)->CanRead(track.GetValue("extension").c_str())) { - if ((*it)->Read(file->path().string().c_str(), &track)) { - saveToDb = true; - break; - } - } - it++; - } - - /* no tag? well... if a decoder can play it, add it to the database - with the file as the name. */ - if (!saveToDb) { - std::string fullPath = file->path().string(); - auto it = this->audioDecoders.begin(); - while (it != this->audioDecoders.end()) { - if ((*it)->CanHandle(fullPath.c_str())) { - saveToDb = true; - track.SetValue("title", file->path().leaf().string().c_str()); - break; - } - ++it; - } - } - - /* write it to the db, if read successfully */ - if (saveToDb) { - track.SetValue("path_id", pathIdStr.c_str()); - track.Save(this->dbConnection, this->libraryPath); - - this->filesSaved++; - if (this->filesSaved % 100 == 0) { - this->TrackRefreshed(); /* no idea... something listens to this. maybe?*/ - } - } - } - } - } - } - catch(...) { - } -} - -////////////////////////////////////////// -///\brief -///Main loop the thread is running in. -////////////////////////////////////////// -void Indexer::ThreadLoop() { - boost::filesystem::path thumbPath(this->libraryPath + "thumbs/"); - - if (!boost::filesystem::exists(thumbPath)) { - boost::filesystem::create_directories(thumbPath); - } - - bool firstTime = true; /* through the loop */ - - while (!this->Exited()) { - this->restart = false; - - Preferences prefs("Indexer"); - - if(!firstTime || (firstTime && prefs.GetBool("SyncOnStartup", true))) { /* first time through the loop skips this */ - this->SynchronizeStart(); - - this->dbConnection.Open(this->dbFilename.c_str(), 0); /* ensure the db is open*/ - - this->SynchronizeInternal(); - this->RunAnalyzers(); - - { - boost::mutex::scoped_lock lock(this->progressMutex); - this->status = 0; - } - - this->dbConnection.Close(); /* TODO: raii */ - - this->SynchronizeEnd(); - } /* end skip */ - - firstTime = false; - - int waitTime = prefs.GetInt("SyncTimeout", 3600); /* sleep before we try again... */ - - if (waitTime) { - boost::xtime waitTimeout; - boost::xtime_get(&waitTimeout, boost::TIME_UTC_); - waitTimeout.sec += waitTime; - - if (!this->Restarted()) { - this->NotificationTimedWait(waitTimeout); - } - } - else { - if (!this->Restarted()) { - this->NotificationWait(); /* zzz */ - } - } - } -} - -void Indexer::SyncDelete() { - /* remove all tracks that no longer reference a valid path entry */ - - this->dbConnection.Execute("DELETE FROM tracks WHERE path_id NOT IN (SELECT id FROM paths)"); - - /* remove files that are no longer on the filesystem. */ - - db::Statement stmtRemove("DELETE FROM tracks WHERE id=?", this->dbConnection); - - db::Statement allTracks( - "SELECT t.id, t.filename " - "FROM tracks t ", this->dbConnection); - - while(allTracks.Step() == db::Row && !this->Exited() && !this->Restarted()) { - bool remove = false; - std::string fn = allTracks.ColumnText(1); - - try { - boost::filesystem::path file(fn); - if (!boost::filesystem::exists(file)) { - remove = true; - } - } - catch(...) { - } - - if (remove) { - stmtRemove.BindInt(0, allTracks.ColumnInt(0)); - stmtRemove.Step(); - stmtRemove.Reset(); - } - } -} - -////////////////////////////////////////// -///\brief -///Removes information related to removed tracks. -/// -///This should be called after SyncDelete() to clean up the mess :) -/// -///\see -/// -////////////////////////////////////////// -void Indexer::SyncCleanup() { - // Remove old artists - this->dbConnection.Execute("DELETE FROM track_artists WHERE track_id NOT IN (SELECT id FROM tracks)"); - this->dbConnection.Execute("DELETE FROM artists WHERE id NOT IN (SELECT DISTINCT(visual_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(album_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(artist_id) FROM track_artists)"); - - // Remove old genres - this->dbConnection.Execute("DELETE FROM track_genres WHERE track_id NOT IN (SELECT id FROM tracks)"); - this->dbConnection.Execute("DELETE FROM genres WHERE id NOT IN (SELECT DISTINCT(visual_genre_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(genre_id) FROM track_genres)"); - - // Remove old albums - this->dbConnection.Execute("DELETE FROM albums WHERE id NOT IN (SELECT DISTINCT(album_id) FROM tracks)"); - - // Remove metadata - this->dbConnection.Execute("DELETE FROM track_meta WHERE track_id NOT IN (SELECT id FROM tracks)"); - this->dbConnection.Execute("DELETE FROM meta_values WHERE id NOT IN (SELECT DISTINCT(meta_value_id) FROM track_meta)"); - this->dbConnection.Execute("DELETE FROM meta_keys WHERE id NOT IN (SELECT DISTINCT(meta_key_id) FROM meta_values)"); - - // ANALYZE - this->dbConnection.Execute("ANALYZE"); - - // Vacuum to remove unwanted space - this->dbConnection.Execute("VACUUM"); - - this->TrackRefreshed(); -} - -////////////////////////////////////////// -///\brief -///Get a vector with all sync paths -////////////////////////////////////////// -void Indexer::GetPaths(std::vector& paths) { - db::Connection connection; - connection.Open(this->dbFilename.c_str()); - db::Statement stmt("SELECT path FROM paths ORDER BY id", connection); - - while (stmt.Step() == db::Row) { - paths.push_back(stmt.ColumnText(0)); - } -} - -static int optimize( - musik::core::db::Connection &connection, - std::string singular, - std::string plural) -{ - std::string outer = boost::str( - boost::format("SELECT id, lower(trim(name)) AS %1% FROM %2% ORDER BY %3%") - % singular % plural % singular); - - db::Statement outerStmt(outer.c_str(), connection); - - std::string inner = boost::str(boost::format("UPDATE %1% SET sort_order=? WHERE id=?") % plural); - db::Statement innerStmt(inner.c_str(), connection); - - int count = 0; - while (outerStmt.Step() == db::Row) { - innerStmt.BindInt(0, count); - innerStmt.BindInt(1, outerStmt.ColumnInt(0)); - innerStmt.Step(); - innerStmt.Reset(); - ++count; - } - - boost::thread::yield(); - - return count; -} - -////////////////////////////////////////// -///\brief -///Optimizes and fixing sorting and indexes -////////////////////////////////////////// -void Indexer::SyncOptimize() { - DBID count = 0, id = 0; - - { - db::ScopedTransaction transaction(this->dbConnection); - optimize(this->dbConnection, "genre", "genres"); - } - - { - db::ScopedTransaction transaction(this->dbConnection); - optimize(this->dbConnection, "artist", "artists"); - } - - { - db::ScopedTransaction transaction(this->dbConnection); - optimize(this->dbConnection, "album", "albums"); - } - - { - db::ScopedTransaction transaction(this->dbConnection); - optimize(this->dbConnection, "content", "meta_values"); - } -} - -////////////////////////////////////////// -///\brief -///Method for adding/removing paths in the database -////////////////////////////////////////// -void Indexer::ProcessAddRemoveQueue() { - boost::mutex::scoped_lock lock(this->exitMutex); - - while (!this->addRemoveQueue.empty()) { - AddRemoveContext context = this->addRemoveQueue.front(); - - if (context.add) { /* insert new paths */ - db::Statement stmt("SELECT id FROM paths WHERE path=?", this->dbConnection); - stmt.BindText(0, context.path); - - if (stmt.Step() != db::Row) { - db::Statement insertPath("INSERT INTO paths (path) VALUES (?)", this->dbConnection); - insertPath.BindText(0, context.path); - insertPath.Step(); - } - } - else { /* remove old ones */ - db::Statement stmt("DELETE FROM paths WHERE path=?", this->dbConnection); - stmt.BindText(0, context.path); - stmt.Step(); - } - - this->addRemoveQueue.pop_front(); - } - - this->PathsUpdated(); -} - -void Indexer::RunAnalyzers() { - typedef audio::IAnalyzer PluginType; - typedef PluginFactory::DestroyDeleter Deleter; - typedef std::shared_ptr PluginPtr; - typedef std::vector PluginVector; - - /* short circuit if there aren't any analyzers */ - - PluginVector analyzers = PluginFactory::Instance() - .QueryInterface("GetAudioAnalyzer"); - - if (analyzers.empty()) { - return; - } - - /* initialize status */ - - { - boost::mutex::scoped_lock lock(this->progressMutex); - this->status = 6; - } - - /* for each track... */ - - DBID trackId = 0; - - db::Statement getNextTrack( - "SELECT id FROM tracks WHERE id>? ORDER BY id LIMIT 1", - this->dbConnection); - - getNextTrack.BindInt(0, trackId); - - while(getNextTrack.Step() == db::Row ) { - trackId = getNextTrack.ColumnInt(0); - - getNextTrack.Reset(); - getNextTrack.UnBindAll(); - - IndexerTrack track(trackId); - - if (LibraryTrack::Load(&track, this->dbConnection)) { - PluginVector runningAnalyzers; - - PluginVector::iterator plugin = analyzers.begin(); - for ( ; plugin != analyzers.end(); ++plugin) { - if ((*plugin)->Start(&track)) { - runningAnalyzers.push_back(*plugin); - } - } - - if(!runningAnalyzers.empty()) { - audio::StreamPtr stream = audio::Stream::Create(audio::Stream::NoDSP); - - if (stream) { - if (stream->OpenStream(track.URI())) { - - /* decode the stream quickly, passing to all analyzers*/ - - audio::BufferPtr buffer; - - while ((buffer = stream->GetNextProcessedOutputBuffer()) && !runningAnalyzers.empty()) { - PluginVector::iterator plugin = runningAnalyzers.begin(); - while(plugin != runningAnalyzers.end()) { - if ((*plugin)->Analyze(&track, buffer.get())) { - ++plugin; - } - else { - plugin = runningAnalyzers.erase(plugin); - } - } - } - - /* done with track decoding and analysis, let the plugins know */ - int successPlugins = 0; - PluginVector::iterator plugin = analyzers.begin(); - - for( ; plugin != analyzers.end(); ++plugin){ - if((*plugin)->End(&track)){ - successPlugins++; - } - } - - /* the analyzers can write metadata back to the DB, so if any of them - completed successfully, then save the track. */ - - if(successPlugins>0) { - track.Save(this->dbConnection, this->libraryPath); - } - } - } - } - } - - if (this->Exited() || this->Restarted()){ - return; - } - - getNextTrack.BindInt(0, trackId); - } -} +////////////////////////////////////////////////////////////////////////////// + +#include "pch.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static const std::string TAG = "Indexer"; + +using namespace musik::core; +using namespace musik::core::metadata; +using namespace musik::core::audio; + +static std::string normalizeDir(std::string path) { + path = boost::filesystem::path(path).make_preferred().string(); + + std::string sep(1, boost::filesystem::path::preferred_separator); + if (path.substr(path.size() - 1, 1) != sep) { + path += sep; + } + + return path; +} + +static std::string normalizePath(const std::string& path) { + return boost::filesystem::path(path).make_preferred().string(); +} + +Indexer::Indexer(const std::string& libraryPath, const std::string& dbFilename) +: thread(NULL) +, status(0) +, restart(false) +, filesIndexed(0) +, filesSaved(0) { + this->dbFilename = dbFilename; + this->libraryPath = libraryPath; + this->thread = new boost::thread(boost::bind(&Indexer::ThreadLoop, this)); +} + +////////////////////////////////////////// +///\brief +///Destructor +/// +///Exits and joins threads +////////////////////////////////////////// +Indexer::~Indexer(){ + if (this->thread) { + this->Exit(); + this->thread->join(); + delete this->thread; + this->thread = NULL; + } +} + +////////////////////////////////////////// +///\brief +///Restart the sync +/// +///\param bNewRestart +///Should if be restarted or not +////////////////////////////////////////// +void Indexer::Synchronize(bool restart) { + boost::mutex::scoped_lock lock(this->exitMutex); + this->restart = restart; + this->Notify(); +} + +////////////////////////////////////////// +///\brief +///Should the sync be restarted? +////////////////////////////////////////// +bool Indexer::Restarted() { + boost::mutex::scoped_lock lock(this->exitMutex); + return this->restart; +} + +////////////////////////////////////////// +///\brief +///Add a new path to the Indexer. +/// +///\param sPath +///Path to add +/// +///\remarks +///If the path already exists it will not be added. +////////////////////////////////////////// +void Indexer::AddPath(const std::string& path) { + Indexer::AddRemoveContext context; + context.add = true; + context.path = normalizeDir(path); + + { + boost::mutex::scoped_lock lock(this->exitMutex); + this->addRemoveQueue.push_back(context); + } + + this->Synchronize(); +} + +////////////////////////////////////////// +///\brief +///Remove a path from the Indexer +/// +///\param sPath +///Path to remove +////////////////////////////////////////// +void Indexer::RemovePath(const std::string& path) { + Indexer::AddRemoveContext context; + context.add = false; + context.path = normalizeDir(path); + + { + boost::mutex::scoped_lock lock(this->exitMutex); + this->addRemoveQueue.push_back(context); + } + + this->Synchronize(); +} + +////////////////////////////////////////// +///\brief +///Main method for doing the synchronization. +////////////////////////////////////////// +void Indexer::SynchronizeInternal() { + /* load all of the metadata (tag) reader plugins */ + typedef PluginFactory::DestroyDeleter MetadataDeleter; + typedef PluginFactory::DestroyDeleter DecoderDeleter; + + this->metadataReaders = PluginFactory::Instance() + .QueryInterface("GetMetadataReader"); + + this->audioDecoders = PluginFactory::Instance() + .QueryInterface("GetDecoderFactory"); + + { + boost::mutex::scoped_lock lock(this->progressMutex); + this->filesIndexed = 0; + } + + this->ProcessAddRemoveQueue(); + + /* get sync paths and ids from the db */ + + std::vector paths; + std::vector pathIds; + + { + db::Statement stmt("SELECT id, path FROM paths", this->dbConnection); + + while (stmt.Step() == db::Row) { + try { + DBID id = stmt.ColumnInt(0); + std::string path = stmt.ColumnText(1); + boost::filesystem::path dir(path); + + if (boost::filesystem::exists(dir)) { + paths.push_back(path); + pathIds.push_back(id); + } + } + catch(...) { + } + } + } + + /* add new files */ + + { + boost::mutex::scoped_lock lock(this->progressMutex); + this->status = 2; + this->filesSaved = 0; + } + + for(std::size_t i = 0; i < paths.size(); ++i) { + std::string path = paths[i]; + this->SyncDirectory(path, path, pathIds[i]); + } + + /* remove undesired entries from db (files themselves will remain) */ + + musik::debug::info(TAG, "cleanup 1/2"); + + { + boost::mutex::scoped_lock lock(this->progressMutex); + this->status = 3; + } + + if (!this->Restarted() && !this->Exited()) { + this->SyncDelete(); + } + + /* cleanup -- remove stale artists, albums, genres, etc */ + + musik::debug::info(TAG, "cleanup 2/2"); + + { + boost::mutex::scoped_lock lock(this->progressMutex); + this->status = 4; + } + + + if(!this->Restarted() && !this->Exited()){ + this->SyncCleanup(); + } + + /* optimize */ + + musik::debug::info(TAG, "optimizing"); + + { + boost::mutex::scoped_lock lock(this->progressMutex); + this->status = 5; + } + + if(!this->Restarted() && !this->Exited()){ + this->SyncOptimize(); + } + + /* unload reader DLLs*/ + + this->metadataReaders.clear(); + + { + boost::mutex::scoped_lock lock(this->progressMutex); + this->status = 0; + } + + musik::debug::info(TAG, "done!"); +} + +void Indexer::SyncDirectory( + const std::string &syncRoot, + const std::string ¤tPath, + DBID pathId) +{ + if (this->Exited() || this->Restarted()) { + return; + } + + std::string normalizedSyncRoot = normalizeDir(syncRoot); + std::string normalizedCurrentPath = normalizeDir(currentPath); + std::string leaf = boost::filesystem::path(currentPath).leaf().string(); /* trailing subdir in currentPath */ + + /* start recursive filesystem scan */ + + try { /* boost::filesystem may throw */ + + /* for each file in the current path... */ + + boost::filesystem::path path(currentPath); + boost::filesystem::directory_iterator end; + boost::filesystem::directory_iterator file(path); + + std::string pathIdStr = boost::lexical_cast(pathId); + + for( ; file != end && !this->Exited() && !this->Restarted(); file++) { + if (is_directory(file->status())) { + /* recursion here */ + musik::debug::info(TAG, "scanning " + file->path().string()); + this->SyncDirectory(syncRoot, file->path().string(), pathId); + } + else { + ++this->filesIndexed; + + musik::core::IndexerTrack track(0); + + /* get cached filesize, parts, size, etc */ + if (track.NeedsToBeIndexed(file->path(), this->dbConnection)) { + bool saveToDb = false; + + /* read the tag from the plugin */ + typedef MetadataReaderList::iterator Iterator; + Iterator it = this->metadataReaders.begin(); + while (it != this->metadataReaders.end()) { + if ((*it)->CanRead(track.GetValue("extension").c_str())) { + if ((*it)->Read(file->path().string().c_str(), &track)) { + saveToDb = true; + break; + } + } + it++; + } + + /* no tag? well... if a decoder can play it, add it to the database + with the file as the name. */ + if (!saveToDb) { + std::string fullPath = file->path().string(); + auto it = this->audioDecoders.begin(); + while (it != this->audioDecoders.end()) { + if ((*it)->CanHandle(fullPath.c_str())) { + saveToDb = true; + track.SetValue("title", file->path().leaf().string().c_str()); + break; + } + ++it; + } + } + + /* write it to the db, if read successfully */ + if (saveToDb) { + track.SetValue("path_id", pathIdStr.c_str()); + track.Save(this->dbConnection, this->libraryPath); + + this->filesSaved++; + if (this->filesSaved % 100 == 0) { + this->TrackRefreshed(); /* no idea... something listens to this. maybe?*/ + } + } + } + } + } + } + catch(...) { + } +} + +////////////////////////////////////////// +///\brief +///Main loop the thread is running in. +////////////////////////////////////////// +void Indexer::ThreadLoop() { + boost::filesystem::path thumbPath(this->libraryPath + "thumbs/"); + + if (!boost::filesystem::exists(thumbPath)) { + boost::filesystem::create_directories(thumbPath); + } + + bool firstTime = true; /* through the loop */ + + while (!this->Exited()) { + this->restart = false; + + Preferences prefs("Indexer"); + + if(!firstTime || (firstTime && prefs.GetBool("SyncOnStartup", true))) { /* first time through the loop skips this */ + this->SynchronizeStart(); + + this->dbConnection.Open(this->dbFilename.c_str(), 0); /* ensure the db is open*/ + + this->SynchronizeInternal(); + this->RunAnalyzers(); + + { + boost::mutex::scoped_lock lock(this->progressMutex); + this->status = 0; + } + + this->dbConnection.Close(); /* TODO: raii */ + + this->SynchronizeEnd(); + } /* end skip */ + + firstTime = false; + + int waitTime = prefs.GetInt("SyncTimeout", 3600); /* sleep before we try again... */ + + if (waitTime) { + boost::xtime waitTimeout; + boost::xtime_get(&waitTimeout, boost::TIME_UTC_); + waitTimeout.sec += waitTime; + + if (!this->Restarted()) { + this->NotificationTimedWait(waitTimeout); + } + } + else { + if (!this->Restarted()) { + this->NotificationWait(); /* zzz */ + } + } + } +} + +void Indexer::SyncDelete() { + /* remove all tracks that no longer reference a valid path entry */ + + this->dbConnection.Execute("DELETE FROM tracks WHERE path_id NOT IN (SELECT id FROM paths)"); + + /* remove files that are no longer on the filesystem. */ + + db::Statement stmtRemove("DELETE FROM tracks WHERE id=?", this->dbConnection); + + db::Statement allTracks( + "SELECT t.id, t.filename " + "FROM tracks t ", this->dbConnection); + + while(allTracks.Step() == db::Row && !this->Exited() && !this->Restarted()) { + bool remove = false; + std::string fn = allTracks.ColumnText(1); + + try { + boost::filesystem::path file(fn); + if (!boost::filesystem::exists(file)) { + remove = true; + } + } + catch(...) { + } + + if (remove) { + stmtRemove.BindInt(0, allTracks.ColumnInt(0)); + stmtRemove.Step(); + stmtRemove.Reset(); + } + } +} + +////////////////////////////////////////// +///\brief +///Removes information related to removed tracks. +/// +///This should be called after SyncDelete() to clean up the mess :) +/// +///\see +/// +////////////////////////////////////////// +void Indexer::SyncCleanup() { + // Remove old artists + this->dbConnection.Execute("DELETE FROM track_artists WHERE track_id NOT IN (SELECT id FROM tracks)"); + this->dbConnection.Execute("DELETE FROM artists WHERE id NOT IN (SELECT DISTINCT(visual_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(album_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(artist_id) FROM track_artists)"); + + // Remove old genres + this->dbConnection.Execute("DELETE FROM track_genres WHERE track_id NOT IN (SELECT id FROM tracks)"); + this->dbConnection.Execute("DELETE FROM genres WHERE id NOT IN (SELECT DISTINCT(visual_genre_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(genre_id) FROM track_genres)"); + + // Remove old albums + this->dbConnection.Execute("DELETE FROM albums WHERE id NOT IN (SELECT DISTINCT(album_id) FROM tracks)"); + + // Remove metadata + this->dbConnection.Execute("DELETE FROM track_meta WHERE track_id NOT IN (SELECT id FROM tracks)"); + this->dbConnection.Execute("DELETE FROM meta_values WHERE id NOT IN (SELECT DISTINCT(meta_value_id) FROM track_meta)"); + this->dbConnection.Execute("DELETE FROM meta_keys WHERE id NOT IN (SELECT DISTINCT(meta_key_id) FROM meta_values)"); + + // ANALYZE + this->dbConnection.Execute("ANALYZE"); + + // Vacuum to remove unwanted space + this->dbConnection.Execute("VACUUM"); + + this->TrackRefreshed(); +} + +////////////////////////////////////////// +///\brief +///Get a vector with all sync paths +////////////////////////////////////////// +void Indexer::GetPaths(std::vector& paths) { + db::Connection connection; + connection.Open(this->dbFilename.c_str()); + db::Statement stmt("SELECT path FROM paths ORDER BY id", connection); + + while (stmt.Step() == db::Row) { + paths.push_back(stmt.ColumnText(0)); + } +} + +static int optimize( + musik::core::db::Connection &connection, + std::string singular, + std::string plural) +{ + std::string outer = boost::str( + boost::format("SELECT id, lower(trim(name)) AS %1% FROM %2% ORDER BY %3%") + % singular % plural % singular); + + db::Statement outerStmt(outer.c_str(), connection); + + std::string inner = boost::str(boost::format("UPDATE %1% SET sort_order=? WHERE id=?") % plural); + db::Statement innerStmt(inner.c_str(), connection); + + int count = 0; + while (outerStmt.Step() == db::Row) { + innerStmt.BindInt(0, count); + innerStmt.BindInt(1, outerStmt.ColumnInt(0)); + innerStmt.Step(); + innerStmt.Reset(); + ++count; + } + + boost::thread::yield(); + + return count; +} + +////////////////////////////////////////// +///\brief +///Optimizes and fixing sorting and indexes +////////////////////////////////////////// +void Indexer::SyncOptimize() { + DBID count = 0, id = 0; + + { + db::ScopedTransaction transaction(this->dbConnection); + optimize(this->dbConnection, "genre", "genres"); + } + + { + db::ScopedTransaction transaction(this->dbConnection); + optimize(this->dbConnection, "artist", "artists"); + } + + { + db::ScopedTransaction transaction(this->dbConnection); + optimize(this->dbConnection, "album", "albums"); + } + + { + db::ScopedTransaction transaction(this->dbConnection); + optimize(this->dbConnection, "content", "meta_values"); + } +} + +////////////////////////////////////////// +///\brief +///Method for adding/removing paths in the database +////////////////////////////////////////// +void Indexer::ProcessAddRemoveQueue() { + boost::mutex::scoped_lock lock(this->exitMutex); + + while (!this->addRemoveQueue.empty()) { + AddRemoveContext context = this->addRemoveQueue.front(); + + if (context.add) { /* insert new paths */ + db::Statement stmt("SELECT id FROM paths WHERE path=?", this->dbConnection); + stmt.BindText(0, context.path); + + if (stmt.Step() != db::Row) { + db::Statement insertPath("INSERT INTO paths (path) VALUES (?)", this->dbConnection); + insertPath.BindText(0, context.path); + insertPath.Step(); + } + } + else { /* remove old ones */ + db::Statement stmt("DELETE FROM paths WHERE path=?", this->dbConnection); + stmt.BindText(0, context.path); + stmt.Step(); + } + + this->addRemoveQueue.pop_front(); + } + + this->PathsUpdated(); +} + +void Indexer::RunAnalyzers() { + typedef audio::IAnalyzer PluginType; + typedef PluginFactory::DestroyDeleter Deleter; + typedef std::shared_ptr PluginPtr; + typedef std::vector PluginVector; + + /* short circuit if there aren't any analyzers */ + + PluginVector analyzers = PluginFactory::Instance() + .QueryInterface("GetAudioAnalyzer"); + + if (analyzers.empty()) { + return; + } + + /* initialize status */ + + { + boost::mutex::scoped_lock lock(this->progressMutex); + this->status = 6; + } + + /* for each track... */ + + DBID trackId = 0; + + db::Statement getNextTrack( + "SELECT id FROM tracks WHERE id>? ORDER BY id LIMIT 1", + this->dbConnection); + + getNextTrack.BindInt(0, trackId); + + while(getNextTrack.Step() == db::Row ) { + trackId = getNextTrack.ColumnInt(0); + + getNextTrack.Reset(); + getNextTrack.UnBindAll(); + + IndexerTrack track(trackId); + + if (LibraryTrack::Load(&track, this->dbConnection)) { + PluginVector runningAnalyzers; + + PluginVector::iterator plugin = analyzers.begin(); + for ( ; plugin != analyzers.end(); ++plugin) { + if ((*plugin)->Start(&track)) { + runningAnalyzers.push_back(*plugin); + } + } + + if(!runningAnalyzers.empty()) { + audio::StreamPtr stream = audio::Stream::Create(audio::Stream::NoDSP); + + if (stream) { + if (stream->OpenStream(track.URI())) { + + /* decode the stream quickly, passing to all analyzers*/ + + audio::BufferPtr buffer; + + while ((buffer = stream->GetNextProcessedOutputBuffer()) && !runningAnalyzers.empty()) { + PluginVector::iterator plugin = runningAnalyzers.begin(); + while(plugin != runningAnalyzers.end()) { + if ((*plugin)->Analyze(&track, buffer.get())) { + ++plugin; + } + else { + plugin = runningAnalyzers.erase(plugin); + } + } + } + + /* done with track decoding and analysis, let the plugins know */ + int successPlugins = 0; + PluginVector::iterator plugin = analyzers.begin(); + + for( ; plugin != analyzers.end(); ++plugin){ + if((*plugin)->End(&track)){ + successPlugins++; + } + } + + /* the analyzers can write metadata back to the DB, so if any of them + completed successfully, then save the track. */ + + if(successPlugins>0) { + track.Save(this->dbConnection, this->libraryPath); + } + } + } + } + } + + if (this->Exited() || this->Restarted()){ + return; + } + + getNextTrack.BindInt(0, trackId); + } +} diff --git a/src/core/library/LibraryFactory.cpp b/src/core/library/LibraryFactory.cpp index befa734c7..8f24a240f 100644 --- a/src/core/library/LibraryFactory.cpp +++ b/src/core/library/LibraryFactory.cpp @@ -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 -#include -#include -#include -#include - -using namespace musik::core; - -LibraryFactory& LibraryFactory::Instance() { - typedef std::shared_ptr 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 +#include +#include +#include +#include + +using namespace musik::core; + +LibraryFactory& LibraryFactory::Instance() { + typedef std::shared_ptr 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(); +} + diff --git a/src/core/library/LocalLibrary.cpp b/src/core/library/LocalLibrary.cpp index 4918ff1da..ae390af14 100644 --- a/src/core/library/LocalLibrary.cpp +++ b/src/core/library/LocalLibrary.cpp @@ -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 -#include -#include -#include -#include -#include -#include - -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(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 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 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 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 +#include +#include +#include +#include +#include +#include + +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(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 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 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 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(); } \ No newline at end of file diff --git a/src/core/library/metadata/MetadataKeyValue.cpp b/src/core/library/metadata/MetadataKeyValue.cpp index 85bf56a1e..2449fa7bd 100644 --- a/src/core/library/metadata/MetadataKeyValue.cpp +++ b/src/core/library/metadata/MetadataKeyValue.cpp @@ -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 - -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 + +using namespace musik::core; + +MetadataKeyValue::MetadataKeyValue(const DBID newId, const char *value) +: id (newId) { + if (value) { + this->value = value; + } +} + +MetadataKeyValue::~MetadataKeyValue() { +} diff --git a/src/core/library/metadata/MetadataValue.cpp b/src/core/library/metadata/MetadataValue.cpp index f901b8196..dcc1d2e51 100644 --- a/src/core/library/metadata/MetadataValue.cpp +++ b/src/core/library/metadata/MetadataValue.cpp @@ -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 - -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 + +using namespace musik::core; + +MetadataValue::MetadataValue(const DBID newId, const char *value) +: id(newId) { + if (value) { + this->value = value; + } +} + +MetadataValue::MetadataValue() { +} + +MetadataValue::~MetadataValue() { +} diff --git a/src/core/library/query/QueryBase.cpp b/src/core/library/query/QueryBase.cpp index c014bfcb0..7a818b970 100644 --- a/src/core/library/query/QueryBase.cpp +++ b/src/core/library/query/QueryBase.cpp @@ -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 -#include -#include - -using namespace musik::core; -using namespace musik::core::query; - -static boost::atomic 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 +#include +#include + +using namespace musik::core; +using namespace musik::core::query; + +static boost::atomic 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; +} diff --git a/src/core/library/track/IndexerTrack.cpp b/src/core/library/track/IndexerTrack.cpp index ba22402e4..c7bea03d0 100644 --- a/src/core/library/track/IndexerTrack.cpp +++ b/src/core/library/track/IndexerTrack.cpp @@ -30,535 +30,535 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "pch.hpp" - -#include - -#include -#include -#include -#include -#include - -#include - -using namespace musik::core; - -#define GENRE_TRACK_COLUMN_NAME "genre" -#define GENRES_TABLE_NAME "genres" -#define GENRE_TRACK_JUNCTION_TABLE_NAME "track_genres" -#define GENRE_TRACK_FOREIGN_KEY "genre_id" - -#define ARTIST_TRACK_COLUMN_NAME "artist" -#define ARTISTS_TABLE_NAME "artists" -#define ARTIST_TRACK_JUNCTION_TABLE_NAME "track_artists" -#define ARTIST_TRACK_FOREIGN_KEY "artist_id" - -IndexerTrack::IndexerTrack(DBID id) -: internalMetadata(new IndexerTrack::MetadataWithThumbnail()) -, id(id) -{ -} - -IndexerTrack::~IndexerTrack() { - delete this->internalMetadata; - this->internalMetadata = NULL; -} - -std::string IndexerTrack::GetValue(const char* metakey) { - if (metakey && this->internalMetadata) { - MetadataMap::iterator metavalue = this->internalMetadata->metadata.find(metakey); - if (metavalue != this->internalMetadata->metadata.end()) { - return metavalue->second; - } - } - - return ""; -} - -void IndexerTrack::SetValue(const char* metakey, const char* value) { - if (metakey && value) { - this->internalMetadata->metadata.insert( - std::pair(metakey,value)); - } -} - -void IndexerTrack::ClearValue(const char* metakey) { - if (this->internalMetadata) { - this->internalMetadata->metadata.erase(metakey); - } -} - -void IndexerTrack::SetThumbnail(const char *data,long size) { - if (this->internalMetadata->thumbnailData) { - delete this->internalMetadata->thumbnailData; - } - - this->internalMetadata->thumbnailData = new char[size]; - this->internalMetadata->thumbnailSize = size; - - memcpy(this->internalMetadata->thumbnailData, data, size); -} - -std::string IndexerTrack::URI() { - return this->GetValue("filename"); -} - -Track::MetadataIteratorRange IndexerTrack::GetValues(const char* metakey) { - if (this->internalMetadata) { - return this->internalMetadata->metadata.equal_range(metakey); - } - - return Track::MetadataIteratorRange(); -} - -Track::MetadataIteratorRange IndexerTrack::GetAllValues() { - if (this->internalMetadata) { - return Track::MetadataIteratorRange( - this->internalMetadata->metadata.begin(), - this->internalMetadata->metadata.end()); - } - - return Track::MetadataIteratorRange(); -} - -DBID IndexerTrack::Id() { - return this->id; -} - -bool IndexerTrack::NeedsToBeIndexed( - const boost::filesystem::path &file, - db::Connection &dbConnection) -{ - try { - this->SetValue("path", file.string().c_str()); - this->SetValue("filename", file.string().c_str()); - - size_t lastDot = file.leaf().string().find_last_of("."); - if (lastDot != std::string::npos){ - this->SetValue("extension", file.leaf().string().substr(lastDot + 1).c_str()); - } - - DBID fileSize = (DBID) boost::filesystem::file_size(file); - DBTIME fileTime = (DBTIME) boost::filesystem::last_write_time(file); - - this->SetValue("filesize", boost::lexical_cast(fileSize).c_str()); - this->SetValue("filetime", boost::lexical_cast(fileTime).c_str()); - - db::CachedStatement stmt( - "SELECT id, filename, filesize, filetime " \ - "FROM tracks t " \ - "WHERE filename=?", dbConnection); - - stmt.BindText(0, this->GetValue("filename")); - - bool fileDifferent = true; - - if (stmt.Step() == db::Row) { - this->id = stmt.ColumnInt(0); - int dbFileSize = stmt.ColumnInt(2); - int dbFileTime = stmt.ColumnInt(3); - - if (fileSize == dbFileSize && fileTime == dbFileTime) { - return false; - } - } - } - catch(...) { - } - - return true; -} - -static DBID writeToTracksTable( - db::Connection &dbConnection, - IndexerTrack& track) -{ - db::CachedStatement stmt("INSERT OR REPLACE INTO tracks " \ - "(id, track, disc, bpm, duration, filesize, year, title, filename, filetime, path_id) " \ - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", dbConnection); - - stmt.BindText(1, track.GetValue("track")); - stmt.BindText(2, track.GetValue("disc")); - stmt.BindText(3, track.GetValue("bpm")); - stmt.BindText(4, track.GetValue("duration")); - stmt.BindText(5, track.GetValue("filesize")); - stmt.BindText(6, track.GetValue("year")); - stmt.BindText(7, track.GetValue("title")); - stmt.BindText(8, track.GetValue("filename")); - stmt.BindText(9, track.GetValue("filetime")); - stmt.BindText(10, track.GetValue("path_id")); - - if (track.Id() != 0) { - stmt.BindInt(0, track.Id()); - } - - if (stmt.Step() == db::Done) { - if (track.Id() == 0) { - return dbConnection.LastInsertedId(); - } - } - - return track.Id(); -} - -static void removeRelation( - db::Connection& connection, - const std::string& field, - DBID trackId) -{ - std::string query = boost::str(boost::format("DELETE FROM %1% WHERE track_id=?") % field); - db::CachedStatement stmt(query.c_str(), connection); - stmt.BindInt(0, trackId); - stmt.Step(); -} - -static void removeKnownFields(Track::MetadataMap& metadata) { - metadata.erase("track"); - metadata.erase("disc"); - metadata.erase("bpm"); - metadata.erase("duration"); - metadata.erase("year"); - metadata.erase("title"); - metadata.erase("filename"); - metadata.erase("filetime"); - metadata.erase("filesize"); - metadata.erase("title"); - metadata.erase("path"); - metadata.erase("extension"); - metadata.erase("genre"); - metadata.erase("artist"); - metadata.erase("album"); -} - -DBID IndexerTrack::SaveThumbnail(db::Connection& connection, const std::string& libraryDirectory) { - DBID thumbnailId = 0; - - if (this->internalMetadata->thumbnailData) { - uint64 sum = Checksum(this->internalMetadata->thumbnailData, this->internalMetadata->thumbnailSize); - - db::CachedStatement thumbs("SELECT id FROM thumbnails WHERE filesize=? AND checksum=?", connection); - thumbs.BindInt(0, this->internalMetadata->thumbnailSize); - thumbs.BindInt(1, sum); - - if (thumbs.Step() == db::Row) { - thumbnailId = thumbs.ColumnInt(0); /* thumbnail already exists */ - } - - if (thumbnailId == 0) { /* doesn't exist yet, let's insert the record and write the file */ - db::Statement insertThumb("INSERT INTO thumbnails (filesize,checksum) VALUES (?,?)", connection); - insertThumb.BindInt(0, this->internalMetadata->thumbnailSize); - insertThumb.BindInt(1, sum); - - if (insertThumb.Step() == db::Done) { - thumbnailId = connection.LastInsertedId(); - - std::string filename = - libraryDirectory + - "thumbs/" + - boost::lexical_cast(thumbnailId) + - ".jpg"; - -#ifdef WIN32 - std::wstring wfilename = u8to16(filename); - FILE *thumbFile = _wfopen(wfilename.c_str(), _T("wb")); -#else - FILE *thumbFile = fopen(filename.c_str(), "wb"); -#endif - fwrite(this->internalMetadata->thumbnailData, sizeof(char), this->internalMetadata->thumbnailSize, thumbFile); - fclose(thumbFile); - } - } - } - - return thumbnailId; -} - -void IndexerTrack::ProcessNonStandardMetadata(db::Connection& connection) { - MetadataMap unknownFields(this->internalMetadata->metadata); - removeKnownFields(unknownFields); - - db::CachedStatement selectMetaKey("SELECT id FROM meta_keys WHERE name=?", connection); - db::CachedStatement selectMetaValue("SELECT id FROM meta_values WHERE meta_key_id=? AND content=?", connection); - db::CachedStatement insertMetaValue("INSERT INTO meta_values (meta_key_id,content) VALUES (?,?)", connection); - db::CachedStatement insertTrackMeta("INSERT INTO track_meta (track_id,meta_value_id) VALUES (?,?)", connection); - db::CachedStatement insertMetaKey("INSERT INTO meta_keys (name) VALUES (?)", connection); - - MetadataMap::const_iterator it = unknownFields.begin(); - for ( ; it != unknownFields.end(); ++it){ - DBID keyId = 0; - std::string key; - - /* lookup the ID for the key; insert if it doesn't exist.. */ - - selectMetaKey.BindText(0, it->first); - - if (selectMetaKey.Step() == db::Row) { - keyId = selectMetaKey.ColumnInt(0); - } - else { - insertMetaKey.BindText(0, it->first); - - if (insertMetaKey.Step() == db::Done) { - keyId = connection.LastInsertedId(); - } - } - - selectMetaKey.Reset(); - - if (keyId == 0) { - continue; /* welp... */ - } - - /* see if we already have the value as a normalized row in our table. - if we don't, insert it. */ - - DBID valueId = 0; - - selectMetaValue.BindInt(0, keyId); - selectMetaValue.BindText(1, it->second); - - if (selectMetaValue.Step() == db::Row) { - valueId = selectMetaValue.ColumnInt(0); - } - else { - insertMetaValue.BindInt(0, keyId); - insertMetaValue.BindText(1, it->second); - - if (insertMetaValue.Step() == db::Done) { - valueId = connection.LastInsertedId(); - } - - insertMetaValue.Reset(); - } - - selectMetaValue.Reset(); - - /* now that we have a keyId and a valueId, create the relationship */ - - if (valueId != 0) { - insertTrackMeta.BindInt(0, this->id); - insertTrackMeta.BindInt(1, valueId); - insertTrackMeta.Step(); - insertTrackMeta.Reset(); - } - } -} - -DBID IndexerTrack::SaveSingleValueField( - db::Connection& dbConnection, - const std::string& trackMetadataKeyName, - const std::string& fieldTableName) -{ - DBID id = 0; - - std::string selectQuery = boost::str(boost::format( - "SELECT id FROM %1% WHERE name=?") % fieldTableName); - - db::CachedStatement stmt(selectQuery.c_str(), dbConnection); - std::string value = this->GetValue(trackMetadataKeyName.c_str()); - - stmt.BindText(0, value); - - if (stmt.Step() == db::Row) { - id = stmt.ColumnInt(0); - } - else { - std::string insertStatement = boost::str(boost::format( - "INSERT INTO %1% (name) VALUES (?)") % fieldTableName); - - db::Statement insertValue(insertStatement.c_str(), dbConnection); - insertValue.BindText(0, value); - - if (insertValue.Step() == db::Done) { - id = dbConnection.LastInsertedId(); - } - } - - return id; -} - -DBID IndexerTrack::SaveMultiValueField( - db::Connection& connection, - const std::string& tracksTableColumnName, - const std::string& fieldTableName, - const std::string& junctionTableName, - const std::string& junctionTableForeignKeyColumnName) -{ - std::string aggregatedValue; - DBID fieldId = 0; - int count = 0; - - std::set processed; /* for deduping */ - - MetadataIteratorRange values = this->GetValues(tracksTableColumnName.c_str()); - while (values.first != values.second) { - if (processed.find(values.first->second) == processed.end()) { - processed.insert(values.first->second); - - std::string value = values.first->second; - - fieldId = SaveNormalizedFieldValue( - connection, - fieldTableName, - value, - false, - junctionTableName, - junctionTableForeignKeyColumnName); - - if (count != 0) { - aggregatedValue += ", "; - } - - aggregatedValue += value; - - ++count; - } - - ++values.first; - } - - if (count > 1 || fieldId == 0) { - fieldId = SaveNormalizedFieldValue( - connection, - fieldTableName, - aggregatedValue, - true); - } - - return fieldId; -} - -DBID IndexerTrack::SaveGenre(db::Connection& dbConnection) { - return this->SaveMultiValueField( - dbConnection, - GENRE_TRACK_COLUMN_NAME, - GENRES_TABLE_NAME, - GENRE_TRACK_JUNCTION_TABLE_NAME, - GENRE_TRACK_FOREIGN_KEY); -} - -DBID IndexerTrack::SaveArtist(db::Connection& dbConnection) { - return this->SaveMultiValueField( - dbConnection, - ARTIST_TRACK_COLUMN_NAME, - ARTISTS_TABLE_NAME, - ARTIST_TRACK_JUNCTION_TABLE_NAME, - ARTIST_TRACK_FOREIGN_KEY); -} - -bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirectory) { - db::ScopedTransaction transaction(dbConnection); - - if (this->GetValue("album_artist") == "") { - this->SetValue("album_artist", this->GetValue("artist").c_str()); - } - - /* remove existing relations -- we're going to update them with fresh data */ - - if (this->id != 0) { - removeRelation(dbConnection, "track_genres", this->id); - removeRelation(dbConnection, "track_artists", this->id); - removeRelation(dbConnection, "track_meta", this->id); - } - - /* write generic info to the tracks table */ - - this->id = writeToTracksTable(dbConnection, *this); - - DBID albumId = this->SaveSingleValueField(dbConnection, "album", "albums"); - DBID genreId = this->SaveGenre(dbConnection); - DBID artistId = this->SaveArtist(dbConnection); - DBID albumArtistId = this->SaveSingleValueField(dbConnection, "album_artist", "artists"); - DBID thumbnailId = this->SaveThumbnail(dbConnection, libraryDirectory); - - /* update all of the track foreign keys */ - - { - db::CachedStatement stmt( - "UPDATE tracks " \ - "SET album_id=?, visual_genre_id=?, visual_artist_id=?, album_artist_id=?, thumbnail_id=? " \ - "WHERE id=?", dbConnection); - - stmt.BindInt(0, albumId); - stmt.BindInt(1, genreId); - stmt.BindInt(2, artistId); - stmt.BindInt(3, albumArtistId); - stmt.BindInt(4, thumbnailId); - stmt.BindInt(5, this->id); - stmt.Step(); - } - - ProcessNonStandardMetadata(dbConnection); - - return true; -} - -DBID IndexerTrack::SaveNormalizedFieldValue( - db::Connection &dbConnection, - const std::string& tableName, - const std::string& fieldValue, - bool isAggregatedValue, - const std::string& relationJunctionTableName, - const std::string& relationJunctionTableColumn) -{ - DBID fieldId = 0; - - /* find by value */ - - { - std::string query = boost::str(boost::format("SELECT id FROM %1% WHERE name=?") % tableName); - db::CachedStatement stmt(query.c_str(), dbConnection); - stmt.BindText(0, fieldValue); - - if (stmt.Step() == db::Row) { - fieldId = stmt.ColumnInt(0); - } - } - - /* not found? insert. */ - - if (fieldId == 0) { - std::string query = boost::str(boost::format( - "INSERT INTO %1% (name, aggregated) VALUES (?, ?)") % tableName); - - db::CachedStatement stmt(query.c_str(), dbConnection); - stmt.BindText(0, fieldValue); - stmt.BindInt(1, isAggregatedValue ? 1 : 0); - - if (stmt.Step() == db::Done) { - fieldId = dbConnection.LastInsertedId(); - } - } - - /* if this is a normalized value it may need to be inserted into a - junction table. see if we were asked to do this... */ - - if (relationJunctionTableName.size() && relationJunctionTableColumn.size()) { - std::string query = boost::str(boost::format( - "INSERT INTO %1% (track_id, %2%) VALUES (?, ?)") - % relationJunctionTableName % relationJunctionTableColumn); - - db::Statement stmt(query.c_str(), dbConnection); - stmt.BindInt(0, this->id); - stmt.BindInt(1, fieldId); - stmt.Step(); - } - - return fieldId; -} - -TrackPtr IndexerTrack::Copy() { - return TrackPtr(new IndexerTrack(this->id)); -} - -IndexerTrack::MetadataWithThumbnail::MetadataWithThumbnail() -: thumbnailData(NULL) -, thumbnailSize(0) { -} - -IndexerTrack::MetadataWithThumbnail::~MetadataWithThumbnail() { - delete this->thumbnailData; -} +////////////////////////////////////////////////////////////////////////////// + +#include "pch.hpp" + +#include + +#include +#include +#include +#include +#include + +#include + +using namespace musik::core; + +#define GENRE_TRACK_COLUMN_NAME "genre" +#define GENRES_TABLE_NAME "genres" +#define GENRE_TRACK_JUNCTION_TABLE_NAME "track_genres" +#define GENRE_TRACK_FOREIGN_KEY "genre_id" + +#define ARTIST_TRACK_COLUMN_NAME "artist" +#define ARTISTS_TABLE_NAME "artists" +#define ARTIST_TRACK_JUNCTION_TABLE_NAME "track_artists" +#define ARTIST_TRACK_FOREIGN_KEY "artist_id" + +IndexerTrack::IndexerTrack(DBID id) +: internalMetadata(new IndexerTrack::MetadataWithThumbnail()) +, id(id) +{ +} + +IndexerTrack::~IndexerTrack() { + delete this->internalMetadata; + this->internalMetadata = NULL; +} + +std::string IndexerTrack::GetValue(const char* metakey) { + if (metakey && this->internalMetadata) { + MetadataMap::iterator metavalue = this->internalMetadata->metadata.find(metakey); + if (metavalue != this->internalMetadata->metadata.end()) { + return metavalue->second; + } + } + + return ""; +} + +void IndexerTrack::SetValue(const char* metakey, const char* value) { + if (metakey && value) { + this->internalMetadata->metadata.insert( + std::pair(metakey,value)); + } +} + +void IndexerTrack::ClearValue(const char* metakey) { + if (this->internalMetadata) { + this->internalMetadata->metadata.erase(metakey); + } +} + +void IndexerTrack::SetThumbnail(const char *data,long size) { + if (this->internalMetadata->thumbnailData) { + delete this->internalMetadata->thumbnailData; + } + + this->internalMetadata->thumbnailData = new char[size]; + this->internalMetadata->thumbnailSize = size; + + memcpy(this->internalMetadata->thumbnailData, data, size); +} + +std::string IndexerTrack::URI() { + return this->GetValue("filename"); +} + +Track::MetadataIteratorRange IndexerTrack::GetValues(const char* metakey) { + if (this->internalMetadata) { + return this->internalMetadata->metadata.equal_range(metakey); + } + + return Track::MetadataIteratorRange(); +} + +Track::MetadataIteratorRange IndexerTrack::GetAllValues() { + if (this->internalMetadata) { + return Track::MetadataIteratorRange( + this->internalMetadata->metadata.begin(), + this->internalMetadata->metadata.end()); + } + + return Track::MetadataIteratorRange(); +} + +DBID IndexerTrack::Id() { + return this->id; +} + +bool IndexerTrack::NeedsToBeIndexed( + const boost::filesystem::path &file, + db::Connection &dbConnection) +{ + try { + this->SetValue("path", file.string().c_str()); + this->SetValue("filename", file.string().c_str()); + + size_t lastDot = file.leaf().string().find_last_of("."); + if (lastDot != std::string::npos){ + this->SetValue("extension", file.leaf().string().substr(lastDot + 1).c_str()); + } + + DBID fileSize = (DBID) boost::filesystem::file_size(file); + DBTIME fileTime = (DBTIME) boost::filesystem::last_write_time(file); + + this->SetValue("filesize", boost::lexical_cast(fileSize).c_str()); + this->SetValue("filetime", boost::lexical_cast(fileTime).c_str()); + + db::CachedStatement stmt( + "SELECT id, filename, filesize, filetime " \ + "FROM tracks t " \ + "WHERE filename=?", dbConnection); + + stmt.BindText(0, this->GetValue("filename")); + + bool fileDifferent = true; + + if (stmt.Step() == db::Row) { + this->id = stmt.ColumnInt(0); + int dbFileSize = stmt.ColumnInt(2); + int dbFileTime = stmt.ColumnInt(3); + + if (fileSize == dbFileSize && fileTime == dbFileTime) { + return false; + } + } + } + catch(...) { + } + + return true; +} + +static DBID writeToTracksTable( + db::Connection &dbConnection, + IndexerTrack& track) +{ + db::CachedStatement stmt("INSERT OR REPLACE INTO tracks " \ + "(id, track, disc, bpm, duration, filesize, year, title, filename, filetime, path_id) " \ + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", dbConnection); + + stmt.BindText(1, track.GetValue("track")); + stmt.BindText(2, track.GetValue("disc")); + stmt.BindText(3, track.GetValue("bpm")); + stmt.BindText(4, track.GetValue("duration")); + stmt.BindText(5, track.GetValue("filesize")); + stmt.BindText(6, track.GetValue("year")); + stmt.BindText(7, track.GetValue("title")); + stmt.BindText(8, track.GetValue("filename")); + stmt.BindText(9, track.GetValue("filetime")); + stmt.BindText(10, track.GetValue("path_id")); + + if (track.Id() != 0) { + stmt.BindInt(0, track.Id()); + } + + if (stmt.Step() == db::Done) { + if (track.Id() == 0) { + return dbConnection.LastInsertedId(); + } + } + + return track.Id(); +} + +static void removeRelation( + db::Connection& connection, + const std::string& field, + DBID trackId) +{ + std::string query = boost::str(boost::format("DELETE FROM %1% WHERE track_id=?") % field); + db::CachedStatement stmt(query.c_str(), connection); + stmt.BindInt(0, trackId); + stmt.Step(); +} + +static void removeKnownFields(Track::MetadataMap& metadata) { + metadata.erase("track"); + metadata.erase("disc"); + metadata.erase("bpm"); + metadata.erase("duration"); + metadata.erase("year"); + metadata.erase("title"); + metadata.erase("filename"); + metadata.erase("filetime"); + metadata.erase("filesize"); + metadata.erase("title"); + metadata.erase("path"); + metadata.erase("extension"); + metadata.erase("genre"); + metadata.erase("artist"); + metadata.erase("album"); +} + +DBID IndexerTrack::SaveThumbnail(db::Connection& connection, const std::string& libraryDirectory) { + DBID thumbnailId = 0; + + if (this->internalMetadata->thumbnailData) { + uint64 sum = Checksum(this->internalMetadata->thumbnailData, this->internalMetadata->thumbnailSize); + + db::CachedStatement thumbs("SELECT id FROM thumbnails WHERE filesize=? AND checksum=?", connection); + thumbs.BindInt(0, this->internalMetadata->thumbnailSize); + thumbs.BindInt(1, sum); + + if (thumbs.Step() == db::Row) { + thumbnailId = thumbs.ColumnInt(0); /* thumbnail already exists */ + } + + if (thumbnailId == 0) { /* doesn't exist yet, let's insert the record and write the file */ + db::Statement insertThumb("INSERT INTO thumbnails (filesize,checksum) VALUES (?,?)", connection); + insertThumb.BindInt(0, this->internalMetadata->thumbnailSize); + insertThumb.BindInt(1, sum); + + if (insertThumb.Step() == db::Done) { + thumbnailId = connection.LastInsertedId(); + + std::string filename = + libraryDirectory + + "thumbs/" + + boost::lexical_cast(thumbnailId) + + ".jpg"; + +#ifdef WIN32 + std::wstring wfilename = u8to16(filename); + FILE *thumbFile = _wfopen(wfilename.c_str(), _T("wb")); +#else + FILE *thumbFile = fopen(filename.c_str(), "wb"); +#endif + fwrite(this->internalMetadata->thumbnailData, sizeof(char), this->internalMetadata->thumbnailSize, thumbFile); + fclose(thumbFile); + } + } + } + + return thumbnailId; +} + +void IndexerTrack::ProcessNonStandardMetadata(db::Connection& connection) { + MetadataMap unknownFields(this->internalMetadata->metadata); + removeKnownFields(unknownFields); + + db::CachedStatement selectMetaKey("SELECT id FROM meta_keys WHERE name=?", connection); + db::CachedStatement selectMetaValue("SELECT id FROM meta_values WHERE meta_key_id=? AND content=?", connection); + db::CachedStatement insertMetaValue("INSERT INTO meta_values (meta_key_id,content) VALUES (?,?)", connection); + db::CachedStatement insertTrackMeta("INSERT INTO track_meta (track_id,meta_value_id) VALUES (?,?)", connection); + db::CachedStatement insertMetaKey("INSERT INTO meta_keys (name) VALUES (?)", connection); + + MetadataMap::const_iterator it = unknownFields.begin(); + for ( ; it != unknownFields.end(); ++it){ + DBID keyId = 0; + std::string key; + + /* lookup the ID for the key; insert if it doesn't exist.. */ + + selectMetaKey.BindText(0, it->first); + + if (selectMetaKey.Step() == db::Row) { + keyId = selectMetaKey.ColumnInt(0); + } + else { + insertMetaKey.BindText(0, it->first); + + if (insertMetaKey.Step() == db::Done) { + keyId = connection.LastInsertedId(); + } + } + + selectMetaKey.Reset(); + + if (keyId == 0) { + continue; /* welp... */ + } + + /* see if we already have the value as a normalized row in our table. + if we don't, insert it. */ + + DBID valueId = 0; + + selectMetaValue.BindInt(0, keyId); + selectMetaValue.BindText(1, it->second); + + if (selectMetaValue.Step() == db::Row) { + valueId = selectMetaValue.ColumnInt(0); + } + else { + insertMetaValue.BindInt(0, keyId); + insertMetaValue.BindText(1, it->second); + + if (insertMetaValue.Step() == db::Done) { + valueId = connection.LastInsertedId(); + } + + insertMetaValue.Reset(); + } + + selectMetaValue.Reset(); + + /* now that we have a keyId and a valueId, create the relationship */ + + if (valueId != 0) { + insertTrackMeta.BindInt(0, this->id); + insertTrackMeta.BindInt(1, valueId); + insertTrackMeta.Step(); + insertTrackMeta.Reset(); + } + } +} + +DBID IndexerTrack::SaveSingleValueField( + db::Connection& dbConnection, + const std::string& trackMetadataKeyName, + const std::string& fieldTableName) +{ + DBID id = 0; + + std::string selectQuery = boost::str(boost::format( + "SELECT id FROM %1% WHERE name=?") % fieldTableName); + + db::CachedStatement stmt(selectQuery.c_str(), dbConnection); + std::string value = this->GetValue(trackMetadataKeyName.c_str()); + + stmt.BindText(0, value); + + if (stmt.Step() == db::Row) { + id = stmt.ColumnInt(0); + } + else { + std::string insertStatement = boost::str(boost::format( + "INSERT INTO %1% (name) VALUES (?)") % fieldTableName); + + db::Statement insertValue(insertStatement.c_str(), dbConnection); + insertValue.BindText(0, value); + + if (insertValue.Step() == db::Done) { + id = dbConnection.LastInsertedId(); + } + } + + return id; +} + +DBID IndexerTrack::SaveMultiValueField( + db::Connection& connection, + const std::string& tracksTableColumnName, + const std::string& fieldTableName, + const std::string& junctionTableName, + const std::string& junctionTableForeignKeyColumnName) +{ + std::string aggregatedValue; + DBID fieldId = 0; + int count = 0; + + std::set processed; /* for deduping */ + + MetadataIteratorRange values = this->GetValues(tracksTableColumnName.c_str()); + while (values.first != values.second) { + if (processed.find(values.first->second) == processed.end()) { + processed.insert(values.first->second); + + std::string value = values.first->second; + + fieldId = SaveNormalizedFieldValue( + connection, + fieldTableName, + value, + false, + junctionTableName, + junctionTableForeignKeyColumnName); + + if (count != 0) { + aggregatedValue += ", "; + } + + aggregatedValue += value; + + ++count; + } + + ++values.first; + } + + if (count > 1 || fieldId == 0) { + fieldId = SaveNormalizedFieldValue( + connection, + fieldTableName, + aggregatedValue, + true); + } + + return fieldId; +} + +DBID IndexerTrack::SaveGenre(db::Connection& dbConnection) { + return this->SaveMultiValueField( + dbConnection, + GENRE_TRACK_COLUMN_NAME, + GENRES_TABLE_NAME, + GENRE_TRACK_JUNCTION_TABLE_NAME, + GENRE_TRACK_FOREIGN_KEY); +} + +DBID IndexerTrack::SaveArtist(db::Connection& dbConnection) { + return this->SaveMultiValueField( + dbConnection, + ARTIST_TRACK_COLUMN_NAME, + ARTISTS_TABLE_NAME, + ARTIST_TRACK_JUNCTION_TABLE_NAME, + ARTIST_TRACK_FOREIGN_KEY); +} + +bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirectory) { + db::ScopedTransaction transaction(dbConnection); + + if (this->GetValue("album_artist") == "") { + this->SetValue("album_artist", this->GetValue("artist").c_str()); + } + + /* remove existing relations -- we're going to update them with fresh data */ + + if (this->id != 0) { + removeRelation(dbConnection, "track_genres", this->id); + removeRelation(dbConnection, "track_artists", this->id); + removeRelation(dbConnection, "track_meta", this->id); + } + + /* write generic info to the tracks table */ + + this->id = writeToTracksTable(dbConnection, *this); + + DBID albumId = this->SaveSingleValueField(dbConnection, "album", "albums"); + DBID genreId = this->SaveGenre(dbConnection); + DBID artistId = this->SaveArtist(dbConnection); + DBID albumArtistId = this->SaveSingleValueField(dbConnection, "album_artist", "artists"); + DBID thumbnailId = this->SaveThumbnail(dbConnection, libraryDirectory); + + /* update all of the track foreign keys */ + + { + db::CachedStatement stmt( + "UPDATE tracks " \ + "SET album_id=?, visual_genre_id=?, visual_artist_id=?, album_artist_id=?, thumbnail_id=? " \ + "WHERE id=?", dbConnection); + + stmt.BindInt(0, albumId); + stmt.BindInt(1, genreId); + stmt.BindInt(2, artistId); + stmt.BindInt(3, albumArtistId); + stmt.BindInt(4, thumbnailId); + stmt.BindInt(5, this->id); + stmt.Step(); + } + + ProcessNonStandardMetadata(dbConnection); + + return true; +} + +DBID IndexerTrack::SaveNormalizedFieldValue( + db::Connection &dbConnection, + const std::string& tableName, + const std::string& fieldValue, + bool isAggregatedValue, + const std::string& relationJunctionTableName, + const std::string& relationJunctionTableColumn) +{ + DBID fieldId = 0; + + /* find by value */ + + { + std::string query = boost::str(boost::format("SELECT id FROM %1% WHERE name=?") % tableName); + db::CachedStatement stmt(query.c_str(), dbConnection); + stmt.BindText(0, fieldValue); + + if (stmt.Step() == db::Row) { + fieldId = stmt.ColumnInt(0); + } + } + + /* not found? insert. */ + + if (fieldId == 0) { + std::string query = boost::str(boost::format( + "INSERT INTO %1% (name, aggregated) VALUES (?, ?)") % tableName); + + db::CachedStatement stmt(query.c_str(), dbConnection); + stmt.BindText(0, fieldValue); + stmt.BindInt(1, isAggregatedValue ? 1 : 0); + + if (stmt.Step() == db::Done) { + fieldId = dbConnection.LastInsertedId(); + } + } + + /* if this is a normalized value it may need to be inserted into a + junction table. see if we were asked to do this... */ + + if (relationJunctionTableName.size() && relationJunctionTableColumn.size()) { + std::string query = boost::str(boost::format( + "INSERT INTO %1% (track_id, %2%) VALUES (?, ?)") + % relationJunctionTableName % relationJunctionTableColumn); + + db::Statement stmt(query.c_str(), dbConnection); + stmt.BindInt(0, this->id); + stmt.BindInt(1, fieldId); + stmt.Step(); + } + + return fieldId; +} + +TrackPtr IndexerTrack::Copy() { + return TrackPtr(new IndexerTrack(this->id)); +} + +IndexerTrack::MetadataWithThumbnail::MetadataWithThumbnail() +: thumbnailData(NULL) +, thumbnailSize(0) { +} + +IndexerTrack::MetadataWithThumbnail::~MetadataWithThumbnail() { + delete this->thumbnailData; +} diff --git a/src/core/library/track/LibraryTrack.cpp b/src/core/library/track/LibraryTrack.cpp index 543a4eae4..799dd87f8 100644 --- a/src/core/library/track/LibraryTrack.cpp +++ b/src/core/library/track/LibraryTrack.cpp @@ -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 -#include - -#include -#include -#include -#include - -#include -#include - -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(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 +#include + +#include +#include +#include +#include + +#include +#include + +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(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; } \ No newline at end of file diff --git a/src/core/library/track/Track.cpp b/src/core/library/track/Track.cpp index 52551dfa2..a68928df3 100644 --- a/src/core/library/track/Track.cpp +++ b/src/core/library/track/Track.cpp @@ -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 -#include - -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 +#include + +using namespace musik::core; + +Track::~Track() { +} + +DBID Track::Id() { + return 0; +} + +LibraryPtr Track::Library() { + static LibraryPtr nullLibrary; + return nullLibrary; +} + +int Track::LibraryId() { + return 0; +} diff --git a/src/core/pch.cpp b/src/core/pch.cpp index 14a3ec331..f36b566a3 100644 --- a/src/core/pch.cpp +++ b/src/core/pch.cpp @@ -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" diff --git a/src/core/playback/NonLibraryTrackHelper.cpp b/src/core/playback/NonLibraryTrackHelper.cpp index 62e84065e..19a98cced 100644 --- a/src/core/playback/NonLibraryTrackHelper.cpp +++ b/src/core/playback/NonLibraryTrackHelper.cpp @@ -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 -#include -#include -#include -#include - -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 Deleter; - typedef std::vector > MetadataReaderList; - - MetadataReaderList metadataReaders = - PluginFactory::Instance() .QueryInterface("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 +#include +#include +#include +#include + +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 Deleter; + typedef std::vector > MetadataReaderList; + + MetadataReaderList metadataReaders = + PluginFactory::Instance() .QueryInterface("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); + } + } + } +} diff --git a/src/core/playback/Transport.cpp b/src/core/playback/Transport.cpp index bab840da0..bfeb4bae6 100644 --- a/src/core/playback/Transport.cpp +++ b/src/core/playback/Transport.cpp @@ -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 -#include -#include -#include -#include - -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 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::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 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 +#include +#include +#include +#include + +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 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::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 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()); +} diff --git a/src/core/plugin/PluginFactory.cpp b/src/core/plugin/PluginFactory.cpp index 2c21c731e..029d754a1 100644 --- a/src/core/plugin/PluginFactory.cpp +++ b/src/core/plugin/PluginFactory.cpp @@ -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 -#include -#include -#include -#include - -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::iterator dll = this->loadedDlls.begin(); - for( ; dll != this->loadedDlls.end(); dll++) { - #ifdef WIN32 - FreeLibrary((HMODULE) (*dll)); - #else - dlclose(*dll); - #endif - } - - loadedDlls.clear(); -} - -#include - -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 +#include +#include +#include +#include + +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::iterator dll = this->loadedDlls.begin(); + for( ; dll != this->loadedDlls.end(); dll++) { + #ifdef WIN32 + FreeLibrary((HMODULE) (*dll)); + #else + dlclose(*dll); + #endif + } + + loadedDlls.clear(); +} + +#include + +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(...) { + } +} diff --git a/src/core/support/Common.cpp b/src/core/support/Common.cpp index e0bdd3490..e0b665129 100644 --- a/src/core/support/Common.cpp +++ b/src/core/support/Common.cpp @@ -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 -#include -#include -#include -#include - -#include - -#ifdef WIN32 - /* nothing special for Win32 */ -#elif __APPLE__ - #include -#else - #include - #include - #include - #include -#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 +#include +#include +#include +#include + +#include + +#ifdef WIN32 + /* nothing special for Win32 */ +#elif __APPLE__ + #include +#else + #include + #include + #include + #include +#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; +} diff --git a/src/core/support/Preferences.cpp b/src/core/support/Preferences.cpp index 068c059b7..03abb5acd 100644 --- a/src/core/support/Preferences.cpp +++ b/src/core/support/Preferences.cpp @@ -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 - -#include -#include - -#include -#include - -////////////////////////////////////////////////////////////////////////////// - -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(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(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 + +#include +#include + +#include +#include + +////////////////////////////////////////////////////////////////////////////// + +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(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(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)"); + +} + diff --git a/src/core/support/ThreadHelper.cpp b/src/core/support/ThreadHelper.cpp index 2986f5d9c..528fb5bbd 100644 --- a/src/core/support/ThreadHelper.cpp +++ b/src/core/support/ThreadHelper.cpp @@ -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 - -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 + +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(); +} diff --git a/src/core/support/Version.cpp b/src/core/support/Version.cpp index dc654cd31..791668cff 100644 --- a/src/core/support/Version.cpp +++ b/src/core/support/Version.cpp @@ -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 -#include - -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 +#include + +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); +} diff --git a/src/musikbox/app/layout/BrowseLayout.cpp b/src/musikbox/app/layout/BrowseLayout.cpp index aae41c517..7d93d7194 100755 --- a/src/musikbox/app/layout/BrowseLayout.cpp +++ b/src/musikbox/app/layout/BrowseLayout.cpp @@ -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 -#include -#include -#include -#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( - 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 +#include +#include +#include +#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( + 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); +} diff --git a/src/musikbox/app/layout/ConsoleLayout.cpp b/src/musikbox/app/layout/ConsoleLayout.cpp index b0c92e21a..303a274b0 100755 --- a/src/musikbox/app/layout/ConsoleLayout.cpp +++ b/src/musikbox/app/layout/ConsoleLayout.cpp @@ -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 -#include - -#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 +#include + +#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(); +} diff --git a/src/musikbox/app/layout/LibraryLayout.cpp b/src/musikbox/app/layout/LibraryLayout.cpp index 19f0b717f..da3274353 100755 --- a/src/musikbox/app/layout/LibraryLayout.cpp +++ b/src/musikbox/app/layout/LibraryLayout.cpp @@ -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 -#include - -#include - -#include - -#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 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 +#include + +#include + +#include + +#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 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); +} diff --git a/src/musikbox/app/layout/NowPlayingLayout.cpp b/src/musikbox/app/layout/NowPlayingLayout.cpp index 42972c3e4..92fd1431b 100755 --- a/src/musikbox/app/layout/NowPlayingLayout.cpp +++ b/src/musikbox/app/layout/NowPlayingLayout.cpp @@ -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 -#include -#include -#include -#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( - 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 +#include +#include +#include +#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( + 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); } \ No newline at end of file diff --git a/src/musikbox/app/query/CategoryListViewQuery.cpp b/src/musikbox/app/query/CategoryListViewQuery.cpp index 32531a552..1dbdcb75f 100755 --- a/src/musikbox/app/query/CategoryListViewQuery.cpp +++ b/src/musikbox/app/query/CategoryListViewQuery.cpp @@ -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 -#include - -#include - -#include - -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 >); - -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 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 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 +#include + +#include + +#include + +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 >); + +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 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 row(new Result()); + row->id = stmt.ColumnInt64(0); + row->displayValue = stmt.ColumnText(1); + result->push_back(row); + } + + return true; +} diff --git a/src/musikbox/app/query/CategoryTrackListQuery.cpp b/src/musikbox/app/query/CategoryTrackListQuery.cpp index a284bc34f..ab011ceb8 100755 --- a/src/musikbox/app/query/CategoryTrackListQuery.cpp +++ b/src/musikbox/app/query/CategoryTrackListQuery.cpp @@ -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 -#include -#include - -#include - -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 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()); - this->headers.reset(new std::set()); - 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()(parts); - } - - return this->hash; -} - -bool CategoryTrackListQuery::OnRun(Connection& db) { - if (result) { - result.reset(new std::vector()); - headers.reset(new std::set()); - } - - 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 +#include +#include + +#include + +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 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()); + this->headers.reset(new std::set()); + 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()(parts); + } + + return this->hash; +} + +bool CategoryTrackListQuery::OnRun(Connection& db) { + if (result) { + result.reset(new std::vector()); + headers.reset(new std::set()); + } + + 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; +} diff --git a/src/musikbox/app/query/NowPlayingTrackListQuery.cpp b/src/musikbox/app/query/NowPlayingTrackListQuery.cpp index 79f95c6ea..2d22b68c8 100755 --- a/src/musikbox/app/query/NowPlayingTrackListQuery.cpp +++ b/src/musikbox/app/query/NowPlayingTrackListQuery.cpp @@ -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 -#include -#include - -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()); - this->headers.reset(new std::set()); - 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()(this->Name()); - } - - return this->hash; -} - -bool NowPlayingTrackListQuery::OnRun(Connection& db) { - if (result) { - result.reset(new std::vector()); - headers.reset(new std::set()); - } - - this->playback.Copy(*result); - - return true; -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "NowPlayingTrackListQuery.h" + +#include +#include +#include + +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()); + this->headers.reset(new std::set()); + 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()(this->Name()); + } + + return this->hash; +} + +bool NowPlayingTrackListQuery::OnRun(Connection& db) { + if (result) { + result.reset(new std::vector()); + headers.reset(new std::set()); + } + + this->playback.Copy(*result); + + return true; +} diff --git a/src/musikbox/app/service/PlaybackService.cpp b/src/musikbox/app/service/PlaybackService.cpp index c30250594..316438787 100755 --- a/src/musikbox/app/service/PlaybackService.cpp +++ b/src/musikbox/app/service/PlaybackService.cpp @@ -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 -#include "PlaybackService.h" - -#include -#include - -#include -#include - -using musik::core::TrackPtr; -using musik::core::audio::Transport; - -using cursespp::IMessageTarget; -using cursespp::IMessage; - -using namespace musik::core::library::constants; -using namespace musik::box; - -#define NO_POSITION (size_t) -1 -#define URI_AT_INDEX(x) this->playlist.at(x)->URI() -#define PREVIOUS_GRACE_PERIOD 2.0f -#define MESSAGE_STREAM_EVENT 1000 -#define MESSAGE_PLAYBACK_EVENT 1001 - -PlaybackService::PlaybackService(Transport& transport) -: transport(transport) { - transport.StreamEvent.connect(this, &PlaybackService::OnStreamEvent); - transport.PlaybackEvent.connect(this, &PlaybackService::OnPlaybackEvent); - this->index = NO_POSITION; - this->nextIndex = NO_POSITION; -} - -void PlaybackService::ProcessMessage(IMessage &message) { - if (message.Type() == MESSAGE_STREAM_EVENT) { - int64 eventType = message.UserData1(); - - if (eventType == Transport::StreamAlmostDone) { - if (this->playlist.size() > this->index + 1) { - if (this->nextIndex != this->index + 1) { - this->nextIndex = this->index + 1; - this->transport.PrepareNextTrack(URI_AT_INDEX(nextIndex)); - } - } - } - else if (eventType == Transport::StreamPlaying) { - if (this->nextIndex != NO_POSITION) { - this->index = this->nextIndex; - this->nextIndex = NO_POSITION; - } - - if (this->index != NO_POSITION) { - this->TrackChanged(this->index, this->playlist.at(this->index)); - } - } - } - else if (message.Type() == MESSAGE_PLAYBACK_EVENT) { - int64 eventType = message.UserData1(); - - if (eventType == Transport::PlaybackStopped) { - this->TrackChanged(NO_POSITION, TrackPtr()); - } - } -} - -bool PlaybackService::Next() { - if (transport.GetPlaybackState() == Transport::PlaybackStopped) { - return false; - } - - if (this->playlist.size() > index + 1) { - this->Play(index + 1); - return true; - } - - return false; -} - -bool PlaybackService::Previous() { - if (transport.GetPlaybackState() == Transport::PlaybackStopped) { - return false; - } - - if (transport.Position() > PREVIOUS_GRACE_PERIOD) { - this->Play(index); - return true; - } - - if (index > 0) { - this->Play(index - 1); - return true; - } - - return false; -} - -void PlaybackService::Play(std::vector& tracks, size_t index) { - /* do the copy outside of the critical section, then swap. */ - std::vector temp; - std::copy(tracks.begin(), tracks.end(), std::back_inserter(temp)); - - { - boost::recursive_mutex::scoped_lock lock(this->stateMutex); - std::swap(temp, this->playlist); - } - - this->Play(index); -} - -void PlaybackService::Copy(std::vector& target) { - boost::recursive_mutex::scoped_lock lock(this->stateMutex); - std::copy(this->playlist.begin(), this->playlist.end(), std::back_inserter(target)); -} - -void PlaybackService::Play(size_t index) { - transport.Start(URI_AT_INDEX(index)); - this->nextIndex = NO_POSITION; - this->index = index; -} - -size_t PlaybackService::GetIndex() { - return this->index; -} - -TrackPtr PlaybackService::GetTrackAtIndex(size_t index) { - return this->playlist.at(index); -} - -void PlaybackService::OnStreamEvent(int eventType, std::string uri) { - cursespp::MessageQueue::Instance().Post( - cursespp::Message::Create(this, MESSAGE_STREAM_EVENT, eventType, 0)); -} - -void PlaybackService::OnPlaybackEvent(int eventType) { - cursespp::MessageQueue::Instance().Post( - cursespp::Message::Create(this, MESSAGE_PLAYBACK_EVENT, eventType, 0)); -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "PlaybackService.h" + +#include +#include + +#include +#include + +using musik::core::TrackPtr; +using musik::core::audio::Transport; + +using cursespp::IMessageTarget; +using cursespp::IMessage; + +using namespace musik::core::library::constants; +using namespace musik::box; + +#define NO_POSITION (size_t) -1 +#define URI_AT_INDEX(x) this->playlist.at(x)->URI() +#define PREVIOUS_GRACE_PERIOD 2.0f +#define MESSAGE_STREAM_EVENT 1000 +#define MESSAGE_PLAYBACK_EVENT 1001 + +PlaybackService::PlaybackService(Transport& transport) +: transport(transport) { + transport.StreamEvent.connect(this, &PlaybackService::OnStreamEvent); + transport.PlaybackEvent.connect(this, &PlaybackService::OnPlaybackEvent); + this->index = NO_POSITION; + this->nextIndex = NO_POSITION; +} + +void PlaybackService::ProcessMessage(IMessage &message) { + if (message.Type() == MESSAGE_STREAM_EVENT) { + int64 eventType = message.UserData1(); + + if (eventType == Transport::StreamAlmostDone) { + if (this->playlist.size() > this->index + 1) { + if (this->nextIndex != this->index + 1) { + this->nextIndex = this->index + 1; + this->transport.PrepareNextTrack(URI_AT_INDEX(nextIndex)); + } + } + } + else if (eventType == Transport::StreamPlaying) { + if (this->nextIndex != NO_POSITION) { + this->index = this->nextIndex; + this->nextIndex = NO_POSITION; + } + + if (this->index != NO_POSITION) { + this->TrackChanged(this->index, this->playlist.at(this->index)); + } + } + } + else if (message.Type() == MESSAGE_PLAYBACK_EVENT) { + int64 eventType = message.UserData1(); + + if (eventType == Transport::PlaybackStopped) { + this->TrackChanged(NO_POSITION, TrackPtr()); + } + } +} + +bool PlaybackService::Next() { + if (transport.GetPlaybackState() == Transport::PlaybackStopped) { + return false; + } + + if (this->playlist.size() > index + 1) { + this->Play(index + 1); + return true; + } + + return false; +} + +bool PlaybackService::Previous() { + if (transport.GetPlaybackState() == Transport::PlaybackStopped) { + return false; + } + + if (transport.Position() > PREVIOUS_GRACE_PERIOD) { + this->Play(index); + return true; + } + + if (index > 0) { + this->Play(index - 1); + return true; + } + + return false; +} + +void PlaybackService::Play(std::vector& tracks, size_t index) { + /* do the copy outside of the critical section, then swap. */ + std::vector temp; + std::copy(tracks.begin(), tracks.end(), std::back_inserter(temp)); + + { + boost::recursive_mutex::scoped_lock lock(this->stateMutex); + std::swap(temp, this->playlist); + } + + this->Play(index); +} + +void PlaybackService::Copy(std::vector& target) { + boost::recursive_mutex::scoped_lock lock(this->stateMutex); + std::copy(this->playlist.begin(), this->playlist.end(), std::back_inserter(target)); +} + +void PlaybackService::Play(size_t index) { + transport.Start(URI_AT_INDEX(index)); + this->nextIndex = NO_POSITION; + this->index = index; +} + +size_t PlaybackService::GetIndex() { + return this->index; +} + +TrackPtr PlaybackService::GetTrackAtIndex(size_t index) { + return this->playlist.at(index); +} + +void PlaybackService::OnStreamEvent(int eventType, std::string uri) { + cursespp::MessageQueue::Instance().Post( + cursespp::Message::Create(this, MESSAGE_STREAM_EVENT, eventType, 0)); +} + +void PlaybackService::OnPlaybackEvent(int eventType) { + cursespp::MessageQueue::Instance().Post( + cursespp::Message::Create(this, MESSAGE_PLAYBACK_EVENT, eventType, 0)); +} diff --git a/src/musikbox/app/util/GlobalHotkeys.cpp b/src/musikbox/app/util/GlobalHotkeys.cpp index 774835f34..acd166d99 100755 --- a/src/musikbox/app/util/GlobalHotkeys.cpp +++ b/src/musikbox/app/util/GlobalHotkeys.cpp @@ -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 "GlobalHotkeys.h" - -using musik::core::LibraryPtr; -using musik::core::audio::Transport; -using namespace musik::box; - -GlobalHotkeys::GlobalHotkeys(PlaybackService& playback, LibraryPtr library) -: playback(playback) -, transport(playback.GetTransport()) { - this->library = library; -} - -GlobalHotkeys::~GlobalHotkeys() { - -} - -bool GlobalHotkeys::Handle(const std::string& kn) { - if (kn == "^P") { - int state = this->transport.GetPlaybackState(); - if (state == Transport::PlaybackPaused) { - this->transport.Resume(); - } - else if (state == Transport::PlaybackPlaying) { - this->transport.Pause(); - } - return true; - } - if (kn == "ALT_I" || kn == "M-i") { - this->transport.SetVolume(this->transport.Volume() + 0.05); /* 5% */ - return true; - } - else if (kn == "ALT_K" || kn == "M-k") { - this->transport.SetVolume(this->transport.Volume() - 0.05); - return true; - } - else if (kn == "ALT_J" || kn == "M-j") { - this->playback.Previous(); - return true; - } - else if (kn == "ALT_L" || kn == "M-l") { - this->playback.Next(); - return true; - } - else if (kn == "ALT_U" || kn == "M-u") { - double time = this->transport.Position(); - this->transport.SetPosition(time - 10.0f); - return true; - } - else if (kn == "ALT_O" || kn == "M-o") { - double time = this->transport.Position(); - this->transport.SetPosition(time + 10.0f); - return true; - } - else if (kn == "^R") { - library->Indexer()->Synchronize(true); - return true; - } - - return false; -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "GlobalHotkeys.h" + +using musik::core::LibraryPtr; +using musik::core::audio::Transport; +using namespace musik::box; + +GlobalHotkeys::GlobalHotkeys(PlaybackService& playback, LibraryPtr library) +: playback(playback) +, transport(playback.GetTransport()) { + this->library = library; +} + +GlobalHotkeys::~GlobalHotkeys() { + +} + +bool GlobalHotkeys::Handle(const std::string& kn) { + if (kn == "^P") { + int state = this->transport.GetPlaybackState(); + if (state == Transport::PlaybackPaused) { + this->transport.Resume(); + } + else if (state == Transport::PlaybackPlaying) { + this->transport.Pause(); + } + return true; + } + if (kn == "ALT_I" || kn == "M-i") { + this->transport.SetVolume(this->transport.Volume() + 0.05); /* 5% */ + return true; + } + else if (kn == "ALT_K" || kn == "M-k") { + this->transport.SetVolume(this->transport.Volume() - 0.05); + return true; + } + else if (kn == "ALT_J" || kn == "M-j") { + this->playback.Previous(); + return true; + } + else if (kn == "ALT_L" || kn == "M-l") { + this->playback.Next(); + return true; + } + else if (kn == "ALT_U" || kn == "M-u") { + double time = this->transport.Position(); + this->transport.SetPosition(time - 10.0f); + return true; + } + else if (kn == "ALT_O" || kn == "M-o") { + double time = this->transport.Position(); + this->transport.SetPosition(time + 10.0f); + return true; + } + else if (kn == "^R") { + library->Indexer()->Synchronize(true); + return true; + } + + return false; +} diff --git a/src/musikbox/app/util/SystemInfo.cpp b/src/musikbox/app/util/SystemInfo.cpp index 960c2358a..8d5e2bf25 100755 --- a/src/musikbox/app/util/SystemInfo.cpp +++ b/src/musikbox/app/util/SystemInfo.cpp @@ -30,158 +30,158 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" - -#include "SystemInfo.h" - -using namespace musik::box; - -#ifdef WIN32 - -#include "windows.h" -#include "psapi.h" -#include "pdh.h" - -class WindowsSystemInfo : public SystemInfo { - public: - WindowsSystemInfo(); - virtual ~WindowsSystemInfo(); - - virtual int64 GetTotalVirtualMemory(); - virtual int64 GetUsedVirtualMemory(); - virtual int64 GetTotalPhysicalMemory(); - virtual int64 GetUsedPhysicalMemory(); - virtual double GetCpuUsage(); - - private: - ULARGE_INTEGER lastCpu, lastSysCpu, lastUserCpu; - int processorCount; - HANDLE self; - PDH_HQUERY cpuQuery; - PDH_HCOUNTER cpuTotal; -}; -#endif - -SystemInfo* SystemInfo::Create() { -#ifdef WIN32 - return new WindowsSystemInfo(); -#else - return new SystemInfo(); -#endif -} - -SystemInfo::SystemInfo() { - -} - -SystemInfo::~SystemInfo() { - -} - -int64 SystemInfo::GetTotalVirtualMemory() { - return 0; -} - -int64 SystemInfo::GetUsedVirtualMemory() { - return 0; -} - -int64 SystemInfo::GetTotalPhysicalMemory() { - return 0; -} - -int64 SystemInfo::GetUsedPhysicalMemory() { - return 0; -} - -double SystemInfo::GetCpuUsage() { - return 0; -} - - -#ifdef WIN32 - -#define GET_MEMORY_STATUS \ - MEMORYSTATUSEX memInfo; \ - memInfo.dwLength = sizeof(MEMORYSTATUSEX); \ - GlobalMemoryStatusEx(&memInfo); \ - -#define GET_PROCESS_MEMORY_COUNTERS \ - PROCESS_MEMORY_COUNTERS_EX pmc; \ - GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)); \ - -WindowsSystemInfo::WindowsSystemInfo() { - PdhOpenQuery(NULL, NULL, &cpuQuery); - PdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal); - PdhCollectQueryData(cpuQuery); - - SYSTEM_INFO sysInfo; - FILETIME ftime, fsys, fuser; - - GetSystemInfo(&sysInfo); - processorCount = sysInfo.dwNumberOfProcessors; - - GetSystemTimeAsFileTime(&ftime); - memcpy(&lastCpu, &ftime, sizeof(FILETIME)); - - self = GetCurrentProcess(); - GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); - memcpy(&lastSysCpu, &fsys, sizeof(FILETIME)); - memcpy(&lastUserCpu, &fuser, sizeof(FILETIME)); -} - -WindowsSystemInfo::~WindowsSystemInfo() { - PdhCloseQuery(cpuQuery); -} - -int64 WindowsSystemInfo::GetTotalVirtualMemory() { - GET_MEMORY_STATUS - return memInfo.ullTotalPageFile; -} - -int64 WindowsSystemInfo::GetUsedVirtualMemory() { - GET_PROCESS_MEMORY_COUNTERS - return pmc.PrivateUsage; -} - -int64 WindowsSystemInfo::GetTotalPhysicalMemory() { - GET_MEMORY_STATUS - return memInfo.ullTotalPhys; -} - -int64 WindowsSystemInfo::GetUsedPhysicalMemory() { - GET_PROCESS_MEMORY_COUNTERS - return pmc.WorkingSetSize; -} - -double WindowsSystemInfo::GetCpuUsage() { - FILETIME ftime, fsys, fuser; - ULARGE_INTEGER now, sys, user; - double percent; - - GetSystemTimeAsFileTime(&ftime); - memcpy(&now, &ftime, sizeof(FILETIME)); - - GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); - memcpy(&sys, &fsys, sizeof(FILETIME)); - memcpy(&user, &fuser, sizeof(FILETIME)); - - percent = - (double) (sys.QuadPart - lastSysCpu.QuadPart) + - (double) (user.QuadPart - lastUserCpu.QuadPart); - - ULONGLONG diff = now.QuadPart - lastCpu.QuadPart; - diff = std::max((ULONGLONG) 1, diff); - - percent /= diff; - percent /= processorCount; - - lastCpu = now; - lastUserCpu = user; - lastSysCpu = sys; - - return (percent * 100.0f); -} -#endif +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "SystemInfo.h" + +using namespace musik::box; + +#ifdef WIN32 + +#include "windows.h" +#include "psapi.h" +#include "pdh.h" + +class WindowsSystemInfo : public SystemInfo { + public: + WindowsSystemInfo(); + virtual ~WindowsSystemInfo(); + + virtual int64 GetTotalVirtualMemory(); + virtual int64 GetUsedVirtualMemory(); + virtual int64 GetTotalPhysicalMemory(); + virtual int64 GetUsedPhysicalMemory(); + virtual double GetCpuUsage(); + + private: + ULARGE_INTEGER lastCpu, lastSysCpu, lastUserCpu; + int processorCount; + HANDLE self; + PDH_HQUERY cpuQuery; + PDH_HCOUNTER cpuTotal; +}; +#endif + +SystemInfo* SystemInfo::Create() { +#ifdef WIN32 + return new WindowsSystemInfo(); +#else + return new SystemInfo(); +#endif +} + +SystemInfo::SystemInfo() { + +} + +SystemInfo::~SystemInfo() { + +} + +int64 SystemInfo::GetTotalVirtualMemory() { + return 0; +} + +int64 SystemInfo::GetUsedVirtualMemory() { + return 0; +} + +int64 SystemInfo::GetTotalPhysicalMemory() { + return 0; +} + +int64 SystemInfo::GetUsedPhysicalMemory() { + return 0; +} + +double SystemInfo::GetCpuUsage() { + return 0; +} + + +#ifdef WIN32 + +#define GET_MEMORY_STATUS \ + MEMORYSTATUSEX memInfo; \ + memInfo.dwLength = sizeof(MEMORYSTATUSEX); \ + GlobalMemoryStatusEx(&memInfo); \ + +#define GET_PROCESS_MEMORY_COUNTERS \ + PROCESS_MEMORY_COUNTERS_EX pmc; \ + GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)); \ + +WindowsSystemInfo::WindowsSystemInfo() { + PdhOpenQuery(NULL, NULL, &cpuQuery); + PdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal); + PdhCollectQueryData(cpuQuery); + + SYSTEM_INFO sysInfo; + FILETIME ftime, fsys, fuser; + + GetSystemInfo(&sysInfo); + processorCount = sysInfo.dwNumberOfProcessors; + + GetSystemTimeAsFileTime(&ftime); + memcpy(&lastCpu, &ftime, sizeof(FILETIME)); + + self = GetCurrentProcess(); + GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); + memcpy(&lastSysCpu, &fsys, sizeof(FILETIME)); + memcpy(&lastUserCpu, &fuser, sizeof(FILETIME)); +} + +WindowsSystemInfo::~WindowsSystemInfo() { + PdhCloseQuery(cpuQuery); +} + +int64 WindowsSystemInfo::GetTotalVirtualMemory() { + GET_MEMORY_STATUS + return memInfo.ullTotalPageFile; +} + +int64 WindowsSystemInfo::GetUsedVirtualMemory() { + GET_PROCESS_MEMORY_COUNTERS + return pmc.PrivateUsage; +} + +int64 WindowsSystemInfo::GetTotalPhysicalMemory() { + GET_MEMORY_STATUS + return memInfo.ullTotalPhys; +} + +int64 WindowsSystemInfo::GetUsedPhysicalMemory() { + GET_PROCESS_MEMORY_COUNTERS + return pmc.WorkingSetSize; +} + +double WindowsSystemInfo::GetCpuUsage() { + FILETIME ftime, fsys, fuser; + ULARGE_INTEGER now, sys, user; + double percent; + + GetSystemTimeAsFileTime(&ftime); + memcpy(&now, &ftime, sizeof(FILETIME)); + + GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); + memcpy(&sys, &fsys, sizeof(FILETIME)); + memcpy(&user, &fuser, sizeof(FILETIME)); + + percent = + (double) (sys.QuadPart - lastSysCpu.QuadPart) + + (double) (user.QuadPart - lastUserCpu.QuadPart); + + ULONGLONG diff = now.QuadPart - lastCpu.QuadPart; + diff = std::max((ULONGLONG) 1, diff); + + percent /= diff; + percent /= processorCount; + + lastCpu = now; + lastUserCpu = user; + lastSysCpu = sys; + + return (percent * 100.0f); +} +#endif diff --git a/src/musikbox/app/util/Text.cpp b/src/musikbox/app/util/Text.cpp index 12ba78bfa..658055cf7 100755 --- a/src/musikbox/app/util/Text.cpp +++ b/src/musikbox/app/util/Text.cpp @@ -30,65 +30,65 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" -#include "Text.h" - -#include - -namespace musik { - namespace box { - namespace text { - void Truncate(std::string& str, size_t len) { - /* not a simple substr anymore, gotta deal with multi-byte - characters... */ - if (u8len(str) > len) { - std::string::iterator it = str.begin(); - std::string::iterator end = str.end(); - - size_t c = 0; - while (c < len && it != str.end()) { - try { - utf8::next(it, end); - } - catch (...) { - /* invalid encoding, just treat as a single char */ - ++it; - } - - ++c; - } - - str = std::string(str.begin(), it); - } - } - - void Ellipsize(std::string& str, size_t len) { - if (u8len(str) > len) { - Truncate(str, len - 2); - str += ".."; - } - } - - std::string Duration(int seconds) { - int mins = (seconds / 60); - int secs = seconds - (mins * 60); - return boost::str(boost::format("%d:%02d") % mins % secs); - } - - std::string Duration(double seconds) { - return Duration((int) round(seconds)); - } - - std::string Duration(std::string& str) { - if (str.size()) { - int seconds = boost::lexical_cast(str); - return Duration(seconds); - } - - return "0:00"; - } - } - } -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "Text.h" + +#include + +namespace musik { + namespace box { + namespace text { + void Truncate(std::string& str, size_t len) { + /* not a simple substr anymore, gotta deal with multi-byte + characters... */ + if (u8len(str) > len) { + std::string::iterator it = str.begin(); + std::string::iterator end = str.end(); + + size_t c = 0; + while (c < len && it != str.end()) { + try { + utf8::next(it, end); + } + catch (...) { + /* invalid encoding, just treat as a single char */ + ++it; + } + + ++c; + } + + str = std::string(str.begin(), it); + } + } + + void Ellipsize(std::string& str, size_t len) { + if (u8len(str) > len) { + Truncate(str, len - 2); + str += ".."; + } + } + + std::string Duration(int seconds) { + int mins = (seconds / 60); + int secs = seconds - (mins * 60); + return boost::str(boost::format("%d:%02d") % mins % secs); + } + + std::string Duration(double seconds) { + return Duration((int) round(seconds)); + } + + std::string Duration(std::string& str) { + if (str.size()) { + int seconds = boost::lexical_cast(str); + return Duration(seconds); + } + + return "0:00"; + } + } + } +} diff --git a/src/musikbox/app/window/CategoryListView.cpp b/src/musikbox/app/window/CategoryListView.cpp index 3e5e68a44..04d853e47 100755 --- a/src/musikbox/app/window/CategoryListView.cpp +++ b/src/musikbox/app/window/CategoryListView.cpp @@ -30,111 +30,111 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" -#include - -#include -#include -#include - -#include - -#include -#include - -#include "CategoryListView.h" - -using musik::core::LibraryPtr; -using musik::core::IQuery; -using namespace musik::core::library::constants; -using namespace musik::box; -using cursespp::SingleLineEntry; - -#define WINDOW_MESSAGE_QUERY_COMPLETED 1002 - -CategoryListView::CategoryListView(LibraryPtr library, const std::string& fieldName) -: ListWindow(NULL) { - this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); - this->library = library; - this->library->QueryCompleted.connect(this, &CategoryListView::OnQueryCompleted); - this->fieldName = fieldName; - this->adapter = new Adapter(*this); -} - -CategoryListView::~CategoryListView() { - delete adapter; -} - -void CategoryListView::Requery() { - if (this->activeQuery) { - this->activeQuery->Cancel(); - } - - this->activeQuery.reset(new CategoryListViewQuery(this->fieldName)); - this->library->Enqueue(activeQuery); -} - -DBID CategoryListView::GetSelectedId() { - size_t index = this->GetSelectedIndex(); - if (index != NO_SELECTION && this->metadata && index < this->metadata->size()) { - return this->metadata->at(index)->id; - } - return -1; -} - -std::string CategoryListView::GetFieldName() { - return this->fieldName; -} - -void CategoryListView::SetFieldName(const std::string& fieldName) { - if (this->fieldName != fieldName) { - this->fieldName = fieldName; - - if (this->metadata) { - this->metadata.reset(); - } - - this->Requery(); - } -} - -void CategoryListView::OnQueryCompleted(QueryPtr query) { - if (query == this->activeQuery) { - this->PostMessage(WINDOW_MESSAGE_QUERY_COMPLETED); - } -} - -void CategoryListView::ProcessMessage(IMessage &message) { - if (message.Type() == WINDOW_MESSAGE_QUERY_COMPLETED) { - if (this->activeQuery && this->activeQuery->GetStatus() == IQuery::Finished) { - this->metadata = activeQuery->GetResult(); - activeQuery.reset(); - this->OnAdapterChanged(); - this->OnInvalidated(); - } - } -} - -IScrollAdapter& CategoryListView::GetScrollAdapter() { - return *adapter; -} - -CategoryListView::Adapter::Adapter(CategoryListView &parent) -: parent(parent) { -} - -size_t CategoryListView::Adapter::GetEntryCount() { - return parent.metadata ? parent.metadata->size() : 0; -} - -IScrollAdapter::EntryPtr CategoryListView::Adapter::GetEntry(size_t index) { - std::string value = parent.metadata->at(index)->displayValue; - text::Ellipsize(value, this->GetWidth()); - - int64 attrs = (index == parent.GetSelectedIndex()) ? COLOR_PAIR(BOX_COLOR_BLACK_ON_GREEN) : -1LL; - std::shared_ptr entry(new SingleLineEntry(value)); - entry->SetAttrs(attrs); - return entry; -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include + +#include +#include +#include + +#include + +#include +#include + +#include "CategoryListView.h" + +using musik::core::LibraryPtr; +using musik::core::IQuery; +using namespace musik::core::library::constants; +using namespace musik::box; +using cursespp::SingleLineEntry; + +#define WINDOW_MESSAGE_QUERY_COMPLETED 1002 + +CategoryListView::CategoryListView(LibraryPtr library, const std::string& fieldName) +: ListWindow(NULL) { + this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); + this->library = library; + this->library->QueryCompleted.connect(this, &CategoryListView::OnQueryCompleted); + this->fieldName = fieldName; + this->adapter = new Adapter(*this); +} + +CategoryListView::~CategoryListView() { + delete adapter; +} + +void CategoryListView::Requery() { + if (this->activeQuery) { + this->activeQuery->Cancel(); + } + + this->activeQuery.reset(new CategoryListViewQuery(this->fieldName)); + this->library->Enqueue(activeQuery); +} + +DBID CategoryListView::GetSelectedId() { + size_t index = this->GetSelectedIndex(); + if (index != NO_SELECTION && this->metadata && index < this->metadata->size()) { + return this->metadata->at(index)->id; + } + return -1; +} + +std::string CategoryListView::GetFieldName() { + return this->fieldName; +} + +void CategoryListView::SetFieldName(const std::string& fieldName) { + if (this->fieldName != fieldName) { + this->fieldName = fieldName; + + if (this->metadata) { + this->metadata.reset(); + } + + this->Requery(); + } +} + +void CategoryListView::OnQueryCompleted(QueryPtr query) { + if (query == this->activeQuery) { + this->PostMessage(WINDOW_MESSAGE_QUERY_COMPLETED); + } +} + +void CategoryListView::ProcessMessage(IMessage &message) { + if (message.Type() == WINDOW_MESSAGE_QUERY_COMPLETED) { + if (this->activeQuery && this->activeQuery->GetStatus() == IQuery::Finished) { + this->metadata = activeQuery->GetResult(); + activeQuery.reset(); + this->OnAdapterChanged(); + this->OnInvalidated(); + } + } +} + +IScrollAdapter& CategoryListView::GetScrollAdapter() { + return *adapter; +} + +CategoryListView::Adapter::Adapter(CategoryListView &parent) +: parent(parent) { +} + +size_t CategoryListView::Adapter::GetEntryCount() { + return parent.metadata ? parent.metadata->size() : 0; +} + +IScrollAdapter::EntryPtr CategoryListView::Adapter::GetEntry(size_t index) { + std::string value = parent.metadata->at(index)->displayValue; + text::Ellipsize(value, this->GetWidth()); + + int64 attrs = (index == parent.GetSelectedIndex()) ? COLOR_PAIR(BOX_COLOR_BLACK_ON_GREEN) : -1LL; + std::shared_ptr entry(new SingleLineEntry(value)); + entry->SetAttrs(attrs); + return entry; +} diff --git a/src/musikbox/app/window/CommandWindow.cpp b/src/musikbox/app/window/CommandWindow.cpp index 3dc6694d2..f112a2922 100755 --- a/src/musikbox/app/window/CommandWindow.cpp +++ b/src/musikbox/app/window/CommandWindow.cpp @@ -30,271 +30,271 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" - -#include -#include -#include -#include - -#include "CommandWindow.h" - -#include -#include -#include -#include -#include -#include - -#include - -#define MAX_SIZE 2046 - -using musik::core::Indexer; -using musik::core::IndexerPtr; -using musik::core::LibraryFactory; -using musik::core::LibraryPtr; -using musik::core::TrackPtr; -using musik::core::QueryPtr; - -using namespace musik::core::audio; -using namespace musik::box; -using namespace cursespp; - -template -bool tostr(T& t, const std::string& s) { - std::istringstream iss(s); - return !(iss >> t).fail(); -} - -inline static void redrawContents(IWindow &window, const std::string& text) { - WINDOW* c = window.GetContent(); - werase(c); - wprintw(c, text.c_str()); - window.Repaint(); -} - -CommandWindow::CommandWindow( - IWindow *parent, - Transport& transport, - LibraryPtr library, - OutputWindow& output, - LogWindow& logWindow) -: Window(parent) { - this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); - this->transport = &transport; - this->library = library; - this->bufferLength = 0; - this->output = &output; - this->logWindow = &logWindow; - this->paused = false; - this->Help(); -} - -CommandWindow::~CommandWindow() { -} - -void CommandWindow::Show() { - Window::Show(); - redrawContents(*this, buffer); -} - -void CommandWindow::Focus() { - -} - -void removeUtf8Char(std::string& value) { - std::string::iterator it = value.end(); - std::string::iterator start = value.begin(); - if (it != start) { - utf8::prior(it, start); - value = std::string(value.begin(), it); - } -} - -void CommandWindow::Write(const std::string& key) { - if (key == "^H" || key == "^?" || key == "KEY_BACKSPACE") { /* backspace */ - removeUtf8Char(this->buffer); - } - else if (key == "^M") { /* return */ - output->WriteLine("> " + this->buffer + "\n", COLOR_PAIR(BOX_COLOR_BLACK_ON_GREY)); - - if (!this->ProcessCommand(this->buffer)) { - if (this->buffer.size()) { - output->WriteLine( - "illegal command: '" + - this->buffer + - "'\n", COLOR_PAIR(BOX_COLOR_RED_ON_GREY)); - } - } - - this->buffer = ""; - } - else { - /* one character at a time. if it's more than one character, we're - dealing with an escape sequence and should not print it. */ - if (u8len(key) == 1) { - this->buffer += key; - } - } - - this->bufferLength = u8len(buffer); - - redrawContents(*this, buffer); -} - -void CommandWindow::Seek(const std::vector& args) { - if (args.size() > 0) { - double newPosition = 0; - if (tostr(newPosition, args[0])) { - transport->SetPosition(newPosition); - } - } -} - -void CommandWindow::SetVolume(const std::vector& args) { - if (args.size() > 0) { - float newVolume = 0; - if (tostr(newVolume, args[0])) { - this->SetVolume(newVolume / 100.0f); - } - } -} - -void CommandWindow::SetVolume(float volume) { - transport->SetVolume(volume); -} - -void CommandWindow::Help() { - int64 s = -1; - this->output->WriteLine("help:\n", s); - this->output->WriteLine(" to switch between windows", s); - this->output->WriteLine(" ALT+Q console view", s); - this->output->WriteLine(" ALT+W library view", s); - this->output->WriteLine("", s); - this->output->WriteLine(" addir : add a music directory", s); - this->output->WriteLine(" rmdir : remove a music directory", s); - this->output->WriteLine(" lsdirs: list scanned directories", s); - this->output->WriteLine(" rescan: rescan paths for new metadata", s); - this->output->WriteLine("", s); - this->output->WriteLine(" play : play audio at ", s); - this->output->WriteLine(" pause: pause/resume", s); - this->output->WriteLine(" stop: stop and clean up everything", s); - this->output->WriteLine(" volume: <0 - 100>: set % volume", s); - this->output->WriteLine(" clear: clear the log window", s); - this->output->WriteLine(" seek : seek to into track", s); - this->output->WriteLine("", s); - this->output->WriteLine(" plugins: list loaded plugins", s); - this->output->WriteLine("", s); - this->output->WriteLine(" : quit\n", s); -} - -bool CommandWindow::ProcessCommand(const std::string& cmd) { - std::vector args; - boost::algorithm::split(args, cmd, boost::is_any_of(" ")); - - std::string name = args.size() > 0 ? args[0] : ""; - args.erase(args.begin()); - - if (name == "plugins") { - this->ListPlugins(); - } - else if (name == "clear") { - this->logWindow->ClearContents(); - } - else if (name == "play" || name == "pl" || name == "p") { - return this->PlayFile(args); - } - else if (name == "addir") { - std::string path = boost::algorithm::join(args, " "); - library->Indexer()->AddPath(path); - } - else if (name == "rmdir") { - std::string path = boost::algorithm::join(args, " "); - library->Indexer()->RemovePath(path); - } - else if (name == "lsdirs") { - std::vector paths; - library->Indexer()->GetPaths(paths); - this->output->WriteLine("paths:"); - for (size_t i = 0; i < paths.size(); i++) { - this->output->WriteLine(" " + paths.at(i)); - } - this->output->WriteLine(""); - } - else if (name == "rescan" || name == "scan" || name == "index") { - library->Indexer()->Synchronize(); - } - else if (name == "h" || name == "help") { - this->Help(); - } - else if (cmd == "pa" || cmd == "pause") { - this->Pause(); - } - else if (cmd == "s" || cmd =="stop") { - this->Stop(); - } - else if (name == "sk" || name == "seek") { - this->Seek(args); - } - else if (name == "plugins") { - this->ListPlugins(); - } - else if (name == "v" || name == "volume") { - this->SetVolume(args); - } - else { - return false; - } - - - return true; -} - -bool CommandWindow::PlayFile(const std::vector& args) { - if (args.size() > 0) { - std::string filename = boost::algorithm::join(args, " "); - transport->Start(filename); - return true; - } - - return false; -} - -void CommandWindow::Pause() { - if (this->paused) { - transport->Resume(); - this->paused = !this->paused; - } - else { - transport->Pause(); - this->paused = !this->paused; - } -} - -void CommandWindow::Stop() { - this->transport->Stop(); -} - -void CommandWindow::ListPlugins() const { - using musik::core::IPlugin; - using musik::core::PluginFactory; - - typedef std::vector > PluginList; - typedef PluginFactory::NullDeleter Deleter; - - PluginList plugins = PluginFactory::Instance() - .QueryInterface("GetPlugin"); - - PluginList::iterator it = plugins.begin(); - for (; it != plugins.end(); it++) { - std::string format = - " " + std::string((*it)->Name()) + " " - "v" + std::string((*it)->Version()) + "\n" - " by " + std::string((*it)->Author()) + "\n"; - - this->output->WriteLine(format, BOX_COLOR_RED_ON_BLUE); - } -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include +#include +#include +#include + +#include "CommandWindow.h" + +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_SIZE 2046 + +using musik::core::Indexer; +using musik::core::IndexerPtr; +using musik::core::LibraryFactory; +using musik::core::LibraryPtr; +using musik::core::TrackPtr; +using musik::core::QueryPtr; + +using namespace musik::core::audio; +using namespace musik::box; +using namespace cursespp; + +template +bool tostr(T& t, const std::string& s) { + std::istringstream iss(s); + return !(iss >> t).fail(); +} + +inline static void redrawContents(IWindow &window, const std::string& text) { + WINDOW* c = window.GetContent(); + werase(c); + wprintw(c, text.c_str()); + window.Repaint(); +} + +CommandWindow::CommandWindow( + IWindow *parent, + Transport& transport, + LibraryPtr library, + OutputWindow& output, + LogWindow& logWindow) +: Window(parent) { + this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); + this->transport = &transport; + this->library = library; + this->bufferLength = 0; + this->output = &output; + this->logWindow = &logWindow; + this->paused = false; + this->Help(); +} + +CommandWindow::~CommandWindow() { +} + +void CommandWindow::Show() { + Window::Show(); + redrawContents(*this, buffer); +} + +void CommandWindow::Focus() { + +} + +void removeUtf8Char(std::string& value) { + std::string::iterator it = value.end(); + std::string::iterator start = value.begin(); + if (it != start) { + utf8::prior(it, start); + value = std::string(value.begin(), it); + } +} + +void CommandWindow::Write(const std::string& key) { + if (key == "^H" || key == "^?" || key == "KEY_BACKSPACE") { /* backspace */ + removeUtf8Char(this->buffer); + } + else if (key == "^M") { /* return */ + output->WriteLine("> " + this->buffer + "\n", COLOR_PAIR(BOX_COLOR_BLACK_ON_GREY)); + + if (!this->ProcessCommand(this->buffer)) { + if (this->buffer.size()) { + output->WriteLine( + "illegal command: '" + + this->buffer + + "'\n", COLOR_PAIR(BOX_COLOR_RED_ON_GREY)); + } + } + + this->buffer = ""; + } + else { + /* one character at a time. if it's more than one character, we're + dealing with an escape sequence and should not print it. */ + if (u8len(key) == 1) { + this->buffer += key; + } + } + + this->bufferLength = u8len(buffer); + + redrawContents(*this, buffer); +} + +void CommandWindow::Seek(const std::vector& args) { + if (args.size() > 0) { + double newPosition = 0; + if (tostr(newPosition, args[0])) { + transport->SetPosition(newPosition); + } + } +} + +void CommandWindow::SetVolume(const std::vector& args) { + if (args.size() > 0) { + float newVolume = 0; + if (tostr(newVolume, args[0])) { + this->SetVolume(newVolume / 100.0f); + } + } +} + +void CommandWindow::SetVolume(float volume) { + transport->SetVolume(volume); +} + +void CommandWindow::Help() { + int64 s = -1; + this->output->WriteLine("help:\n", s); + this->output->WriteLine(" to switch between windows", s); + this->output->WriteLine(" ALT+Q console view", s); + this->output->WriteLine(" ALT+W library view", s); + this->output->WriteLine("", s); + this->output->WriteLine(" addir : add a music directory", s); + this->output->WriteLine(" rmdir : remove a music directory", s); + this->output->WriteLine(" lsdirs: list scanned directories", s); + this->output->WriteLine(" rescan: rescan paths for new metadata", s); + this->output->WriteLine("", s); + this->output->WriteLine(" play : play audio at ", s); + this->output->WriteLine(" pause: pause/resume", s); + this->output->WriteLine(" stop: stop and clean up everything", s); + this->output->WriteLine(" volume: <0 - 100>: set % volume", s); + this->output->WriteLine(" clear: clear the log window", s); + this->output->WriteLine(" seek : seek to into track", s); + this->output->WriteLine("", s); + this->output->WriteLine(" plugins: list loaded plugins", s); + this->output->WriteLine("", s); + this->output->WriteLine(" : quit\n", s); +} + +bool CommandWindow::ProcessCommand(const std::string& cmd) { + std::vector args; + boost::algorithm::split(args, cmd, boost::is_any_of(" ")); + + std::string name = args.size() > 0 ? args[0] : ""; + args.erase(args.begin()); + + if (name == "plugins") { + this->ListPlugins(); + } + else if (name == "clear") { + this->logWindow->ClearContents(); + } + else if (name == "play" || name == "pl" || name == "p") { + return this->PlayFile(args); + } + else if (name == "addir") { + std::string path = boost::algorithm::join(args, " "); + library->Indexer()->AddPath(path); + } + else if (name == "rmdir") { + std::string path = boost::algorithm::join(args, " "); + library->Indexer()->RemovePath(path); + } + else if (name == "lsdirs") { + std::vector paths; + library->Indexer()->GetPaths(paths); + this->output->WriteLine("paths:"); + for (size_t i = 0; i < paths.size(); i++) { + this->output->WriteLine(" " + paths.at(i)); + } + this->output->WriteLine(""); + } + else if (name == "rescan" || name == "scan" || name == "index") { + library->Indexer()->Synchronize(); + } + else if (name == "h" || name == "help") { + this->Help(); + } + else if (cmd == "pa" || cmd == "pause") { + this->Pause(); + } + else if (cmd == "s" || cmd =="stop") { + this->Stop(); + } + else if (name == "sk" || name == "seek") { + this->Seek(args); + } + else if (name == "plugins") { + this->ListPlugins(); + } + else if (name == "v" || name == "volume") { + this->SetVolume(args); + } + else { + return false; + } + + + return true; +} + +bool CommandWindow::PlayFile(const std::vector& args) { + if (args.size() > 0) { + std::string filename = boost::algorithm::join(args, " "); + transport->Start(filename); + return true; + } + + return false; +} + +void CommandWindow::Pause() { + if (this->paused) { + transport->Resume(); + this->paused = !this->paused; + } + else { + transport->Pause(); + this->paused = !this->paused; + } +} + +void CommandWindow::Stop() { + this->transport->Stop(); +} + +void CommandWindow::ListPlugins() const { + using musik::core::IPlugin; + using musik::core::PluginFactory; + + typedef std::vector > PluginList; + typedef PluginFactory::NullDeleter Deleter; + + PluginList plugins = PluginFactory::Instance() + .QueryInterface("GetPlugin"); + + PluginList::iterator it = plugins.begin(); + for (; it != plugins.end(); it++) { + std::string format = + " " + std::string((*it)->Name()) + " " + "v" + std::string((*it)->Version()) + "\n" + " by " + std::string((*it)->Author()) + "\n"; + + this->output->WriteLine(format, BOX_COLOR_RED_ON_BLUE); + } +} diff --git a/src/musikbox/app/window/EntryWithHeader.cpp b/src/musikbox/app/window/EntryWithHeader.cpp index 84e9aa186..41b94c012 100755 --- a/src/musikbox/app/window/EntryWithHeader.cpp +++ b/src/musikbox/app/window/EntryWithHeader.cpp @@ -30,49 +30,49 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include "EntryWithHeader.h" -#include -#include - -using namespace musik::box; - -EntryWithHeader::EntryWithHeader( - const std::string& header, const std::string& value) -{ - this->header = header; - this->value = value; - this->attrs = -1; -} - -void EntryWithHeader::SetWidth(size_t width) { - this->width = width; -} - -int64 EntryWithHeader::GetAttrs(size_t line) { - return (line == 0) ? this->headerAttrs : this->attrs; -} - -void EntryWithHeader::SetAttrs(int64 headerAttrs, int64 attrs) { - this->headerAttrs = headerAttrs; - this->attrs = attrs; -} - -size_t EntryWithHeader::GetLineCount() { - return 2; -} - -#define TRUNCATE(value, width) \ - u8substr(value, 0, width > 0 ? width : 0) - -std::string EntryWithHeader::GetLine(size_t line) { - if (line == 0) { - std::string ellipsized = this->header; - musik::box::text::Ellipsize(ellipsized, this->width); - return ellipsized; - } - - return TRUNCATE(this->value, this->width); -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "EntryWithHeader.h" +#include +#include + +using namespace musik::box; + +EntryWithHeader::EntryWithHeader( + const std::string& header, const std::string& value) +{ + this->header = header; + this->value = value; + this->attrs = -1; +} + +void EntryWithHeader::SetWidth(size_t width) { + this->width = width; +} + +int64 EntryWithHeader::GetAttrs(size_t line) { + return (line == 0) ? this->headerAttrs : this->attrs; +} + +void EntryWithHeader::SetAttrs(int64 headerAttrs, int64 attrs) { + this->headerAttrs = headerAttrs; + this->attrs = attrs; +} + +size_t EntryWithHeader::GetLineCount() { + return 2; +} + +#define TRUNCATE(value, width) \ + u8substr(value, 0, width > 0 ? width : 0) + +std::string EntryWithHeader::GetLine(size_t line) { + if (line == 0) { + std::string ellipsized = this->header; + musik::box::text::Ellipsize(ellipsized, this->width); + return ellipsized; + } + + return TRUNCATE(this->value, this->width); +} diff --git a/src/musikbox/app/window/LogWindow.cpp b/src/musikbox/app/window/LogWindow.cpp index 93c40a68a..3b67da363 100755 --- a/src/musikbox/app/window/LogWindow.cpp +++ b/src/musikbox/app/window/LogWindow.cpp @@ -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 "LogWindow.h" - -#include -#include -#include - -using namespace musik::box; -using namespace cursespp; - -typedef IScrollAdapter::IEntry IEntry; - -LogWindow::LogWindow(IWindow *parent) -: ScrollableWindow(parent) { - this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); - - this->adapter = new SimpleScrollAdapter(); - this->adapter->SetMaxEntries(500); - - musik::debug::string_logged.connect(this, &LogWindow::OnLogged); - musik::debug::warn("LogWindow", "initialized"); -} - -LogWindow::~LogWindow() { -} - -IScrollAdapter& LogWindow::GetScrollAdapter() { - return (IScrollAdapter&) *this->adapter; -} - -void LogWindow::ClearContents() { - this->adapter->Clear(); - this->OnAdapterChanged(); -} - -void LogWindow::Update() { - boost::mutex::scoped_lock lock(pendingMutex); - - if (!pending.size()) { - return; - } - - WINDOW* contents = this->GetContent(); - - for (size_t i = 0; i < pending.size(); i++) { - int64 attrs = COLOR_PAIR(BOX_COLOR_WHITE_ON_BLACK); - - LogEntry* entry = pending[i]; - - switch (entry->level) { - case musik::debug::level_error: { - attrs = COLOR_PAIR(BOX_COLOR_RED_ON_BLACK) | A_BOLD; - break; - } - - case musik::debug::level_warning: { - attrs = COLOR_PAIR(BOX_COLOR_YELLOW_ON_BLACK) | A_BOLD; - break; - } - } - - std::string s = boost::str(boost::format( - "[%1%] %2%") % entry->tag % entry->message); - - this->adapter->AddEntry(std::shared_ptr(new MultiLineEntry(s, attrs))); - - delete entry; - } - - this->OnAdapterChanged(); - pending.clear(); -} - -void LogWindow::OnLogged( /* from a background thread */ - musik::debug::log_level level, - std::string tag, - std::string message) -{ - boost::mutex::scoped_lock lock(pendingMutex); - - LogEntry *entry = new LogEntry(); - entry->level = level; - entry->tag = tag; - entry->message = message; - pending.push_back(entry); -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "LogWindow.h" + +#include +#include +#include + +using namespace musik::box; +using namespace cursespp; + +typedef IScrollAdapter::IEntry IEntry; + +LogWindow::LogWindow(IWindow *parent) +: ScrollableWindow(parent) { + this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); + + this->adapter = new SimpleScrollAdapter(); + this->adapter->SetMaxEntries(500); + + musik::debug::string_logged.connect(this, &LogWindow::OnLogged); + musik::debug::warn("LogWindow", "initialized"); +} + +LogWindow::~LogWindow() { +} + +IScrollAdapter& LogWindow::GetScrollAdapter() { + return (IScrollAdapter&) *this->adapter; +} + +void LogWindow::ClearContents() { + this->adapter->Clear(); + this->OnAdapterChanged(); +} + +void LogWindow::Update() { + boost::mutex::scoped_lock lock(pendingMutex); + + if (!pending.size()) { + return; + } + + WINDOW* contents = this->GetContent(); + + for (size_t i = 0; i < pending.size(); i++) { + int64 attrs = COLOR_PAIR(BOX_COLOR_WHITE_ON_BLACK); + + LogEntry* entry = pending[i]; + + switch (entry->level) { + case musik::debug::level_error: { + attrs = COLOR_PAIR(BOX_COLOR_RED_ON_BLACK) | A_BOLD; + break; + } + + case musik::debug::level_warning: { + attrs = COLOR_PAIR(BOX_COLOR_YELLOW_ON_BLACK) | A_BOLD; + break; + } + } + + std::string s = boost::str(boost::format( + "[%1%] %2%") % entry->tag % entry->message); + + this->adapter->AddEntry(std::shared_ptr(new MultiLineEntry(s, attrs))); + + delete entry; + } + + this->OnAdapterChanged(); + pending.clear(); +} + +void LogWindow::OnLogged( /* from a background thread */ + musik::debug::log_level level, + std::string tag, + std::string message) +{ + boost::mutex::scoped_lock lock(pendingMutex); + + LogEntry *entry = new LogEntry(); + entry->level = level; + entry->tag = tag; + entry->message = message; + pending.push_back(entry); +} diff --git a/src/musikbox/app/window/OutputWindow.cpp b/src/musikbox/app/window/OutputWindow.cpp index 074fcf808..8281613ea 100755 --- a/src/musikbox/app/window/OutputWindow.cpp +++ b/src/musikbox/app/window/OutputWindow.cpp @@ -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 "OutputWindow.h" - -#include -#include -#include - -using namespace musik::box; -using namespace cursespp; - -typedef IScrollAdapter::EntryPtr EntryPtr; - -OutputWindow::OutputWindow(IWindow *parent) -: ScrollableWindow(parent) { - this->SetContentColor(BOX_COLOR_BLACK_ON_GREY); - this->adapter = new SimpleScrollAdapter(); - this->adapter->SetDisplaySize(this->GetContentWidth(), this->GetContentHeight()); - this->adapter->SetMaxEntries(500); -} - -OutputWindow::~OutputWindow() { - -} - -IScrollAdapter& OutputWindow::GetScrollAdapter() { - return (IScrollAdapter&) *this->adapter; -} - -void OutputWindow::WriteLine(const std::string& text, int64 attrs) { - this->adapter->AddEntry(EntryPtr(new MultiLineEntry(text, attrs))); - this->OnAdapterChanged(); -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "OutputWindow.h" + +#include +#include +#include + +using namespace musik::box; +using namespace cursespp; + +typedef IScrollAdapter::EntryPtr EntryPtr; + +OutputWindow::OutputWindow(IWindow *parent) +: ScrollableWindow(parent) { + this->SetContentColor(BOX_COLOR_BLACK_ON_GREY); + this->adapter = new SimpleScrollAdapter(); + this->adapter->SetDisplaySize(this->GetContentWidth(), this->GetContentHeight()); + this->adapter->SetMaxEntries(500); +} + +OutputWindow::~OutputWindow() { + +} + +IScrollAdapter& OutputWindow::GetScrollAdapter() { + return (IScrollAdapter&) *this->adapter; +} + +void OutputWindow::WriteLine(const std::string& text, int64 attrs) { + this->adapter->AddEntry(EntryPtr(new MultiLineEntry(text, attrs))); + this->OnAdapterChanged(); +} diff --git a/src/musikbox/app/window/ResourcesWindow.cpp b/src/musikbox/app/window/ResourcesWindow.cpp index af1a094bd..cfe99d9a7 100755 --- a/src/musikbox/app/window/ResourcesWindow.cpp +++ b/src/musikbox/app/window/ResourcesWindow.cpp @@ -30,46 +30,46 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" - -#include "ResourcesWindow.h" - -#include -#include - -#include - -#include - -using namespace musik::box; -using namespace cursespp; - -#define BYTES_PER_MEGABYTE 1048576.0f - -ResourcesWindow::ResourcesWindow(IWindow *parent) -: Window(parent) { - this->systemInfo = SystemInfo::Create(); -} - -ResourcesWindow::~ResourcesWindow() { - delete this->systemInfo; -} - -void ResourcesWindow::Update() { - this->Clear(); - - double virtualMemoryUsed = (double) systemInfo->GetUsedVirtualMemory() / BYTES_PER_MEGABYTE; - double physicalMemoryUsed = (double) systemInfo->GetUsedPhysicalMemory() / BYTES_PER_MEGABYTE; - double cpuUsage = (double) systemInfo->GetCpuUsage(); - - wprintw( - this->GetContent(), - "cpu %.2f%% - virt %.2f (mb) - phys %.2f (mb)", - cpuUsage, - virtualMemoryUsed, - physicalMemoryUsed); - - this->Repaint(); -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "ResourcesWindow.h" + +#include +#include + +#include + +#include + +using namespace musik::box; +using namespace cursespp; + +#define BYTES_PER_MEGABYTE 1048576.0f + +ResourcesWindow::ResourcesWindow(IWindow *parent) +: Window(parent) { + this->systemInfo = SystemInfo::Create(); +} + +ResourcesWindow::~ResourcesWindow() { + delete this->systemInfo; +} + +void ResourcesWindow::Update() { + this->Clear(); + + double virtualMemoryUsed = (double) systemInfo->GetUsedVirtualMemory() / BYTES_PER_MEGABYTE; + double physicalMemoryUsed = (double) systemInfo->GetUsedPhysicalMemory() / BYTES_PER_MEGABYTE; + double cpuUsage = (double) systemInfo->GetCpuUsage(); + + wprintw( + this->GetContent(), + "cpu %.2f%% - virt %.2f (mb) - phys %.2f (mb)", + cpuUsage, + virtualMemoryUsed, + physicalMemoryUsed); + + this->Repaint(); +} diff --git a/src/musikbox/app/window/TrackListView.cpp b/src/musikbox/app/window/TrackListView.cpp index 9bb871230..115ce9928 100755 --- a/src/musikbox/app/window/TrackListView.cpp +++ b/src/musikbox/app/window/TrackListView.cpp @@ -30,190 +30,190 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" - -#include -#include -#include - -#include "TrackListView.h" - -#include - -#include -#include - -#include -#include -#include - -#include - -#define WINDOW_MESSAGE_QUERY_COMPLETED 1002 - -using namespace musik::core; -using namespace musik::core::audio; -using namespace musik::core::library; -using namespace musik::box; -using namespace cursespp; -using boost::io::group; -using std::setw; -using std::setfill; -using std::setiosflags; - -TrackListView::TrackListView(PlaybackService& playback, LibraryPtr library) -: ListWindow(NULL) -, playback(playback) { - this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); - this->library = library; - this->library->QueryCompleted.connect(this, &TrackListView::OnQueryCompleted); - this->playback.TrackChanged.connect(this, &TrackListView::OnTrackChanged); - this->adapter = new Adapter(*this); - this->lastQueryHash = 0; -} - -TrackListView::~TrackListView() { - -} - -void TrackListView::Requery(std::shared_ptr query) { - this->query = query; - this->library->Enqueue(this->query); -} - -void TrackListView::OnQueryCompleted(QueryPtr query) { - if (query == this->query) { - this->PostMessage(WINDOW_MESSAGE_QUERY_COMPLETED); - } -} - -TrackListView::TrackList TrackListView::GetTrackList() { - return this->metadata; -} - -void TrackListView::Clear() { - this->query.reset(); - this->metadata.reset(new std::vector()); - this->headers.reset(new std::set()); - this->OnAdapterChanged(); -} - -void TrackListView::ProcessMessage(IMessage &message) { - if (message.Type() == WINDOW_MESSAGE_QUERY_COMPLETED) { - if (this->query && this->query->GetStatus() == IQuery::Finished) { - this->metadata = this->query->GetResult(); - this->headers = this->query->GetHeaders(); - - /* if the query was functionally the same as the last query, don't - mess with the selected index */ - if (this->lastQueryHash != query->GetQueryHash()) { - this->SetSelectedIndex(0); - } - - this->lastQueryHash = this->query->GetQueryHash(); - this->query.reset(); - - this->OnAdapterChanged(); /* internal handling */ - this->Requeried(); /* for external handlers */ - } - } -} - -void TrackListView::OnTrackChanged(size_t index, musik::core::TrackPtr track) { - this->playing = track; - this->OnAdapterChanged(); -} - -IScrollAdapter& TrackListView::GetScrollAdapter() { - return *this->adapter; -} - -TrackListView::Adapter::Adapter(TrackListView &parent) -: parent(parent) { -} - -size_t TrackListView::Adapter::GetEntryCount() { - return parent.metadata ? parent.metadata->size() : 0; -} - -#define TRACK_COL_WIDTH 3 -#define ARTIST_COL_WIDTH 14 -#define ALBUM_COL_WIDTH 14 -#define DURATION_COL_WIDTH 5 /* 00:00 */ - -/* so this part is a bit tricky... we draw multiple columns, but we use -standard std::setw() stuff, which is not aware of multi-byte characters. -so we have to manually adjust the widths (i.e. we can't just use simple -constants) */ -#define DISPLAY_WIDTH(chars, str) \ - chars + (str.size() - u8len(str)) - -IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(size_t index) { - bool selected = index == parent.GetSelectedIndex(); - int64 attrs = selected - ? COLOR_PAIR(BOX_COLOR_BLACK_ON_GREEN) - : -1LL; - - TrackPtr track = parent.metadata->at(index); - - TrackPtr playing = parent.playing; - if (playing && - playing->Id() == track->Id() && - playing->LibraryId() == track->LibraryId()) - { - if (selected) { - attrs = COLOR_PAIR(BOX_COLOR_BLACK_ON_YELLOW); - } - else { - attrs = COLOR_PAIR(BOX_COLOR_YELLOW_ON_BLACK) | A_BOLD; - } - } - - std::string trackNum = track->GetValue(constants::Track::TRACK_NUM); - std::string artist = track->GetValue(constants::Track::ARTIST); - std::string album = track->GetValue(constants::Track::ALBUM); - std::string title = track->GetValue(constants::Track::TITLE); - std::string duration = track->GetValue(constants::Track::DURATION); - - int column0Width = DISPLAY_WIDTH(TRACK_COL_WIDTH, trackNum); - int column2Width = DISPLAY_WIDTH(DURATION_COL_WIDTH, duration); - int column3Width = DISPLAY_WIDTH(ARTIST_COL_WIDTH, artist); - int column4Width = DISPLAY_WIDTH(ALBUM_COL_WIDTH, album); - - size_t column1CharacterCount = - this->GetWidth() - - column0Width - - column2Width - - column3Width - - column4Width - - (3 * 4); /* 3 = spacing */ - - int column1Width = DISPLAY_WIDTH(column1CharacterCount, title); - - text::Ellipsize(artist, ARTIST_COL_WIDTH); - text::Ellipsize(album, ALBUM_COL_WIDTH); - text::Ellipsize(title, column1CharacterCount); - duration = text::Duration(duration); - - std::string text = boost::str( - boost::format("%s %s %s %s %s") - % group(setw(column0Width), setfill(' '), trackNum) - % group(setw(column1Width), setiosflags(std::ios::left), setfill(' '), title) - % group(setw(column2Width), setiosflags(std::ios::right), setfill(' '), duration) - % group(setw(column3Width), setiosflags(std::ios::left), setfill(' '), artist) - % group(setw(column4Width), setiosflags(std::ios::left), setfill(' '), album)); - - if (this->parent.headers->find(index) != this->parent.headers->end()) { - std::string album = track->GetValue(constants::Track::ALBUM); - std::shared_ptr entry(new EntryWithHeader(album, text)); - entry->SetAttrs(COLOR_PAIR(BOX_COLOR_GREEN_ON_BLACK), attrs); - return entry; - } - else { - std::shared_ptr entry(new SingleLineEntry(text)); - entry->SetAttrs(attrs); - return entry; - } -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include +#include +#include + +#include "TrackListView.h" + +#include + +#include +#include + +#include +#include +#include + +#include + +#define WINDOW_MESSAGE_QUERY_COMPLETED 1002 + +using namespace musik::core; +using namespace musik::core::audio; +using namespace musik::core::library; +using namespace musik::box; +using namespace cursespp; +using boost::io::group; +using std::setw; +using std::setfill; +using std::setiosflags; + +TrackListView::TrackListView(PlaybackService& playback, LibraryPtr library) +: ListWindow(NULL) +, playback(playback) { + this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); + this->library = library; + this->library->QueryCompleted.connect(this, &TrackListView::OnQueryCompleted); + this->playback.TrackChanged.connect(this, &TrackListView::OnTrackChanged); + this->adapter = new Adapter(*this); + this->lastQueryHash = 0; +} + +TrackListView::~TrackListView() { + +} + +void TrackListView::Requery(std::shared_ptr query) { + this->query = query; + this->library->Enqueue(this->query); +} + +void TrackListView::OnQueryCompleted(QueryPtr query) { + if (query == this->query) { + this->PostMessage(WINDOW_MESSAGE_QUERY_COMPLETED); + } +} + +TrackListView::TrackList TrackListView::GetTrackList() { + return this->metadata; +} + +void TrackListView::Clear() { + this->query.reset(); + this->metadata.reset(new std::vector()); + this->headers.reset(new std::set()); + this->OnAdapterChanged(); +} + +void TrackListView::ProcessMessage(IMessage &message) { + if (message.Type() == WINDOW_MESSAGE_QUERY_COMPLETED) { + if (this->query && this->query->GetStatus() == IQuery::Finished) { + this->metadata = this->query->GetResult(); + this->headers = this->query->GetHeaders(); + + /* if the query was functionally the same as the last query, don't + mess with the selected index */ + if (this->lastQueryHash != query->GetQueryHash()) { + this->SetSelectedIndex(0); + } + + this->lastQueryHash = this->query->GetQueryHash(); + this->query.reset(); + + this->OnAdapterChanged(); /* internal handling */ + this->Requeried(); /* for external handlers */ + } + } +} + +void TrackListView::OnTrackChanged(size_t index, musik::core::TrackPtr track) { + this->playing = track; + this->OnAdapterChanged(); +} + +IScrollAdapter& TrackListView::GetScrollAdapter() { + return *this->adapter; +} + +TrackListView::Adapter::Adapter(TrackListView &parent) +: parent(parent) { +} + +size_t TrackListView::Adapter::GetEntryCount() { + return parent.metadata ? parent.metadata->size() : 0; +} + +#define TRACK_COL_WIDTH 3 +#define ARTIST_COL_WIDTH 14 +#define ALBUM_COL_WIDTH 14 +#define DURATION_COL_WIDTH 5 /* 00:00 */ + +/* so this part is a bit tricky... we draw multiple columns, but we use +standard std::setw() stuff, which is not aware of multi-byte characters. +so we have to manually adjust the widths (i.e. we can't just use simple +constants) */ +#define DISPLAY_WIDTH(chars, str) \ + chars + (str.size() - u8len(str)) + +IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(size_t index) { + bool selected = index == parent.GetSelectedIndex(); + int64 attrs = selected + ? COLOR_PAIR(BOX_COLOR_BLACK_ON_GREEN) + : -1LL; + + TrackPtr track = parent.metadata->at(index); + + TrackPtr playing = parent.playing; + if (playing && + playing->Id() == track->Id() && + playing->LibraryId() == track->LibraryId()) + { + if (selected) { + attrs = COLOR_PAIR(BOX_COLOR_BLACK_ON_YELLOW); + } + else { + attrs = COLOR_PAIR(BOX_COLOR_YELLOW_ON_BLACK) | A_BOLD; + } + } + + std::string trackNum = track->GetValue(constants::Track::TRACK_NUM); + std::string artist = track->GetValue(constants::Track::ARTIST); + std::string album = track->GetValue(constants::Track::ALBUM); + std::string title = track->GetValue(constants::Track::TITLE); + std::string duration = track->GetValue(constants::Track::DURATION); + + int column0Width = DISPLAY_WIDTH(TRACK_COL_WIDTH, trackNum); + int column2Width = DISPLAY_WIDTH(DURATION_COL_WIDTH, duration); + int column3Width = DISPLAY_WIDTH(ARTIST_COL_WIDTH, artist); + int column4Width = DISPLAY_WIDTH(ALBUM_COL_WIDTH, album); + + size_t column1CharacterCount = + this->GetWidth() - + column0Width - + column2Width - + column3Width - + column4Width - + (3 * 4); /* 3 = spacing */ + + int column1Width = DISPLAY_WIDTH(column1CharacterCount, title); + + text::Ellipsize(artist, ARTIST_COL_WIDTH); + text::Ellipsize(album, ALBUM_COL_WIDTH); + text::Ellipsize(title, column1CharacterCount); + duration = text::Duration(duration); + + std::string text = boost::str( + boost::format("%s %s %s %s %s") + % group(setw(column0Width), setfill(' '), trackNum) + % group(setw(column1Width), setiosflags(std::ios::left), setfill(' '), title) + % group(setw(column2Width), setiosflags(std::ios::right), setfill(' '), duration) + % group(setw(column3Width), setiosflags(std::ios::left), setfill(' '), artist) + % group(setw(column4Width), setiosflags(std::ios::left), setfill(' '), album)); + + if (this->parent.headers->find(index) != this->parent.headers->end()) { + std::string album = track->GetValue(constants::Track::ALBUM); + std::shared_ptr entry(new EntryWithHeader(album, text)); + entry->SetAttrs(COLOR_PAIR(BOX_COLOR_GREEN_ON_BLACK), attrs); + return entry; + } + else { + std::shared_ptr entry(new SingleLineEntry(text)); + entry->SetAttrs(attrs); + return entry; + } +} diff --git a/src/musikbox/app/window/TransportWindow.cpp b/src/musikbox/app/window/TransportWindow.cpp index 9723410bb..334431bbe 100755 --- a/src/musikbox/app/window/TransportWindow.cpp +++ b/src/musikbox/app/window/TransportWindow.cpp @@ -30,209 +30,209 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" -#include "TransportWindow.h" - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#include - -using namespace musik::core; -using namespace musik::core::audio; -using namespace musik::core::library; -using namespace musik::core::db; -using namespace musik::box; -using namespace boost::chrono; -using namespace cursespp; - -#define REFRESH_TRANSPORT_READOUT 1001 -#define REFRESH_INTERVAL_MS 1000 - -#define DEBOUNCE_REFRESH(x) \ - this->RemoveMessage(REFRESH_TRANSPORT_READOUT); \ - this->PostMessage(REFRESH_TRANSPORT_READOUT, 0, 0, x); - -TransportWindow::TransportWindow(musik::box::PlaybackService& playback) -: Window(NULL) -, playback(playback) -, transport(playback.GetTransport()) -{ - this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); - this->SetFrameVisible(false); - this->playback.TrackChanged.connect(this, &TransportWindow::OnPlaybackServiceTrackChanged); - this->transport.VolumeChanged.connect(this, &TransportWindow::OnTransportVolumeChanged); - this->transport.TimeChanged.connect(this, &TransportWindow::OnTransportTimeChanged); - this->paused = this->focused = false; -} - -TransportWindow::~TransportWindow() { -} - -void TransportWindow::Show() { - Window::Show(); - this->Update(); -} - -void TransportWindow::ProcessMessage(IMessage &message) { - int type = message.Type(); - - if (type == REFRESH_TRANSPORT_READOUT) { - this->Update(); - DEBOUNCE_REFRESH(REFRESH_INTERVAL_MS) - } -} - -void TransportWindow::OnPlaybackServiceTrackChanged(size_t index, TrackPtr track) { - this->currentTrack = track; - DEBOUNCE_REFRESH(0) -} - -void TransportWindow::OnTransportVolumeChanged() { - DEBOUNCE_REFRESH(0) -} - -void TransportWindow::OnTransportTimeChanged(double time) { - DEBOUNCE_REFRESH(0) -} - -void TransportWindow::Focus() { - this->focused = true; - DEBOUNCE_REFRESH(0) -} - -void TransportWindow::Blur() { - this->focused = false; - DEBOUNCE_REFRESH(0) -} - -void TransportWindow::Update() { - this->Clear(); - WINDOW *c = this->GetContent(); - - bool paused = (transport.GetPlaybackState() == Transport::PlaybackPaused); - bool stopped = (transport.GetPlaybackState() == Transport::PlaybackStopped); - - int64 gb = COLOR_PAIR(BOX_COLOR_GREEN_ON_BLACK); - - if (focused) { - gb = COLOR_PAIR(BOX_COLOR_RED_ON_BLACK); - } - - /* playing SONG TITLE from ALBUM NAME */ - std::string duration = "0"; - - if (stopped) { - wattron(c, gb); - wprintw(c, "playback is stopped"); - wattroff(c, gb); - } - else { - std::string title, album; - - if (this->currentTrack) { - title = this->currentTrack->GetValue(constants::Track::TITLE); - album = this->currentTrack->GetValue(constants::Track::ALBUM); - duration = this->currentTrack->GetValue(constants::Track::DURATION); - } - - title = title.size() ? title : "song title"; - album = album.size() ? album : "album name"; - duration = duration.size() ? duration : "0"; - - wprintw(c, "playing "); - - wattron(c, gb); - wprintw(c, title.c_str()); - wattroff(c, gb); - - wprintw(c, " from "); - - wattron(c, gb); - wprintw(c, album.c_str()); - wattroff(c, gb); - } - - /* volume slider */ - - wprintw(c, "\n"); - - int volumePercent = (size_t) round(this->transport.Volume() * 100.0f) - 1; - int thumbOffset = std::min(9, (volumePercent * 10) / 100); - - std::string volume = "vol "; - - for (int i = 0; i < 10; i++) { - volume += (i == thumbOffset) ? "■" : "─"; - } - - volume += " "; - - wprintw(c, volume.c_str()); - - /* time slider */ - - int64 timerAttrs = 0; - - if (paused) { /* blink the track if paused */ - int64 now = duration_cast( - system_clock::now().time_since_epoch()).count(); - - if (now % 2 == 0) { - timerAttrs = COLOR_PAIR(BOX_COLOR_BLACK_ON_BLACK); - } - } - - transport.Position(); - - int secondsCurrent = (int) round(transport.Position()); - int secondsTotal = boost::lexical_cast(duration); - - std::string currentTime = text::Duration(std::min(secondsCurrent, secondsTotal)); - std::string totalTime = text::Duration(secondsTotal); - - size_t timerWidth = - this->GetContentWidth() - - u8len(volume) - - currentTime.size() - - totalTime.size() - - 2; /* padding */ - - thumbOffset = 0; - - if (secondsTotal) { - size_t progress = (secondsCurrent * 100) / secondsTotal; - thumbOffset = std::min(timerWidth - 1, (progress * timerWidth) / 100); - } - - std::string timerTrack = ""; - for (size_t i = 0; i < timerWidth; i++) { - timerTrack += (i == thumbOffset) ? "■" : "─"; - } - - wattron(c, timerAttrs); /* blink if paused */ - wprintw(c, currentTime.c_str()); - wattroff(c, timerAttrs); - - /* using wprintw() here on large displays (1440p+) will exceed the internal - buffer length of 512 characters, so use boost format. */ - std::string fmt = boost::str(boost::format( - " %s %s") % timerTrack % totalTime); - - waddstr(c, fmt.c_str()); - - this->Repaint(); -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "TransportWindow.h" + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +using namespace musik::core; +using namespace musik::core::audio; +using namespace musik::core::library; +using namespace musik::core::db; +using namespace musik::box; +using namespace boost::chrono; +using namespace cursespp; + +#define REFRESH_TRANSPORT_READOUT 1001 +#define REFRESH_INTERVAL_MS 1000 + +#define DEBOUNCE_REFRESH(x) \ + this->RemoveMessage(REFRESH_TRANSPORT_READOUT); \ + this->PostMessage(REFRESH_TRANSPORT_READOUT, 0, 0, x); + +TransportWindow::TransportWindow(musik::box::PlaybackService& playback) +: Window(NULL) +, playback(playback) +, transport(playback.GetTransport()) +{ + this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK); + this->SetFrameVisible(false); + this->playback.TrackChanged.connect(this, &TransportWindow::OnPlaybackServiceTrackChanged); + this->transport.VolumeChanged.connect(this, &TransportWindow::OnTransportVolumeChanged); + this->transport.TimeChanged.connect(this, &TransportWindow::OnTransportTimeChanged); + this->paused = this->focused = false; +} + +TransportWindow::~TransportWindow() { +} + +void TransportWindow::Show() { + Window::Show(); + this->Update(); +} + +void TransportWindow::ProcessMessage(IMessage &message) { + int type = message.Type(); + + if (type == REFRESH_TRANSPORT_READOUT) { + this->Update(); + DEBOUNCE_REFRESH(REFRESH_INTERVAL_MS) + } +} + +void TransportWindow::OnPlaybackServiceTrackChanged(size_t index, TrackPtr track) { + this->currentTrack = track; + DEBOUNCE_REFRESH(0) +} + +void TransportWindow::OnTransportVolumeChanged() { + DEBOUNCE_REFRESH(0) +} + +void TransportWindow::OnTransportTimeChanged(double time) { + DEBOUNCE_REFRESH(0) +} + +void TransportWindow::Focus() { + this->focused = true; + DEBOUNCE_REFRESH(0) +} + +void TransportWindow::Blur() { + this->focused = false; + DEBOUNCE_REFRESH(0) +} + +void TransportWindow::Update() { + this->Clear(); + WINDOW *c = this->GetContent(); + + bool paused = (transport.GetPlaybackState() == Transport::PlaybackPaused); + bool stopped = (transport.GetPlaybackState() == Transport::PlaybackStopped); + + int64 gb = COLOR_PAIR(BOX_COLOR_GREEN_ON_BLACK); + + if (focused) { + gb = COLOR_PAIR(BOX_COLOR_RED_ON_BLACK); + } + + /* playing SONG TITLE from ALBUM NAME */ + std::string duration = "0"; + + if (stopped) { + wattron(c, gb); + wprintw(c, "playback is stopped"); + wattroff(c, gb); + } + else { + std::string title, album; + + if (this->currentTrack) { + title = this->currentTrack->GetValue(constants::Track::TITLE); + album = this->currentTrack->GetValue(constants::Track::ALBUM); + duration = this->currentTrack->GetValue(constants::Track::DURATION); + } + + title = title.size() ? title : "song title"; + album = album.size() ? album : "album name"; + duration = duration.size() ? duration : "0"; + + wprintw(c, "playing "); + + wattron(c, gb); + wprintw(c, title.c_str()); + wattroff(c, gb); + + wprintw(c, " from "); + + wattron(c, gb); + wprintw(c, album.c_str()); + wattroff(c, gb); + } + + /* volume slider */ + + wprintw(c, "\n"); + + int volumePercent = (size_t) round(this->transport.Volume() * 100.0f) - 1; + int thumbOffset = std::min(9, (volumePercent * 10) / 100); + + std::string volume = "vol "; + + for (int i = 0; i < 10; i++) { + volume += (i == thumbOffset) ? "■" : "─"; + } + + volume += " "; + + wprintw(c, volume.c_str()); + + /* time slider */ + + int64 timerAttrs = 0; + + if (paused) { /* blink the track if paused */ + int64 now = duration_cast( + system_clock::now().time_since_epoch()).count(); + + if (now % 2 == 0) { + timerAttrs = COLOR_PAIR(BOX_COLOR_BLACK_ON_BLACK); + } + } + + transport.Position(); + + int secondsCurrent = (int) round(transport.Position()); + int secondsTotal = boost::lexical_cast(duration); + + std::string currentTime = text::Duration(std::min(secondsCurrent, secondsTotal)); + std::string totalTime = text::Duration(secondsTotal); + + size_t timerWidth = + this->GetContentWidth() - + u8len(volume) - + currentTime.size() - + totalTime.size() - + 2; /* padding */ + + thumbOffset = 0; + + if (secondsTotal) { + size_t progress = (secondsCurrent * 100) / secondsTotal; + thumbOffset = std::min(timerWidth - 1, (progress * timerWidth) / 100); + } + + std::string timerTrack = ""; + for (size_t i = 0; i < timerWidth; i++) { + timerTrack += (i == thumbOffset) ? "■" : "─"; + } + + wattron(c, timerAttrs); /* blink if paused */ + wprintw(c, currentTime.c_str()); + wattroff(c, timerAttrs); + + /* using wprintw() here on large displays (1440p+) will exceed the internal + buffer length of 512 characters, so use boost format. */ + std::string fmt = boost::str(boost::format( + " %s %s") % timerTrack % totalTime); + + waddstr(c, fmt.c_str()); + + this->Repaint(); +} diff --git a/src/musikbox/cursespp/Colors.cpp b/src/musikbox/cursespp/Colors.cpp index 3584c0ed4..34377a17b 100755 --- a/src/musikbox/cursespp/Colors.cpp +++ b/src/musikbox/cursespp/Colors.cpp @@ -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 -#include "Colors.h" - -using namespace cursespp; - -Colors::Colors() { -} - -void Colors::Init() { - init_pair(BOX_COLOR_WHITE_ON_BLUE, COLOR_WHITE, COLOR_BLUE); - init_pair(BOX_COLOR_RED_ON_BLUE, COLOR_RED, COLOR_BLUE); - init_pair(BOX_COLOR_YELLOW_ON_BLUE, COLOR_YELLOW, COLOR_BLUE); - init_pair(BOX_COLOR_BLACK_ON_GREY, COLOR_BLACK, COLOR_WHITE); - init_pair(BOX_COLOR_BLACK_ON_GREEN, COLOR_BLACK, COLOR_GREEN); - init_pair(BOX_COLOR_YELLOW_ON_BLACK, COLOR_YELLOW, -1); - init_pair(BOX_COLOR_WHITE_ON_BLACK, COLOR_WHITE, -1); - init_pair(BOX_COLOR_RED_ON_BLACK, COLOR_RED, -1); - init_pair(BOX_COLOR_RED_ON_GREY, COLOR_RED, COLOR_WHITE); - init_pair(BOX_COLOR_GREEN_ON_BLACK, COLOR_GREEN, -1); - init_pair(BOX_COLOR_BLACK_ON_BLACK, COLOR_BLACK, -1); - init_pair(BOX_COLOR_RED_ON_GREEN, COLOR_RED, COLOR_GREEN); - init_pair(BOX_COLOR_BLACK_ON_YELLOW, COLOR_BLACK, COLOR_YELLOW); -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "Colors.h" + +using namespace cursespp; + +Colors::Colors() { +} + +void Colors::Init() { + init_pair(BOX_COLOR_WHITE_ON_BLUE, COLOR_WHITE, COLOR_BLUE); + init_pair(BOX_COLOR_RED_ON_BLUE, COLOR_RED, COLOR_BLUE); + init_pair(BOX_COLOR_YELLOW_ON_BLUE, COLOR_YELLOW, COLOR_BLUE); + init_pair(BOX_COLOR_BLACK_ON_GREY, COLOR_BLACK, COLOR_WHITE); + init_pair(BOX_COLOR_BLACK_ON_GREEN, COLOR_BLACK, COLOR_GREEN); + init_pair(BOX_COLOR_YELLOW_ON_BLACK, COLOR_YELLOW, -1); + init_pair(BOX_COLOR_WHITE_ON_BLACK, COLOR_WHITE, -1); + init_pair(BOX_COLOR_RED_ON_BLACK, COLOR_RED, -1); + init_pair(BOX_COLOR_RED_ON_GREY, COLOR_RED, COLOR_WHITE); + init_pair(BOX_COLOR_GREEN_ON_BLACK, COLOR_GREEN, -1); + init_pair(BOX_COLOR_BLACK_ON_BLACK, COLOR_BLACK, -1); + init_pair(BOX_COLOR_RED_ON_GREEN, COLOR_RED, COLOR_GREEN); + init_pair(BOX_COLOR_BLACK_ON_YELLOW, COLOR_BLACK, COLOR_YELLOW); +} diff --git a/src/musikbox/cursespp/LayoutBase.cpp b/src/musikbox/cursespp/LayoutBase.cpp index cd0ec3c3b..aefa9eb38 100755 --- a/src/musikbox/cursespp/LayoutBase.cpp +++ b/src/musikbox/cursespp/LayoutBase.cpp @@ -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 -#include "LayoutBase.h" -#include "LayoutStack.h" -#include "Colors.h" - -using namespace cursespp; - -template static int find(std::vector& haystack, T& needle) { - int i = 0; - typename std::vector::iterator it = haystack.begin(); - for (; it != haystack.end(); it++, i++) { - if ((*it) == needle) { - return i; - } - } - - return -1; -} - -bool sortByFocusOrder(IWindowPtr a, IWindowPtr b) { - int orderA = a->GetFocusOrder(); - int orderB = b->GetFocusOrder(); - - if (orderA == orderB) { - return a->GetId() > b->GetId(); - } - - return orderA > orderB; -} - -static inline IWindowPtr adjustFocus(IWindowPtr oldFocus, IWindowPtr newFocus) { - if (oldFocus) { - oldFocus->SetFrameColor(BOX_COLOR_WHITE_ON_BLACK); - oldFocus->Blur(); - } - - if (newFocus) { - newFocus->SetFrameColor(BOX_COLOR_RED_ON_BLACK); - newFocus->Focus(); - } - - return newFocus; -} - -LayoutBase::LayoutBase(IWindow* parent) -: Window(parent) { - this->focused = -1; - this->layoutStack = 0; - this->SetFrameVisible(false); -} - -LayoutBase::~LayoutBase() { - -} - -void LayoutBase::Show() { - Window::Show(); - - for (size_t i = 0; i < this->children.size(); i++) { - this->children.at(i)->Show(); - } - - this->IndexFocusables(); - this->SortFocusables(); -} - -void LayoutBase::Hide() { - for (size_t i = 0; i < this->children.size(); i++) { - this->children.at(i)->Hide(); - } - - Window::Hide(); -} - -void LayoutBase::BringToTop() { - Window::BringToTop(); - - for (size_t i = 0; i < this->children.size(); i++) { - this->children.at(i)->BringToTop(); - } - - this->Repaint(); -} - -void LayoutBase::SendToBottom() { - for (size_t i = 0; i < this->children.size(); i++) { - this->children.at(i)->SendToBottom(); - } - - Window::SendToBottom(); -} - -void LayoutBase::Repaint() { - /* repaint bottom up. start with ourselves, then our children, - recursively. */ - - Window::Repaint(); - - for (size_t i = 0; i < this->children.size(); i++) { - this->children.at(i)->Repaint(); - } -} - -bool LayoutBase::AddWindow(IWindowPtr window) { - if (find(this->children, window) >= 0) { - return true; - } - - window->SetParent(this); - - this->children.push_back(window); - AddFocusable(window); - - return true; -} - -bool LayoutBase::RemoveWindow(IWindowPtr window) { - this->RemoveFocusable(window); - - std::vector::iterator it = this->children.begin(); - for ( ; it != this->children.end(); it++) { - if (*it == window) { - this->children.erase(it); - return true; - } - } - - return false; -} - -void LayoutBase::AddFocusable(IWindowPtr window) { - int order = window->GetFocusOrder(); - if (order >= 0 && find(this->focusable, window) < 0) { - this->focusable.push_back(window); - this->SortFocusables(); - } -} - -void LayoutBase::IndexFocusables() { - IWindowPtr focusedWindow; - if (focused >= 0 && (int) this->focusable.size() > focused) { - focusedWindow = this->focusable.at(focused); - } - - this->focusable.clear(); - for (size_t i = 0; i < this->children.size(); i++) { - AddFocusable(this->children.at(i)); - } - - if (focusedWindow) { - this->focused = find(this->focusable, focusedWindow); - } -} - -void LayoutBase::SortFocusables() { - IWindowPtr focusedWindow; - if (focused >= 0 && (int) this->focusable.size() > focused) { - focusedWindow = this->focusable.at(focused); - } - - std::sort( - this->focusable.begin(), - this->focusable.end(), - sortByFocusOrder); - - if (focusedWindow) { - this->focused = find(this->focusable, focusedWindow); - } - - if (focused == -1 && this->focusable.size() > 0) { - this->focused = 0; - adjustFocus(IWindowPtr(), this->focusable[this->focused]); - } -} - -void LayoutBase::RemoveFocusable(IWindowPtr window) { - std::vector::iterator it = this->focusable.begin(); - for (; it != this->focusable.end(); it++) { - if (*it == window) { - this->focusable.erase(it); - return; - } - } -} - -size_t LayoutBase::GetWindowCount() { - return this->children.size(); -} - -IWindowPtr LayoutBase::GetWindowAt(size_t position) { - return this->children.at(position); -} - -IWindowPtr LayoutBase::FocusNext() { - IWindowPtr oldFocus = GetFocus(); - if (++this->focused >= (int) this->focusable.size()) { - this->focused = 0; - } - - return adjustFocus(oldFocus, GetFocus()); -} - -IWindowPtr LayoutBase::FocusPrev() { - IWindowPtr oldFocus = GetFocus(); - if (--this->focused <= 0) { - this->focused = (int) this->focusable.size() - 1; - } - - return adjustFocus(oldFocus, GetFocus()); -} - -IWindowPtr LayoutBase::GetFocus() { - if (this->focused >= 0 && this->focusable.size() > 0) { - return this->focusable[this->focused]; - } - - return IWindowPtr(); -} - -bool LayoutBase::KeyPress(const std::string& key) { - return false; -} - -ILayoutStack* LayoutBase::GetLayoutStack() { - return this->layoutStack; -} - -void LayoutBase::SetLayoutStack(ILayoutStack* stack) { - this->layoutStack = stack; -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "LayoutBase.h" +#include "LayoutStack.h" +#include "Colors.h" + +using namespace cursespp; + +template static int find(std::vector& haystack, T& needle) { + int i = 0; + typename std::vector::iterator it = haystack.begin(); + for (; it != haystack.end(); it++, i++) { + if ((*it) == needle) { + return i; + } + } + + return -1; +} + +bool sortByFocusOrder(IWindowPtr a, IWindowPtr b) { + int orderA = a->GetFocusOrder(); + int orderB = b->GetFocusOrder(); + + if (orderA == orderB) { + return a->GetId() > b->GetId(); + } + + return orderA > orderB; +} + +static inline IWindowPtr adjustFocus(IWindowPtr oldFocus, IWindowPtr newFocus) { + if (oldFocus) { + oldFocus->SetFrameColor(BOX_COLOR_WHITE_ON_BLACK); + oldFocus->Blur(); + } + + if (newFocus) { + newFocus->SetFrameColor(BOX_COLOR_RED_ON_BLACK); + newFocus->Focus(); + } + + return newFocus; +} + +LayoutBase::LayoutBase(IWindow* parent) +: Window(parent) { + this->focused = -1; + this->layoutStack = 0; + this->SetFrameVisible(false); +} + +LayoutBase::~LayoutBase() { + +} + +void LayoutBase::Show() { + Window::Show(); + + for (size_t i = 0; i < this->children.size(); i++) { + this->children.at(i)->Show(); + } + + this->IndexFocusables(); + this->SortFocusables(); +} + +void LayoutBase::Hide() { + for (size_t i = 0; i < this->children.size(); i++) { + this->children.at(i)->Hide(); + } + + Window::Hide(); +} + +void LayoutBase::BringToTop() { + Window::BringToTop(); + + for (size_t i = 0; i < this->children.size(); i++) { + this->children.at(i)->BringToTop(); + } + + this->Repaint(); +} + +void LayoutBase::SendToBottom() { + for (size_t i = 0; i < this->children.size(); i++) { + this->children.at(i)->SendToBottom(); + } + + Window::SendToBottom(); +} + +void LayoutBase::Repaint() { + /* repaint bottom up. start with ourselves, then our children, + recursively. */ + + Window::Repaint(); + + for (size_t i = 0; i < this->children.size(); i++) { + this->children.at(i)->Repaint(); + } +} + +bool LayoutBase::AddWindow(IWindowPtr window) { + if (find(this->children, window) >= 0) { + return true; + } + + window->SetParent(this); + + this->children.push_back(window); + AddFocusable(window); + + return true; +} + +bool LayoutBase::RemoveWindow(IWindowPtr window) { + this->RemoveFocusable(window); + + std::vector::iterator it = this->children.begin(); + for ( ; it != this->children.end(); it++) { + if (*it == window) { + this->children.erase(it); + return true; + } + } + + return false; +} + +void LayoutBase::AddFocusable(IWindowPtr window) { + int order = window->GetFocusOrder(); + if (order >= 0 && find(this->focusable, window) < 0) { + this->focusable.push_back(window); + this->SortFocusables(); + } +} + +void LayoutBase::IndexFocusables() { + IWindowPtr focusedWindow; + if (focused >= 0 && (int) this->focusable.size() > focused) { + focusedWindow = this->focusable.at(focused); + } + + this->focusable.clear(); + for (size_t i = 0; i < this->children.size(); i++) { + AddFocusable(this->children.at(i)); + } + + if (focusedWindow) { + this->focused = find(this->focusable, focusedWindow); + } +} + +void LayoutBase::SortFocusables() { + IWindowPtr focusedWindow; + if (focused >= 0 && (int) this->focusable.size() > focused) { + focusedWindow = this->focusable.at(focused); + } + + std::sort( + this->focusable.begin(), + this->focusable.end(), + sortByFocusOrder); + + if (focusedWindow) { + this->focused = find(this->focusable, focusedWindow); + } + + if (focused == -1 && this->focusable.size() > 0) { + this->focused = 0; + adjustFocus(IWindowPtr(), this->focusable[this->focused]); + } +} + +void LayoutBase::RemoveFocusable(IWindowPtr window) { + std::vector::iterator it = this->focusable.begin(); + for (; it != this->focusable.end(); it++) { + if (*it == window) { + this->focusable.erase(it); + return; + } + } +} + +size_t LayoutBase::GetWindowCount() { + return this->children.size(); +} + +IWindowPtr LayoutBase::GetWindowAt(size_t position) { + return this->children.at(position); +} + +IWindowPtr LayoutBase::FocusNext() { + IWindowPtr oldFocus = GetFocus(); + if (++this->focused >= (int) this->focusable.size()) { + this->focused = 0; + } + + return adjustFocus(oldFocus, GetFocus()); +} + +IWindowPtr LayoutBase::FocusPrev() { + IWindowPtr oldFocus = GetFocus(); + if (--this->focused <= 0) { + this->focused = (int) this->focusable.size() - 1; + } + + return adjustFocus(oldFocus, GetFocus()); +} + +IWindowPtr LayoutBase::GetFocus() { + if (this->focused >= 0 && this->focusable.size() > 0) { + return this->focusable[this->focused]; + } + + return IWindowPtr(); +} + +bool LayoutBase::KeyPress(const std::string& key) { + return false; +} + +ILayoutStack* LayoutBase::GetLayoutStack() { + return this->layoutStack; +} + +void LayoutBase::SetLayoutStack(ILayoutStack* stack) { + this->layoutStack = stack; +} diff --git a/src/musikbox/cursespp/LayoutStack.cpp b/src/musikbox/cursespp/LayoutStack.cpp index f0c4c6392..c91dbc0ac 100755 --- a/src/musikbox/cursespp/LayoutStack.cpp +++ b/src/musikbox/cursespp/LayoutStack.cpp @@ -30,202 +30,202 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include "LayoutStack.h" - -using namespace cursespp; - -LayoutStack::LayoutStack() { - this->visible = false; - this->stack = 0; -} - -LayoutStack::~LayoutStack() { - -} - -static inline bool contains(const std::deque& group, ILayoutPtr ptr) { - for (size_t i = 0; i < group.size(); i++) { - if (group.at(i) == ptr) { - return true; - } - } - - return false; -} - -static inline bool erase(std::deque& group, ILayoutPtr ptr) { - std::deque::iterator it = group.begin(); - while (it != group.end()) { - if (*it == ptr) { - group.erase(it); - return true; - } - else { - ++it; - } - } - - return false; -} - -bool LayoutStack::AddWindow(IWindowPtr window) { - throw std::runtime_error("AddWindow() not supported in LayoutStack. Use Push()"); -} - -bool LayoutStack::RemoveWindow(IWindowPtr window) { - throw std::runtime_error("RemoveWindow() not supported in LayoutStack. Use Push()"); -} - -size_t LayoutStack::GetWindowCount() { - throw std::runtime_error("GetWindowCount() not supported in LayoutStack."); -} - -IWindowPtr LayoutStack::GetWindowAt(size_t position) { - throw std::runtime_error("GetWindowAt() not supported in LayoutStack."); -} - -ILayoutStack* LayoutStack::GetLayoutStack() { - return this->stack; -} - -void LayoutStack::SetLayoutStack(ILayoutStack* stack) { - this->stack = stack; -} - -void LayoutStack::Show() { - if (!this->visible) { - std::deque::iterator it = this->layouts.begin(); - while (it != this->layouts.end()) { - (*it)->Show(); - ++it; - } - - this->visible = true; - } - - this->BringToTop(); -} - -void LayoutStack::Hide() { - if (this->visible) { - std::deque::iterator it = this->layouts.begin(); - while (it != this->layouts.end()) { - (*it)->Hide(); - ++it; - } - - this->visible = false; - } -} - -bool LayoutStack::KeyPress(const std::string& key) { - if (!this->layouts.size()) { - return false; - } - - return this->layouts.front()->KeyPress(key); -} - -IWindowPtr LayoutStack::FocusNext() { - if (!this->layouts.size()) { - return IWindowPtr(); - } - - return this->layouts.front()->FocusNext(); -} - -ILayoutPtr LayoutStack::Top() { - if (this->layouts.size()) { - return this->layouts.front(); - } - - return ILayoutPtr(); -} - -IWindowPtr LayoutStack::FocusPrev() { - if (!this->layouts.size()) { - return IWindowPtr(); - } - - return this->layouts.front()->FocusPrev(); -} - -IWindowPtr LayoutStack::GetFocus() { - if (!this->layouts.size()) { - return IWindowPtr(); - } - - return this->layouts.front()->GetFocus(); -} - -bool LayoutStack::Push(ILayoutPtr layout) { - ILayoutStack* oldStack = layout->GetLayoutStack(); - - if (oldStack && oldStack != this) { - oldStack->Pop(layout); - } - - layout->SetLayoutStack(this); - - if (!contains(this->layouts, layout)) { - this->layouts.push_front(layout); - } - - this->BringToTop(); - return true; -} - -bool LayoutStack::Pop(ILayoutPtr layout) { - bool erased = erase(this->layouts, layout); - - if (erased) { - layout->Hide(); - this->BringToTop(); - return true; - } - - return false; -} - -void LayoutStack::BringToTop() { - std::deque::reverse_iterator it = this->layouts.rbegin(); - while (it != this->layouts.rend()) { - (*it)->BringToTop(); - ++it; - } -} - -void LayoutStack::SendToBottom() { - std::deque::reverse_iterator it = this->layouts.rbegin(); - while (it != this->layouts.rend()) { - (*it)->SendToBottom(); - ++it; - } -} - -bool LayoutStack::BringToTop(ILayoutPtr layout) { - if (this->layouts.front() != layout) { - if (!erase(this->layouts, layout)) { - return false; - } - } - - this->layouts.push_front(layout); - this->BringToTop(); - return true; -} - -bool LayoutStack::SendToBottom(ILayoutPtr layout) { - if (this->layouts.back() != layout) { - if (!erase(this->layouts, layout)) { - return false; - } - } - - this->layouts.push_back(layout); - this->SendToBottom(); - return true; -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "LayoutStack.h" + +using namespace cursespp; + +LayoutStack::LayoutStack() { + this->visible = false; + this->stack = 0; +} + +LayoutStack::~LayoutStack() { + +} + +static inline bool contains(const std::deque& group, ILayoutPtr ptr) { + for (size_t i = 0; i < group.size(); i++) { + if (group.at(i) == ptr) { + return true; + } + } + + return false; +} + +static inline bool erase(std::deque& group, ILayoutPtr ptr) { + std::deque::iterator it = group.begin(); + while (it != group.end()) { + if (*it == ptr) { + group.erase(it); + return true; + } + else { + ++it; + } + } + + return false; +} + +bool LayoutStack::AddWindow(IWindowPtr window) { + throw std::runtime_error("AddWindow() not supported in LayoutStack. Use Push()"); +} + +bool LayoutStack::RemoveWindow(IWindowPtr window) { + throw std::runtime_error("RemoveWindow() not supported in LayoutStack. Use Push()"); +} + +size_t LayoutStack::GetWindowCount() { + throw std::runtime_error("GetWindowCount() not supported in LayoutStack."); +} + +IWindowPtr LayoutStack::GetWindowAt(size_t position) { + throw std::runtime_error("GetWindowAt() not supported in LayoutStack."); +} + +ILayoutStack* LayoutStack::GetLayoutStack() { + return this->stack; +} + +void LayoutStack::SetLayoutStack(ILayoutStack* stack) { + this->stack = stack; +} + +void LayoutStack::Show() { + if (!this->visible) { + std::deque::iterator it = this->layouts.begin(); + while (it != this->layouts.end()) { + (*it)->Show(); + ++it; + } + + this->visible = true; + } + + this->BringToTop(); +} + +void LayoutStack::Hide() { + if (this->visible) { + std::deque::iterator it = this->layouts.begin(); + while (it != this->layouts.end()) { + (*it)->Hide(); + ++it; + } + + this->visible = false; + } +} + +bool LayoutStack::KeyPress(const std::string& key) { + if (!this->layouts.size()) { + return false; + } + + return this->layouts.front()->KeyPress(key); +} + +IWindowPtr LayoutStack::FocusNext() { + if (!this->layouts.size()) { + return IWindowPtr(); + } + + return this->layouts.front()->FocusNext(); +} + +ILayoutPtr LayoutStack::Top() { + if (this->layouts.size()) { + return this->layouts.front(); + } + + return ILayoutPtr(); +} + +IWindowPtr LayoutStack::FocusPrev() { + if (!this->layouts.size()) { + return IWindowPtr(); + } + + return this->layouts.front()->FocusPrev(); +} + +IWindowPtr LayoutStack::GetFocus() { + if (!this->layouts.size()) { + return IWindowPtr(); + } + + return this->layouts.front()->GetFocus(); +} + +bool LayoutStack::Push(ILayoutPtr layout) { + ILayoutStack* oldStack = layout->GetLayoutStack(); + + if (oldStack && oldStack != this) { + oldStack->Pop(layout); + } + + layout->SetLayoutStack(this); + + if (!contains(this->layouts, layout)) { + this->layouts.push_front(layout); + } + + this->BringToTop(); + return true; +} + +bool LayoutStack::Pop(ILayoutPtr layout) { + bool erased = erase(this->layouts, layout); + + if (erased) { + layout->Hide(); + this->BringToTop(); + return true; + } + + return false; +} + +void LayoutStack::BringToTop() { + std::deque::reverse_iterator it = this->layouts.rbegin(); + while (it != this->layouts.rend()) { + (*it)->BringToTop(); + ++it; + } +} + +void LayoutStack::SendToBottom() { + std::deque::reverse_iterator it = this->layouts.rbegin(); + while (it != this->layouts.rend()) { + (*it)->SendToBottom(); + ++it; + } +} + +bool LayoutStack::BringToTop(ILayoutPtr layout) { + if (this->layouts.front() != layout) { + if (!erase(this->layouts, layout)) { + return false; + } + } + + this->layouts.push_front(layout); + this->BringToTop(); + return true; +} + +bool LayoutStack::SendToBottom(ILayoutPtr layout) { + if (this->layouts.back() != layout) { + if (!erase(this->layouts, layout)) { + return false; + } + } + + this->layouts.push_back(layout); + this->SendToBottom(); + return true; +} diff --git a/src/musikbox/cursespp/ListWindow.cpp b/src/musikbox/cursespp/ListWindow.cpp index 71d1f6878..ee09e3ed6 100755 --- a/src/musikbox/cursespp/ListWindow.cpp +++ b/src/musikbox/cursespp/ListWindow.cpp @@ -30,160 +30,160 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include "ListWindow.h" - -using namespace cursespp; - -typedef IScrollAdapter::ScrollPosition ScrollPos; - -size_t ListWindow::NO_SELECTION = (size_t) -1; - -ListWindow::ListWindow(IWindow *parent) -: ScrollableWindow(parent) { - this->selectedIndex = (size_t) -1; -} - -ListWindow::~ListWindow() { - -} - -void ListWindow::ScrollToTop() { - this->SetSelectedIndex(0); - this->ScrollTo(0); -} - -void ListWindow::ScrollToBottom() { - IScrollAdapter& adapter = this->GetScrollAdapter(); - this->SetSelectedIndex(std::max((size_t) 0, adapter.GetEntryCount() - 1)); - this->ScrollTo(selectedIndex); -} - -void ListWindow::ScrollUp(int delta) { - ScrollPos spos = this->GetScrollPosition(); - IScrollAdapter& adapter = this->GetScrollAdapter(); - - size_t first = spos.firstVisibleEntryIndex; - size_t last = first + spos.visibleEntryCount; - int drawIndex = first; - - int minIndex = 0; - int newIndex = this->selectedIndex - delta; - newIndex = std::max(newIndex, minIndex); - - if (newIndex < (int) first + 1) { - drawIndex = newIndex - 1; - } - - drawIndex = std::max(0, drawIndex); - - this->SetSelectedIndex(newIndex); - this->ScrollTo(drawIndex); -} - -void ListWindow::OnInvalidated() { - this->Invalidated(this, this->GetSelectedIndex()); -} - -void ListWindow::ScrollDown(int delta) { - ScrollPos spos = this->GetScrollPosition(); - IScrollAdapter& adapter = this->GetScrollAdapter(); - - size_t first = spos.firstVisibleEntryIndex; - size_t last = first + spos.visibleEntryCount; - size_t drawIndex = first; - - size_t maxIndex = adapter.GetEntryCount() - 1; - size_t newIndex = this->selectedIndex + delta; - newIndex = std::min(newIndex, maxIndex); - - if (newIndex >= last - 1) { - drawIndex = drawIndex + delta; - } - - this->SetSelectedIndex(newIndex); - this->ScrollTo(drawIndex); -} - -void ListWindow::PageUp() { - IScrollAdapter &adapter = this->GetScrollAdapter(); - ScrollPos spos = this->GetScrollPosition(); - int target = (int) this->GetPreviousPageEntryIndex(); - - /* if the target position is zero, let it be so the user can see - the top of the list. otherwise, scroll down by one to give indication - there is more to see. */ - target = (target > 0) ? target + 1 : 0; - - this->SetSelectedIndex((target == 0) ? 0 : target + 1); - this->ScrollTo(target); -} - -void ListWindow::PageDown() { - /* page down always makes the last item of this page, the first item - of the next page, and selects the following item. */ - - IScrollAdapter &adapter = this->GetScrollAdapter(); - ScrollPos spos = this->GetScrollPosition(); - - size_t lastVisible = spos.firstVisibleEntryIndex + spos.visibleEntryCount - 1; - this->SetSelectedIndex(std::min(adapter.GetEntryCount() - 1, lastVisible + 1)); - - this->ScrollTo(lastVisible); -} - -void ListWindow::ScrollTo(size_t index) { - this->GetScrollAdapter().DrawPage( - this->GetContent(), - index, - &this->GetScrollPosition()); - - this->Repaint(); -} - -void ListWindow::OnSelectionChanged(size_t newIndex, size_t oldIndex) { - /* for subclass use */ -} - -void ListWindow::SetSelectedIndex(size_t index) { - if (this->selectedIndex != index) { - size_t prev = this->selectedIndex; - this->selectedIndex = index; - this->OnSelectionChanged(index, prev); /* internal */ - this->SelectionChanged(this, index, prev); /* external */ - } -} - -size_t ListWindow::GetSelectedIndex() { - return this->selectedIndex; -} - -void ListWindow::OnAdapterChanged() { - IScrollAdapter *adapter = &GetScrollAdapter(); - - size_t count = adapter->GetEntryCount(); - - /* update initial state... */ - if (selectedIndex == NO_SELECTION) { - if (adapter->GetEntryCount()) { - this->SetSelectedIndex(0); - } - } - else if (count == 0) { - this->SetSelectedIndex(NO_SELECTION); - } - - this->ScrollTo(this->scrollPosition.firstVisibleEntryIndex); -} - -void ListWindow::OnSizeChanged() { - ScrollableWindow::OnSizeChanged(); - this->ScrollTo(this->selectedIndex); -} - -IScrollAdapter::ScrollPosition& ListWindow::GetScrollPosition() { - return this->scrollPosition; -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "ListWindow.h" + +using namespace cursespp; + +typedef IScrollAdapter::ScrollPosition ScrollPos; + +size_t ListWindow::NO_SELECTION = (size_t) -1; + +ListWindow::ListWindow(IWindow *parent) +: ScrollableWindow(parent) { + this->selectedIndex = (size_t) -1; +} + +ListWindow::~ListWindow() { + +} + +void ListWindow::ScrollToTop() { + this->SetSelectedIndex(0); + this->ScrollTo(0); +} + +void ListWindow::ScrollToBottom() { + IScrollAdapter& adapter = this->GetScrollAdapter(); + this->SetSelectedIndex(std::max((size_t) 0, adapter.GetEntryCount() - 1)); + this->ScrollTo(selectedIndex); +} + +void ListWindow::ScrollUp(int delta) { + ScrollPos spos = this->GetScrollPosition(); + IScrollAdapter& adapter = this->GetScrollAdapter(); + + size_t first = spos.firstVisibleEntryIndex; + size_t last = first + spos.visibleEntryCount; + int drawIndex = first; + + int minIndex = 0; + int newIndex = this->selectedIndex - delta; + newIndex = std::max(newIndex, minIndex); + + if (newIndex < (int) first + 1) { + drawIndex = newIndex - 1; + } + + drawIndex = std::max(0, drawIndex); + + this->SetSelectedIndex(newIndex); + this->ScrollTo(drawIndex); +} + +void ListWindow::OnInvalidated() { + this->Invalidated(this, this->GetSelectedIndex()); +} + +void ListWindow::ScrollDown(int delta) { + ScrollPos spos = this->GetScrollPosition(); + IScrollAdapter& adapter = this->GetScrollAdapter(); + + size_t first = spos.firstVisibleEntryIndex; + size_t last = first + spos.visibleEntryCount; + size_t drawIndex = first; + + size_t maxIndex = adapter.GetEntryCount() - 1; + size_t newIndex = this->selectedIndex + delta; + newIndex = std::min(newIndex, maxIndex); + + if (newIndex >= last - 1) { + drawIndex = drawIndex + delta; + } + + this->SetSelectedIndex(newIndex); + this->ScrollTo(drawIndex); +} + +void ListWindow::PageUp() { + IScrollAdapter &adapter = this->GetScrollAdapter(); + ScrollPos spos = this->GetScrollPosition(); + int target = (int) this->GetPreviousPageEntryIndex(); + + /* if the target position is zero, let it be so the user can see + the top of the list. otherwise, scroll down by one to give indication + there is more to see. */ + target = (target > 0) ? target + 1 : 0; + + this->SetSelectedIndex((target == 0) ? 0 : target + 1); + this->ScrollTo(target); +} + +void ListWindow::PageDown() { + /* page down always makes the last item of this page, the first item + of the next page, and selects the following item. */ + + IScrollAdapter &adapter = this->GetScrollAdapter(); + ScrollPos spos = this->GetScrollPosition(); + + size_t lastVisible = spos.firstVisibleEntryIndex + spos.visibleEntryCount - 1; + this->SetSelectedIndex(std::min(adapter.GetEntryCount() - 1, lastVisible + 1)); + + this->ScrollTo(lastVisible); +} + +void ListWindow::ScrollTo(size_t index) { + this->GetScrollAdapter().DrawPage( + this->GetContent(), + index, + &this->GetScrollPosition()); + + this->Repaint(); +} + +void ListWindow::OnSelectionChanged(size_t newIndex, size_t oldIndex) { + /* for subclass use */ +} + +void ListWindow::SetSelectedIndex(size_t index) { + if (this->selectedIndex != index) { + size_t prev = this->selectedIndex; + this->selectedIndex = index; + this->OnSelectionChanged(index, prev); /* internal */ + this->SelectionChanged(this, index, prev); /* external */ + } +} + +size_t ListWindow::GetSelectedIndex() { + return this->selectedIndex; +} + +void ListWindow::OnAdapterChanged() { + IScrollAdapter *adapter = &GetScrollAdapter(); + + size_t count = adapter->GetEntryCount(); + + /* update initial state... */ + if (selectedIndex == NO_SELECTION) { + if (adapter->GetEntryCount()) { + this->SetSelectedIndex(0); + } + } + else if (count == 0) { + this->SetSelectedIndex(NO_SELECTION); + } + + this->ScrollTo(this->scrollPosition.firstVisibleEntryIndex); +} + +void ListWindow::OnSizeChanged() { + ScrollableWindow::OnSizeChanged(); + this->ScrollTo(this->selectedIndex); +} + +IScrollAdapter::ScrollPosition& ListWindow::GetScrollPosition() { + return this->scrollPosition; +} diff --git a/src/musikbox/cursespp/Message.cpp b/src/musikbox/cursespp/Message.cpp index 634a4b142..32db8d227 100755 --- a/src/musikbox/cursespp/Message.cpp +++ b/src/musikbox/cursespp/Message.cpp @@ -30,46 +30,46 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include "Message.h" - -using namespace cursespp; - -IMessagePtr Message::Create( - IMessageTarget* target, - int messageType, - int64 data1, - int64 data2) -{ - return IMessagePtr(new Message(target, messageType, data1, data2)); -} - -Message::Message( - IMessageTarget* target, - int messageType, - int64 data1, - int64 data2) -{ - this->target = target; - this->messageType = messageType; - this->data1 = data1; - this->data2 = data2; -} - -IMessageTarget* Message::Target() { - return this->target; -} - -int Message::Type() { - return this->messageType; -} - -int64 Message::UserData1() { - return this->data1; -} - -int64 Message::UserData2() { - return this->data2; +////////////////////////////////////////////////////////////////////////////// + +#include +#include "Message.h" + +using namespace cursespp; + +IMessagePtr Message::Create( + IMessageTarget* target, + int messageType, + int64 data1, + int64 data2) +{ + return IMessagePtr(new Message(target, messageType, data1, data2)); +} + +Message::Message( + IMessageTarget* target, + int messageType, + int64 data1, + int64 data2) +{ + this->target = target; + this->messageType = messageType; + this->data1 = data1; + this->data2 = data2; +} + +IMessageTarget* Message::Target() { + return this->target; +} + +int Message::Type() { + return this->messageType; +} + +int64 Message::UserData1() { + return this->data1; +} + +int64 Message::UserData2() { + return this->data2; } \ No newline at end of file diff --git a/src/musikbox/cursespp/MessageQueue.cpp b/src/musikbox/cursespp/MessageQueue.cpp index 0f7da9895..91b81e458 100755 --- a/src/musikbox/cursespp/MessageQueue.cpp +++ b/src/musikbox/cursespp/MessageQueue.cpp @@ -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 -#include -#include "MessageQueue.h" - -using namespace boost::chrono; -using namespace cursespp; - -MessageQueue MessageQueue::instance; - -MessageQueue::MessageQueue() { - -} - -MessageQueue& MessageQueue::Instance() { - return MessageQueue::instance; -} - -void MessageQueue::Dispatch() { - milliseconds now = duration_cast( - system_clock::now().time_since_epoch()); - - typedef std::list::iterator Iterator; - std::list toDispatch; - - { - boost::recursive_mutex::scoped_lock lock(this->queueMutex); - - Iterator it = this->queue.begin(); - - bool done = false; - while (!done && it != this->queue.end()) { - /* messages are time-ordered. pop and dispatch one at a time - until we get to a message that should be delivered in the future, - or the queue has been exhausted. */ - - EnqueuedMessage *m = (*it); - - if (now >= m->time) { - if (m->message.get()->Target()->IsAcceptingMessages()) { - toDispatch.push_back(m); - it = this->queue.erase(it); - } - else { - it++; - } - } - else { - done = true; - } - } - } - - /* dispatch outside of the critical section */ - - Iterator it = toDispatch.begin(); - while (it != toDispatch.end()) { - this->Dispatch((*it)->message); - delete *it; - it++; - } -} - -void MessageQueue::Remove(IMessageTarget *target, int type) { - boost::recursive_mutex::scoped_lock lock(this->queueMutex); - - std::list::iterator it = this->queue.begin(); - while (it != this->queue.end()) { - IMessagePtr current = (*it)->message; - - if (current->Target() == target) { - if (type == -1 || type == current->Type()) { - delete (*it); - it = this->queue.erase(it); - continue; - } - } - - ++it; - } -} - -void MessageQueue::Post(IMessagePtr message, int64 delayMs) { - boost::recursive_mutex::scoped_lock lock(this->queueMutex); - - delayMs = std::max((int64) 0, delayMs); - - milliseconds now = duration_cast( - system_clock::now().time_since_epoch()); - - EnqueuedMessage *m = new EnqueuedMessage(); - m->message = message; - m->time = now + milliseconds(delayMs); - - /* the queue is time ordered. start from the front of the queue, and - work our way back until we find the correct place to insert the new one */ - - typedef std::list::iterator Iterator; - Iterator curr = this->queue.begin(); - - while (curr != this->queue.end()) { - if ((*curr)->time <= m->time) { - curr++; - } - else { - break; - } - } - - this->queue.insert(curr, m); -} - -void MessageQueue::Dispatch(IMessagePtr message) { - message->Target()->ProcessMessage(*message); -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "MessageQueue.h" + +using namespace boost::chrono; +using namespace cursespp; + +MessageQueue MessageQueue::instance; + +MessageQueue::MessageQueue() { + +} + +MessageQueue& MessageQueue::Instance() { + return MessageQueue::instance; +} + +void MessageQueue::Dispatch() { + milliseconds now = duration_cast( + system_clock::now().time_since_epoch()); + + typedef std::list::iterator Iterator; + std::list toDispatch; + + { + boost::recursive_mutex::scoped_lock lock(this->queueMutex); + + Iterator it = this->queue.begin(); + + bool done = false; + while (!done && it != this->queue.end()) { + /* messages are time-ordered. pop and dispatch one at a time + until we get to a message that should be delivered in the future, + or the queue has been exhausted. */ + + EnqueuedMessage *m = (*it); + + if (now >= m->time) { + if (m->message.get()->Target()->IsAcceptingMessages()) { + toDispatch.push_back(m); + it = this->queue.erase(it); + } + else { + it++; + } + } + else { + done = true; + } + } + } + + /* dispatch outside of the critical section */ + + Iterator it = toDispatch.begin(); + while (it != toDispatch.end()) { + this->Dispatch((*it)->message); + delete *it; + it++; + } +} + +void MessageQueue::Remove(IMessageTarget *target, int type) { + boost::recursive_mutex::scoped_lock lock(this->queueMutex); + + std::list::iterator it = this->queue.begin(); + while (it != this->queue.end()) { + IMessagePtr current = (*it)->message; + + if (current->Target() == target) { + if (type == -1 || type == current->Type()) { + delete (*it); + it = this->queue.erase(it); + continue; + } + } + + ++it; + } +} + +void MessageQueue::Post(IMessagePtr message, int64 delayMs) { + boost::recursive_mutex::scoped_lock lock(this->queueMutex); + + delayMs = std::max((int64) 0, delayMs); + + milliseconds now = duration_cast( + system_clock::now().time_since_epoch()); + + EnqueuedMessage *m = new EnqueuedMessage(); + m->message = message; + m->time = now + milliseconds(delayMs); + + /* the queue is time ordered. start from the front of the queue, and + work our way back until we find the correct place to insert the new one */ + + typedef std::list::iterator Iterator; + Iterator curr = this->queue.begin(); + + while (curr != this->queue.end()) { + if ((*curr)->time <= m->time) { + curr++; + } + else { + break; + } + } + + this->queue.insert(curr, m); +} + +void MessageQueue::Dispatch(IMessagePtr message) { + message->Target()->ProcessMessage(*message); +} diff --git a/src/musikbox/cursespp/MultiLineEntry.cpp b/src/musikbox/cursespp/MultiLineEntry.cpp index 2fe5fe138..6790207fa 100755 --- a/src/musikbox/cursespp/MultiLineEntry.cpp +++ b/src/musikbox/cursespp/MultiLineEntry.cpp @@ -30,168 +30,168 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include -#include "MultiLineEntry.h" - -using namespace cursespp; - -MultiLineEntry::MultiLineEntry(const std::string& value, int64 attrs) { - this->value = value; - this->charCount = value.size(); - this->width = -1; - this->attrs = attrs; -} - -size_t MultiLineEntry::GetLineCount() { - return std::max((size_t) 1, this->lines.size()); -} - -std::string MultiLineEntry::GetLine(size_t n) { - return this->lines.at(n); -} - -int64 MultiLineEntry::GetAttrs(size_t line) { - return this->attrs; -} - -inline static void breakIntoSubLines( - std::string& line, - size_t width, - std::vector& output) -{ - size_t len = u8len(line); - size_t count = (int)ceil((float)len / (float)width); - - /* easy case: the line fits on a single line! */ - - if (count <= 1) { - output.push_back(line); - } - - /* difficult case: the line needs to be split multiple sub-lines to fit - the output display */ - - else { - /* split by whitespace */ - - std::vector words; - boost::algorithm::split(words, line, boost::is_any_of(" \t\v\f\r")); - - /* this isn't super efficient, but let's find all words that are greater - than the width and break them into more sublines... it's possible to - do this more efficiently in the loop below this with a bunch of additional - logic, but let's keep things simple unless we run into performance - problems! */ - - std::vector sanitizedWords; - for (size_t i = 0; i < words.size(); i++) { - std::string word = words.at(i); - size_t len = u8len(word); - - /* this word is fine, it'll easily fit on its own line of necessary */ - - if (width >= len) { - sanitizedWords.push_back(word); - } - - /* otherwise, the word needs to be broken into multiple lines. */ - - else { - std::string accum; - - /* ugh, we gotta split on UTF8 characters, not actual characters. - this makes things a bit more difficult... we iterate over the string - one displayable character at a time, and break it apart into separate - lines as necessary. */ - - std::string::iterator begin = word.begin(); - std::string::iterator end = word.begin(); - int count = 0; - bool done = false; - while (end != word.end()) { - utf8::unchecked::next(end); - ++count; - - if (count == width || end == word.end()) { - sanitizedWords.push_back(std::string(begin, end)); - begin = end; - count = 0; - } - } - } - } - - words.clear(); - - /* now we have a bunch of tokenized words. let's string them together - into sequences that fit in the output window's width */ - - std::string accum; - size_t accumLength = 0; - - for (size_t i = 0; i < sanitizedWords.size(); i++) { - std::string word = sanitizedWords.at(i); - size_t wordLength = u8len(word); - size_t extra = (i != 0); - - /* we have enough space for this new word. accumulate it. */ - - if (accumLength + extra + wordLength < width) { - if (extra) { - accum += " "; - } - - accum += word; - accumLength += wordLength + extra; - } - - /* otherwise, flush the current line, and start a new one... */ - - else { - if (accum.size()) { - output.push_back(accum); - } - - /* special case -- if the word is the exactly length of the - width, just add it as a new line and reset... */ - - if (wordLength == width) { - output.push_back(word); - accum = ""; - accumLength = 0; - } - - /* otherwise, let's start accumulating a new line! */ - - else { - accum = word; - accumLength = wordLength; - } - } - } - - if (accum.size()) { - output.push_back(accum); - } - } -} - -void MultiLineEntry::SetWidth(size_t width) { - if (this->width != width) { - this->width = width; - - this->lines.clear(); - - std::vector split; - boost::algorithm::split(split, this->value, boost::is_any_of("\n")); - - for (size_t i = 0; i < split.size(); i++) { - breakIntoSubLines(split.at(i), this->width, this->lines); - } - } -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "MultiLineEntry.h" + +using namespace cursespp; + +MultiLineEntry::MultiLineEntry(const std::string& value, int64 attrs) { + this->value = value; + this->charCount = value.size(); + this->width = -1; + this->attrs = attrs; +} + +size_t MultiLineEntry::GetLineCount() { + return std::max((size_t) 1, this->lines.size()); +} + +std::string MultiLineEntry::GetLine(size_t n) { + return this->lines.at(n); +} + +int64 MultiLineEntry::GetAttrs(size_t line) { + return this->attrs; +} + +inline static void breakIntoSubLines( + std::string& line, + size_t width, + std::vector& output) +{ + size_t len = u8len(line); + size_t count = (int)ceil((float)len / (float)width); + + /* easy case: the line fits on a single line! */ + + if (count <= 1) { + output.push_back(line); + } + + /* difficult case: the line needs to be split multiple sub-lines to fit + the output display */ + + else { + /* split by whitespace */ + + std::vector words; + boost::algorithm::split(words, line, boost::is_any_of(" \t\v\f\r")); + + /* this isn't super efficient, but let's find all words that are greater + than the width and break them into more sublines... it's possible to + do this more efficiently in the loop below this with a bunch of additional + logic, but let's keep things simple unless we run into performance + problems! */ + + std::vector sanitizedWords; + for (size_t i = 0; i < words.size(); i++) { + std::string word = words.at(i); + size_t len = u8len(word); + + /* this word is fine, it'll easily fit on its own line of necessary */ + + if (width >= len) { + sanitizedWords.push_back(word); + } + + /* otherwise, the word needs to be broken into multiple lines. */ + + else { + std::string accum; + + /* ugh, we gotta split on UTF8 characters, not actual characters. + this makes things a bit more difficult... we iterate over the string + one displayable character at a time, and break it apart into separate + lines as necessary. */ + + std::string::iterator begin = word.begin(); + std::string::iterator end = word.begin(); + int count = 0; + bool done = false; + while (end != word.end()) { + utf8::unchecked::next(end); + ++count; + + if (count == width || end == word.end()) { + sanitizedWords.push_back(std::string(begin, end)); + begin = end; + count = 0; + } + } + } + } + + words.clear(); + + /* now we have a bunch of tokenized words. let's string them together + into sequences that fit in the output window's width */ + + std::string accum; + size_t accumLength = 0; + + for (size_t i = 0; i < sanitizedWords.size(); i++) { + std::string word = sanitizedWords.at(i); + size_t wordLength = u8len(word); + size_t extra = (i != 0); + + /* we have enough space for this new word. accumulate it. */ + + if (accumLength + extra + wordLength < width) { + if (extra) { + accum += " "; + } + + accum += word; + accumLength += wordLength + extra; + } + + /* otherwise, flush the current line, and start a new one... */ + + else { + if (accum.size()) { + output.push_back(accum); + } + + /* special case -- if the word is the exactly length of the + width, just add it as a new line and reset... */ + + if (wordLength == width) { + output.push_back(word); + accum = ""; + accumLength = 0; + } + + /* otherwise, let's start accumulating a new line! */ + + else { + accum = word; + accumLength = wordLength; + } + } + } + + if (accum.size()) { + output.push_back(accum); + } + } +} + +void MultiLineEntry::SetWidth(size_t width) { + if (this->width != width) { + this->width = width; + + this->lines.clear(); + + std::vector split; + boost::algorithm::split(split, this->value, boost::is_any_of("\n")); + + for (size_t i = 0; i < split.size(); i++) { + breakIntoSubLines(split.at(i), this->width, this->lines); + } + } +} diff --git a/src/musikbox/cursespp/Screen.cpp b/src/musikbox/cursespp/Screen.cpp index cff0f5094..0855e4586 100755 --- a/src/musikbox/cursespp/Screen.cpp +++ b/src/musikbox/cursespp/Screen.cpp @@ -30,20 +30,20 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include "Screen.h" - -using namespace cursespp; - -Screen::Screen() { -} - -int Screen::GetWidth() { - return getmaxx(stdscr); -} - -int Screen::GetHeight() { - return getmaxy(stdscr); -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "Screen.h" + +using namespace cursespp; + +Screen::Screen() { +} + +int Screen::GetWidth() { + return getmaxx(stdscr); +} + +int Screen::GetHeight() { + return getmaxy(stdscr); +} diff --git a/src/musikbox/cursespp/ScrollAdapterBase.cpp b/src/musikbox/cursespp/ScrollAdapterBase.cpp index 5f178d3e3..457e3eb21 100755 --- a/src/musikbox/cursespp/ScrollAdapterBase.cpp +++ b/src/musikbox/cursespp/ScrollAdapterBase.cpp @@ -30,147 +30,147 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include "ScrollAdapterBase.h" -#include "MultiLineEntry.h" - -using namespace cursespp; - -typedef IScrollAdapter::EntryPtr EntryPtr; - -ScrollAdapterBase::ScrollAdapterBase() { - this->height = 0; - this->width = 0; -} - -ScrollAdapterBase::~ScrollAdapterBase() { - -} - -void ScrollAdapterBase::SetDisplaySize(size_t width, size_t height) { - this->width = width; - this->height = height; -} - -size_t ScrollAdapterBase::GetLineCount() { - return -1; -} - -void ScrollAdapterBase::GetVisibleItems( - size_t desired, std::deque& target, size_t& start) -{ - size_t actual = desired; - - /* ensure we have enough data to draw from the specified position - to the end. if we don't try to back up a bit until we can fill - the buffer */ - int totalHeight = (int) this->height; - - /* forward search first... */ - int end = (int) GetEntryCount(); - for (int i = (int) desired; i < end && totalHeight > 0; i++) { - EntryPtr entry = this->GetEntry(i); - entry->SetWidth(this->width); - totalHeight -= entry->GetLineCount(); - target.push_back(entry); - } - - if (totalHeight > 0) { - target.clear(); - - /* oops, let's move backwards from the end */ - totalHeight = this->height; - int i = GetEntryCount() - 1; - while (i >= 0 && totalHeight >= 0) { - EntryPtr entry = this->GetEntry(i); - entry->SetWidth(this->width); - - int lines = entry->GetLineCount(); - if (lines > totalHeight) { - break; /* this Entry won't fit. bail. */ - } - - totalHeight -= lines; - target.push_front(entry); - --i; - } - - actual = i + 1; - } - - start = actual; -} - -void ScrollAdapterBase::DrawPage(WINDOW* window, size_t index, ScrollPosition *result) { - if (result != NULL) { - result->visibleEntryCount = 0; - result->firstVisibleEntryIndex = 0; - result->lineCount = 0; - result->totalEntries = 0; - result->logicalIndex = 0; - } - - werase(window); - - if (this->height == 0 || this->width == 0 || this->GetEntryCount() == 0) { - return; - } - - if (index >= GetEntryCount()) { - index = GetEntryCount() - 1; - } - - /* unfortunately this needs to go here so the GetEntry() method knows - what the the implied focus is */ - result->logicalIndex = index; - - std::deque visible; - size_t topIndex; /* calculated by GetVisibleItems */ - GetVisibleItems(index, visible, topIndex); - - size_t drawnLines = 0; - - for (size_t e = 0; e < visible.size(); e++) { - EntryPtr entry = visible.at(e); - size_t count = entry->GetLineCount(); - - for (size_t i = 0; i < count && drawnLines < this->height; i++) { - int64 attrs = entry->GetAttrs(i); - - if (attrs != -1) { - wattron(window, attrs); - } - - std::string line = entry->GetLine(i); - size_t len = u8len(line); - - /* pad with empty spaces to the end of the line. this allows us to - do highlight rows. this should probably be configurable. */ - - int remain = this->width - len; - if (remain > 0) { - line += std::string(remain, ' '); - } - - /* string is padded above, we don't need a \n */ - - wprintw(window, "%s", line.c_str()); - - if (attrs != -1) { - wattroff(window, attrs); - } - - ++drawnLines; - } - } - - if (result != NULL) { - result->visibleEntryCount = visible.size(); - result->firstVisibleEntryIndex = topIndex; - result->lineCount = drawnLines; - result->totalEntries = GetEntryCount(); - result->logicalIndex = index; - } -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "ScrollAdapterBase.h" +#include "MultiLineEntry.h" + +using namespace cursespp; + +typedef IScrollAdapter::EntryPtr EntryPtr; + +ScrollAdapterBase::ScrollAdapterBase() { + this->height = 0; + this->width = 0; +} + +ScrollAdapterBase::~ScrollAdapterBase() { + +} + +void ScrollAdapterBase::SetDisplaySize(size_t width, size_t height) { + this->width = width; + this->height = height; +} + +size_t ScrollAdapterBase::GetLineCount() { + return -1; +} + +void ScrollAdapterBase::GetVisibleItems( + size_t desired, std::deque& target, size_t& start) +{ + size_t actual = desired; + + /* ensure we have enough data to draw from the specified position + to the end. if we don't try to back up a bit until we can fill + the buffer */ + int totalHeight = (int) this->height; + + /* forward search first... */ + int end = (int) GetEntryCount(); + for (int i = (int) desired; i < end && totalHeight > 0; i++) { + EntryPtr entry = this->GetEntry(i); + entry->SetWidth(this->width); + totalHeight -= entry->GetLineCount(); + target.push_back(entry); + } + + if (totalHeight > 0) { + target.clear(); + + /* oops, let's move backwards from the end */ + totalHeight = this->height; + int i = GetEntryCount() - 1; + while (i >= 0 && totalHeight >= 0) { + EntryPtr entry = this->GetEntry(i); + entry->SetWidth(this->width); + + int lines = entry->GetLineCount(); + if (lines > totalHeight) { + break; /* this Entry won't fit. bail. */ + } + + totalHeight -= lines; + target.push_front(entry); + --i; + } + + actual = i + 1; + } + + start = actual; +} + +void ScrollAdapterBase::DrawPage(WINDOW* window, size_t index, ScrollPosition *result) { + if (result != NULL) { + result->visibleEntryCount = 0; + result->firstVisibleEntryIndex = 0; + result->lineCount = 0; + result->totalEntries = 0; + result->logicalIndex = 0; + } + + werase(window); + + if (this->height == 0 || this->width == 0 || this->GetEntryCount() == 0) { + return; + } + + if (index >= GetEntryCount()) { + index = GetEntryCount() - 1; + } + + /* unfortunately this needs to go here so the GetEntry() method knows + what the the implied focus is */ + result->logicalIndex = index; + + std::deque visible; + size_t topIndex; /* calculated by GetVisibleItems */ + GetVisibleItems(index, visible, topIndex); + + size_t drawnLines = 0; + + for (size_t e = 0; e < visible.size(); e++) { + EntryPtr entry = visible.at(e); + size_t count = entry->GetLineCount(); + + for (size_t i = 0; i < count && drawnLines < this->height; i++) { + int64 attrs = entry->GetAttrs(i); + + if (attrs != -1) { + wattron(window, attrs); + } + + std::string line = entry->GetLine(i); + size_t len = u8len(line); + + /* pad with empty spaces to the end of the line. this allows us to + do highlight rows. this should probably be configurable. */ + + int remain = this->width - len; + if (remain > 0) { + line += std::string(remain, ' '); + } + + /* string is padded above, we don't need a \n */ + + wprintw(window, "%s", line.c_str()); + + if (attrs != -1) { + wattroff(window, attrs); + } + + ++drawnLines; + } + } + + if (result != NULL) { + result->visibleEntryCount = visible.size(); + result->firstVisibleEntryIndex = topIndex; + result->lineCount = drawnLines; + result->totalEntries = GetEntryCount(); + result->logicalIndex = index; + } +} diff --git a/src/musikbox/cursespp/ScrollableWindow.cpp b/src/musikbox/cursespp/ScrollableWindow.cpp index 5c2ef8af5..4c19f59a1 100755 --- a/src/musikbox/cursespp/ScrollableWindow.cpp +++ b/src/musikbox/cursespp/ScrollableWindow.cpp @@ -30,174 +30,174 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include - -#include "ScrollableWindow.h" -#include "Screen.h" -#include "Colors.h" - -#include - -using namespace cursespp; - -typedef IScrollAdapter::ScrollPosition ScrollPos; - -#define REDRAW_VISIBLE_PAGE() \ - { \ - ScrollPos& pos = GetScrollPosition(); \ - GetScrollAdapter().DrawPage( \ - this->GetContent(), \ - pos.firstVisibleEntryIndex, \ - &pos); \ - } \ - -ScrollableWindow::ScrollableWindow(IWindow *parent) -: Window(parent) { -} - -ScrollableWindow::~ScrollableWindow() { -} - -void ScrollableWindow::OnSizeChanged() { - Window::OnSizeChanged(); - - IScrollAdapter& adapter = this->GetScrollAdapter(); - ScrollPos& pos = this->GetScrollPosition(); - - adapter.SetDisplaySize( - this->GetContentWidth(), - this->GetContentHeight()); -} - -ScrollPos& ScrollableWindow::GetScrollPosition() { - return this->scrollPosition; -} - -bool ScrollableWindow::KeyPress(const std::string& key) { - if (key == "KEY_NPAGE") { this->PageDown(); return true; } - else if (key == "KEY_PPAGE") { this->PageUp(); return true; } - else if (key == "KEY_DOWN") { this->ScrollDown(); return true; } - else if (key == "KEY_UP") { this->ScrollUp(); return true; } - else if (key == "KEY_HOME") { this->ScrollToTop(); return true; } - else if (key == "KEY_END") { this->ScrollToBottom(); return true; } - return false; -} - -void ScrollableWindow::OnAdapterChanged() { - IScrollAdapter *adapter = &GetScrollAdapter(); - - adapter->SetDisplaySize(GetContentWidth(), GetContentHeight()); - - if (IsLastItemVisible()) { - this->ScrollToBottom(); - } - else { - ScrollPos &pos = this->GetScrollPosition(); - - adapter->DrawPage( - this->GetContent(), - pos.firstVisibleEntryIndex, - &pos); - - this->Repaint(); - } -} - -void ScrollableWindow::Show() { - Window::Show(); - this->OnAdapterChanged(); -} - -void ScrollableWindow::ScrollToTop() { - GetScrollAdapter().DrawPage(this->GetContent(), 0, &this->GetScrollPosition()); - this->Repaint(); -} - -void ScrollableWindow::ScrollToBottom() { - GetScrollAdapter().DrawPage( - this->GetContent(), - GetScrollAdapter().GetEntryCount(), - &this->GetScrollPosition()); - - this->Repaint(); -} - -void ScrollableWindow::ScrollUp(int delta) { - ScrollPos &pos = this->GetScrollPosition(); - - if (pos.firstVisibleEntryIndex > 0) { - GetScrollAdapter().DrawPage( - this->GetContent(), - pos.firstVisibleEntryIndex - delta, - &pos); - - this->Repaint(); - } -} - -void ScrollableWindow::ScrollDown(int delta) { - ScrollPos &pos = this->GetScrollPosition(); - - GetScrollAdapter().DrawPage( - this->GetContent(), - pos.firstVisibleEntryIndex + delta, - &pos); - - this->Repaint(); -} - -size_t ScrollableWindow::GetPreviousPageEntryIndex() { - IScrollAdapter* adapter = &GetScrollAdapter(); - int remaining = this->GetContentHeight(); - int width = this->GetContentWidth(); - - int i = this->GetScrollPosition().firstVisibleEntryIndex; - while (i >= 0) { - IScrollAdapter::EntryPtr entry = adapter->GetEntry(i); - entry->SetWidth(width); - - int count = entry->GetLineCount(); - if (count > remaining) { - break; - } - - i--; - remaining -= count; - } - - return std::max(0, i); -} - -void ScrollableWindow::PageUp() { - ScrollUp( - this->GetScrollPosition().firstVisibleEntryIndex - - GetPreviousPageEntryIndex()); -} - -void ScrollableWindow::PageDown() { - ScrollDown(this->GetScrollPosition().visibleEntryCount - 1); -} - -bool ScrollableWindow::IsLastItemVisible() { - ScrollPos &pos = this->GetScrollPosition(); - - size_t lastIndex = pos.totalEntries; - lastIndex = (lastIndex == 0) ? lastIndex : lastIndex - 1; - - size_t firstVisible = pos.firstVisibleEntryIndex; - size_t lastVisible = firstVisible + pos.visibleEntryCount; - return lastIndex >= firstVisible && lastIndex <= lastVisible; -} - -void ScrollableWindow::Blur() { - Window::Blur(); - REDRAW_VISIBLE_PAGE(); -} - -void ScrollableWindow::Focus() { - Window::Focus(); - REDRAW_VISIBLE_PAGE(); -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "ScrollableWindow.h" +#include "Screen.h" +#include "Colors.h" + +#include + +using namespace cursespp; + +typedef IScrollAdapter::ScrollPosition ScrollPos; + +#define REDRAW_VISIBLE_PAGE() \ + { \ + ScrollPos& pos = GetScrollPosition(); \ + GetScrollAdapter().DrawPage( \ + this->GetContent(), \ + pos.firstVisibleEntryIndex, \ + &pos); \ + } \ + +ScrollableWindow::ScrollableWindow(IWindow *parent) +: Window(parent) { +} + +ScrollableWindow::~ScrollableWindow() { +} + +void ScrollableWindow::OnSizeChanged() { + Window::OnSizeChanged(); + + IScrollAdapter& adapter = this->GetScrollAdapter(); + ScrollPos& pos = this->GetScrollPosition(); + + adapter.SetDisplaySize( + this->GetContentWidth(), + this->GetContentHeight()); +} + +ScrollPos& ScrollableWindow::GetScrollPosition() { + return this->scrollPosition; +} + +bool ScrollableWindow::KeyPress(const std::string& key) { + if (key == "KEY_NPAGE") { this->PageDown(); return true; } + else if (key == "KEY_PPAGE") { this->PageUp(); return true; } + else if (key == "KEY_DOWN") { this->ScrollDown(); return true; } + else if (key == "KEY_UP") { this->ScrollUp(); return true; } + else if (key == "KEY_HOME") { this->ScrollToTop(); return true; } + else if (key == "KEY_END") { this->ScrollToBottom(); return true; } + return false; +} + +void ScrollableWindow::OnAdapterChanged() { + IScrollAdapter *adapter = &GetScrollAdapter(); + + adapter->SetDisplaySize(GetContentWidth(), GetContentHeight()); + + if (IsLastItemVisible()) { + this->ScrollToBottom(); + } + else { + ScrollPos &pos = this->GetScrollPosition(); + + adapter->DrawPage( + this->GetContent(), + pos.firstVisibleEntryIndex, + &pos); + + this->Repaint(); + } +} + +void ScrollableWindow::Show() { + Window::Show(); + this->OnAdapterChanged(); +} + +void ScrollableWindow::ScrollToTop() { + GetScrollAdapter().DrawPage(this->GetContent(), 0, &this->GetScrollPosition()); + this->Repaint(); +} + +void ScrollableWindow::ScrollToBottom() { + GetScrollAdapter().DrawPage( + this->GetContent(), + GetScrollAdapter().GetEntryCount(), + &this->GetScrollPosition()); + + this->Repaint(); +} + +void ScrollableWindow::ScrollUp(int delta) { + ScrollPos &pos = this->GetScrollPosition(); + + if (pos.firstVisibleEntryIndex > 0) { + GetScrollAdapter().DrawPage( + this->GetContent(), + pos.firstVisibleEntryIndex - delta, + &pos); + + this->Repaint(); + } +} + +void ScrollableWindow::ScrollDown(int delta) { + ScrollPos &pos = this->GetScrollPosition(); + + GetScrollAdapter().DrawPage( + this->GetContent(), + pos.firstVisibleEntryIndex + delta, + &pos); + + this->Repaint(); +} + +size_t ScrollableWindow::GetPreviousPageEntryIndex() { + IScrollAdapter* adapter = &GetScrollAdapter(); + int remaining = this->GetContentHeight(); + int width = this->GetContentWidth(); + + int i = this->GetScrollPosition().firstVisibleEntryIndex; + while (i >= 0) { + IScrollAdapter::EntryPtr entry = adapter->GetEntry(i); + entry->SetWidth(width); + + int count = entry->GetLineCount(); + if (count > remaining) { + break; + } + + i--; + remaining -= count; + } + + return std::max(0, i); +} + +void ScrollableWindow::PageUp() { + ScrollUp( + this->GetScrollPosition().firstVisibleEntryIndex - + GetPreviousPageEntryIndex()); +} + +void ScrollableWindow::PageDown() { + ScrollDown(this->GetScrollPosition().visibleEntryCount - 1); +} + +bool ScrollableWindow::IsLastItemVisible() { + ScrollPos &pos = this->GetScrollPosition(); + + size_t lastIndex = pos.totalEntries; + lastIndex = (lastIndex == 0) ? lastIndex : lastIndex - 1; + + size_t firstVisible = pos.firstVisibleEntryIndex; + size_t lastVisible = firstVisible + pos.visibleEntryCount; + return lastIndex >= firstVisible && lastIndex <= lastVisible; +} + +void ScrollableWindow::Blur() { + Window::Blur(); + REDRAW_VISIBLE_PAGE(); +} + +void ScrollableWindow::Focus() { + Window::Focus(); + REDRAW_VISIBLE_PAGE(); +} diff --git a/src/musikbox/cursespp/SimpleScrollAdapter.cpp b/src/musikbox/cursespp/SimpleScrollAdapter.cpp index f8fc81402..e0be93fef 100755 --- a/src/musikbox/cursespp/SimpleScrollAdapter.cpp +++ b/src/musikbox/cursespp/SimpleScrollAdapter.cpp @@ -30,51 +30,51 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" - -#include "SimpleScrollAdapter.h" -#include "SingleLineEntry.h" -#include "MultiLineEntry.h" -#include -#include - -using namespace cursespp; - -#define MAX_ENTRY_COUNT 0xffffffff - -typedef IScrollAdapter::EntryPtr EntryPtr; - -SimpleScrollAdapter::SimpleScrollAdapter() { - this->maxEntries = MAX_ENTRY_COUNT; -} - -SimpleScrollAdapter::~SimpleScrollAdapter() { - -} - -void SimpleScrollAdapter::Clear() { - this->entries.clear(); -} - -size_t SimpleScrollAdapter::GetEntryCount() { - return this->entries.size(); -} - -void SimpleScrollAdapter::SetMaxEntries(size_t maxEntries) { - this->maxEntries = maxEntries; -} - -EntryPtr SimpleScrollAdapter::GetEntry(size_t index) { - return this->entries.at(index); -} - -void SimpleScrollAdapter::AddEntry(std::shared_ptr entry) { - entry->SetWidth(this->GetWidth()); - entries.push_back(entry); - - while (entries.size() > this->maxEntries) { - entries.pop_front(); - } -} +////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "SimpleScrollAdapter.h" +#include "SingleLineEntry.h" +#include "MultiLineEntry.h" +#include +#include + +using namespace cursespp; + +#define MAX_ENTRY_COUNT 0xffffffff + +typedef IScrollAdapter::EntryPtr EntryPtr; + +SimpleScrollAdapter::SimpleScrollAdapter() { + this->maxEntries = MAX_ENTRY_COUNT; +} + +SimpleScrollAdapter::~SimpleScrollAdapter() { + +} + +void SimpleScrollAdapter::Clear() { + this->entries.clear(); +} + +size_t SimpleScrollAdapter::GetEntryCount() { + return this->entries.size(); +} + +void SimpleScrollAdapter::SetMaxEntries(size_t maxEntries) { + this->maxEntries = maxEntries; +} + +EntryPtr SimpleScrollAdapter::GetEntry(size_t index) { + return this->entries.at(index); +} + +void SimpleScrollAdapter::AddEntry(std::shared_ptr entry) { + entry->SetWidth(this->GetWidth()); + entries.push_back(entry); + + while (entries.size() > this->maxEntries) { + entries.pop_front(); + } +} diff --git a/src/musikbox/cursespp/SingleLineEntry.cpp b/src/musikbox/cursespp/SingleLineEntry.cpp index 62820aafb..4e0b9acbe 100755 --- a/src/musikbox/cursespp/SingleLineEntry.cpp +++ b/src/musikbox/cursespp/SingleLineEntry.cpp @@ -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 -#include "SingleLineEntry.h" -#include - -using namespace cursespp; - -SingleLineEntry::SingleLineEntry(const std::string& value) { - this->value = value; - this->attrs = -1; -} - -void SingleLineEntry::SetWidth(size_t width) { - this->width = width; -} - -int64 SingleLineEntry::GetAttrs(size_t line) { - return this->attrs; -} - -void SingleLineEntry::SetAttrs(int64 attrs) { - this->attrs = attrs; -} - -size_t SingleLineEntry::GetLineCount() { - return 1; -} - -std::string SingleLineEntry::GetLine(size_t line) { - return u8substr(this->value, 0, this->width > 0 ? this->width : 0); +////////////////////////////////////////////////////////////////////////////// + +#include +#include "SingleLineEntry.h" +#include + +using namespace cursespp; + +SingleLineEntry::SingleLineEntry(const std::string& value) { + this->value = value; + this->attrs = -1; +} + +void SingleLineEntry::SetWidth(size_t width) { + this->width = width; +} + +int64 SingleLineEntry::GetAttrs(size_t line) { + return this->attrs; +} + +void SingleLineEntry::SetAttrs(int64 attrs) { + this->attrs = attrs; +} + +size_t SingleLineEntry::GetLineCount() { + return 1; +} + +std::string SingleLineEntry::GetLine(size_t line) { + return u8substr(this->value, 0, this->width > 0 ? this->width : 0); } \ No newline at end of file diff --git a/src/musikbox/cursespp/Window.cpp b/src/musikbox/cursespp/Window.cpp index 2ee7565ec..4c372c8a2 100755 --- a/src/musikbox/cursespp/Window.cpp +++ b/src/musikbox/cursespp/Window.cpp @@ -30,425 +30,425 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include "Window.h" -#include "IWindowGroup.h" -#include "IInput.h" -#include "Message.h" -#include "MessageQueue.h" - -using namespace cursespp; - -static int NEXT_ID = 0; -static bool drawPending = false; - -void Window::WriteToScreen(IInput* input) { - if (drawPending) { - drawPending = false; - - update_panels(); - doupdate(); - - /* had problems finding reliable documentation here, but it seems like - manually moving the cursor requires a refresh() -- doupdate() is not - good enough. further, we allow windows to repaint themselves at will, - which may change the cursor position. after each draw cycle, move the - cursor back to the focused input. */ - if (input) { - Window* inputWindow = dynamic_cast(input); - if (inputWindow) { - wmove(inputWindow->GetContent(), 0, input->Length()); - wrefresh(inputWindow->GetContent()); - } - } - } -} - -void Window::Invalidate() { - wclear(stdscr); - drawPending = true; -} - -Window::Window(IWindow *parent) { - this->frame = this->content = 0; - this->framePanel = this->contentPanel = 0; - this->parent = parent; - this->height = 0; - this->width = 0; - this->x = 0; - this->y = 0; - this->contentColor = -1; - this->frameColor = -1; - this->drawFrame = true; - this->isVisible = false; - this->focusOrder = -1; - this->id = NEXT_ID++; -} - -Window::~Window() { - MessageQueue::Instance().Remove(this); - this->Destroy(); -} - -int Window::GetId() const { - return this->id; -} - -void Window::ProcessMessage(IMessage &message) { - -} - -bool Window::IsAcceptingMessages() { - return true; -} - -bool Window::IsVisible() { - return this->isVisible; -} - -void Window::BringToTop() { - if (this->framePanel) { - top_panel(this->framePanel); - - if (this->contentPanel != this->framePanel) { - top_panel(this->contentPanel); - } - } -} - -void Window::SendToBottom() { - if (this->framePanel) { - bottom_panel(this->contentPanel); - - if (this->contentPanel != this->framePanel) { - bottom_panel(this->framePanel); - } - } -} - -void Window::PostMessage(int messageType, int64 user1, int64 user2, int64 delay) { - MessageQueue::Instance().Post( - Message::Create( - this, - messageType, - user1, - user2), - delay); -} - -void Window::RemoveMessage(int messageType) { - MessageQueue::Instance().Remove(this, messageType); -} - -void Window::SetParent(IWindow* parent) { - if (this->parent != parent) { - IWindowGroup* group = dynamic_cast(this->parent); - - if (group) { - group->RemoveWindow(this->Window::shared_from_this()); - } - - this->parent = parent; - - if (this->frame) { - this->Hide(); - this->Show(); - } - } -} - -void Window::MoveAndResize(int x, int y, int width, int height) { - bool sizeChanged = this->width != width || this->height != height; - bool positionChanged = this->x != x || this->y != y; - - if (sizeChanged || positionChanged) { - this->width = width; - this->height = height; - this->x = x; - this->y = y; - - if (this->frame) { - this->Recreate(); - } - - if (sizeChanged) { - this->OnSizeChanged(); - } - - if (positionChanged) { - this->OnPositionChanged(); - } - } -} - -void Window::SetSize(int width, int height) { - if (this->width != width || this->height != height) { - this->width = width; - this->height = height; - - if (this->frame) { - this->Recreate(); - } - - this->OnSizeChanged(); - } -} - -void Window::SetPosition(int x, int y) { - if (this->x != x || this->y != y) { - this->x = x; - this->y = y; - - if (this->frame) { - this->Recreate(); - } - - this->OnPositionChanged(); - } -} - -void Window::OnPositionChanged() { - /* for subclass use */ -} - -void Window::OnSizeChanged() { - /* for subclass use */ -} - -void Window::OnVisibilityChanged(bool visible) { - /* for subclass use */ -} - -int Window::GetWidth() const { - return this->width; -} - -int Window::GetHeight() const { - return this->height; -} - -int Window::GetContentHeight() const { - if (!this->drawFrame) { - return this->height; - } - - return this->height ? this->height - 2 : 0; -} - -int Window::GetContentWidth() const { - if (!this->drawFrame) { - return this->width; - } - - return this->width ? this->width - 2 : 0; -} - -int Window::GetX() const { - return this->x; -} - -int Window::GetY() const { - return this->y; -} - -void Window::SetContentColor(int64 color) { - this->contentColor = color; - - if (this->contentColor != -1 && this->content) { - wbkgd(this->frame, COLOR_PAIR(this->frameColor)); - - if (this->content != this->frame) { - wbkgd(this->content, COLOR_PAIR(this->contentColor)); - } - - this->Repaint(); - } -} - -void Window::SetFrameColor(int64 color) { - this->frameColor = color; - - if (this->drawFrame && this->frameColor != -1 && this->frame) { - wbkgd(this->frame, COLOR_PAIR(this->frameColor)); - - if (this->content != this->frame) { - wbkgd(this->content, COLOR_PAIR(this->contentColor)); - } - - this->Repaint(); - } -} - -WINDOW* Window::GetContent() const { - return this->content; -} - -WINDOW* Window::GetFrame() const { - return this->frame; -} - -int Window::GetFocusOrder() { - return this->focusOrder; -} - -void Window::SetFocusOrder(int order) { - this->focusOrder = order; -} - -void Window::Show() { - if (this->framePanel) { - if (!this->isVisible) { - show_panel(this->framePanel); - - if (this->framePanel != this->contentPanel) { - show_panel(this->contentPanel); - } - - this->isVisible = true; - drawPending = true; - - this->OnVisibilityChanged(true); - } - } - else { - this->Create(); - } -} - -void Window::Recreate() { - this->Destroy(); - this->Create(); -} - -void Window::Create() { - /* if we have a parent, place the new window relative to the parent. */ - - this->frame = newwin( - this->height, - this->width, - this->y, - this->x); - - //this->frame = (this->parent == NULL) - // ? newwin( - // this->height, - // this->width, - // this->y, - // this->x) - // : newwin( - // this->height, - // this->width, - // this->parent->GetY() + this->y, - // this->parent->GetX() + this->x); - - this->framePanel = new_panel(this->frame); - - /* if we were asked not to draw a frame, we'll set the frame equal to - the content view, and use the content views colors*/ - - if (!this->drawFrame) { - this->content = this->frame; - - if (this->contentColor != -1) { - wbkgd(this->frame, COLOR_PAIR(this->contentColor)); - } - } - - /* otherwise we'll draw a box around the frame, and create a content - sub-window inside */ - - else { - box(this->frame, 0, 0); - - this->content = newwin( - this->height - 2, - this->width - 2, - this->GetY() + 1, - this->GetX() + 1); - - this->contentPanel = new_panel(this->content); - - if (this->frameColor != -1) { - wbkgd(this->frame, COLOR_PAIR(this->frameColor)); - } - - if (this->contentColor != -1) { - wbkgd(this->content, COLOR_PAIR(this->contentColor)); - } - } - - this->Show(); -} - -void Window::Hide() { - if (this->frame) { - if (this->isVisible) { - hide_panel(this->framePanel); - - if (this->content != this->frame) { - hide_panel(this->contentPanel); - } - - this->isVisible = false; - this->OnVisibilityChanged(false); - } - } -} - -void Window::Destroy() { - this->Hide(); - - if (this->frame) { - del_panel(this->framePanel); - delwin(this->frame); - - if (this->content != this->frame) { - del_panel(this->contentPanel); - delwin(this->content); - } - } - - this->framePanel = this->contentPanel = 0; - this->content = this->frame = 0; -} - -void Window::SetFrameVisible(bool enabled) { - if (enabled != this->drawFrame) { - this->drawFrame = enabled; - - if (this->frame || this->content) { - this->Destroy(); - this->Show(); - } - } -} - -bool Window::IsFrameVisible() { - return this->drawFrame; -} - -IWindow* Window::GetParent() const { - return this->parent; -} - -void Window::Clear() { - werase(this->content); -} - -void Window::Repaint() { - if (this->isVisible) { - if (this->frame) { - drawPending = true; - } - } -} - -void Window::Focus() { - -} - -void Window::Blur() { - -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "Window.h" +#include "IWindowGroup.h" +#include "IInput.h" +#include "Message.h" +#include "MessageQueue.h" + +using namespace cursespp; + +static int NEXT_ID = 0; +static bool drawPending = false; + +void Window::WriteToScreen(IInput* input) { + if (drawPending) { + drawPending = false; + + update_panels(); + doupdate(); + + /* had problems finding reliable documentation here, but it seems like + manually moving the cursor requires a refresh() -- doupdate() is not + good enough. further, we allow windows to repaint themselves at will, + which may change the cursor position. after each draw cycle, move the + cursor back to the focused input. */ + if (input) { + Window* inputWindow = dynamic_cast(input); + if (inputWindow) { + wmove(inputWindow->GetContent(), 0, input->Length()); + wrefresh(inputWindow->GetContent()); + } + } + } +} + +void Window::Invalidate() { + wclear(stdscr); + drawPending = true; +} + +Window::Window(IWindow *parent) { + this->frame = this->content = 0; + this->framePanel = this->contentPanel = 0; + this->parent = parent; + this->height = 0; + this->width = 0; + this->x = 0; + this->y = 0; + this->contentColor = -1; + this->frameColor = -1; + this->drawFrame = true; + this->isVisible = false; + this->focusOrder = -1; + this->id = NEXT_ID++; +} + +Window::~Window() { + MessageQueue::Instance().Remove(this); + this->Destroy(); +} + +int Window::GetId() const { + return this->id; +} + +void Window::ProcessMessage(IMessage &message) { + +} + +bool Window::IsAcceptingMessages() { + return true; +} + +bool Window::IsVisible() { + return this->isVisible; +} + +void Window::BringToTop() { + if (this->framePanel) { + top_panel(this->framePanel); + + if (this->contentPanel != this->framePanel) { + top_panel(this->contentPanel); + } + } +} + +void Window::SendToBottom() { + if (this->framePanel) { + bottom_panel(this->contentPanel); + + if (this->contentPanel != this->framePanel) { + bottom_panel(this->framePanel); + } + } +} + +void Window::PostMessage(int messageType, int64 user1, int64 user2, int64 delay) { + MessageQueue::Instance().Post( + Message::Create( + this, + messageType, + user1, + user2), + delay); +} + +void Window::RemoveMessage(int messageType) { + MessageQueue::Instance().Remove(this, messageType); +} + +void Window::SetParent(IWindow* parent) { + if (this->parent != parent) { + IWindowGroup* group = dynamic_cast(this->parent); + + if (group) { + group->RemoveWindow(this->Window::shared_from_this()); + } + + this->parent = parent; + + if (this->frame) { + this->Hide(); + this->Show(); + } + } +} + +void Window::MoveAndResize(int x, int y, int width, int height) { + bool sizeChanged = this->width != width || this->height != height; + bool positionChanged = this->x != x || this->y != y; + + if (sizeChanged || positionChanged) { + this->width = width; + this->height = height; + this->x = x; + this->y = y; + + if (this->frame) { + this->Recreate(); + } + + if (sizeChanged) { + this->OnSizeChanged(); + } + + if (positionChanged) { + this->OnPositionChanged(); + } + } +} + +void Window::SetSize(int width, int height) { + if (this->width != width || this->height != height) { + this->width = width; + this->height = height; + + if (this->frame) { + this->Recreate(); + } + + this->OnSizeChanged(); + } +} + +void Window::SetPosition(int x, int y) { + if (this->x != x || this->y != y) { + this->x = x; + this->y = y; + + if (this->frame) { + this->Recreate(); + } + + this->OnPositionChanged(); + } +} + +void Window::OnPositionChanged() { + /* for subclass use */ +} + +void Window::OnSizeChanged() { + /* for subclass use */ +} + +void Window::OnVisibilityChanged(bool visible) { + /* for subclass use */ +} + +int Window::GetWidth() const { + return this->width; +} + +int Window::GetHeight() const { + return this->height; +} + +int Window::GetContentHeight() const { + if (!this->drawFrame) { + return this->height; + } + + return this->height ? this->height - 2 : 0; +} + +int Window::GetContentWidth() const { + if (!this->drawFrame) { + return this->width; + } + + return this->width ? this->width - 2 : 0; +} + +int Window::GetX() const { + return this->x; +} + +int Window::GetY() const { + return this->y; +} + +void Window::SetContentColor(int64 color) { + this->contentColor = color; + + if (this->contentColor != -1 && this->content) { + wbkgd(this->frame, COLOR_PAIR(this->frameColor)); + + if (this->content != this->frame) { + wbkgd(this->content, COLOR_PAIR(this->contentColor)); + } + + this->Repaint(); + } +} + +void Window::SetFrameColor(int64 color) { + this->frameColor = color; + + if (this->drawFrame && this->frameColor != -1 && this->frame) { + wbkgd(this->frame, COLOR_PAIR(this->frameColor)); + + if (this->content != this->frame) { + wbkgd(this->content, COLOR_PAIR(this->contentColor)); + } + + this->Repaint(); + } +} + +WINDOW* Window::GetContent() const { + return this->content; +} + +WINDOW* Window::GetFrame() const { + return this->frame; +} + +int Window::GetFocusOrder() { + return this->focusOrder; +} + +void Window::SetFocusOrder(int order) { + this->focusOrder = order; +} + +void Window::Show() { + if (this->framePanel) { + if (!this->isVisible) { + show_panel(this->framePanel); + + if (this->framePanel != this->contentPanel) { + show_panel(this->contentPanel); + } + + this->isVisible = true; + drawPending = true; + + this->OnVisibilityChanged(true); + } + } + else { + this->Create(); + } +} + +void Window::Recreate() { + this->Destroy(); + this->Create(); +} + +void Window::Create() { + /* if we have a parent, place the new window relative to the parent. */ + + this->frame = newwin( + this->height, + this->width, + this->y, + this->x); + + //this->frame = (this->parent == NULL) + // ? newwin( + // this->height, + // this->width, + // this->y, + // this->x) + // : newwin( + // this->height, + // this->width, + // this->parent->GetY() + this->y, + // this->parent->GetX() + this->x); + + this->framePanel = new_panel(this->frame); + + /* if we were asked not to draw a frame, we'll set the frame equal to + the content view, and use the content views colors*/ + + if (!this->drawFrame) { + this->content = this->frame; + + if (this->contentColor != -1) { + wbkgd(this->frame, COLOR_PAIR(this->contentColor)); + } + } + + /* otherwise we'll draw a box around the frame, and create a content + sub-window inside */ + + else { + box(this->frame, 0, 0); + + this->content = newwin( + this->height - 2, + this->width - 2, + this->GetY() + 1, + this->GetX() + 1); + + this->contentPanel = new_panel(this->content); + + if (this->frameColor != -1) { + wbkgd(this->frame, COLOR_PAIR(this->frameColor)); + } + + if (this->contentColor != -1) { + wbkgd(this->content, COLOR_PAIR(this->contentColor)); + } + } + + this->Show(); +} + +void Window::Hide() { + if (this->frame) { + if (this->isVisible) { + hide_panel(this->framePanel); + + if (this->content != this->frame) { + hide_panel(this->contentPanel); + } + + this->isVisible = false; + this->OnVisibilityChanged(false); + } + } +} + +void Window::Destroy() { + this->Hide(); + + if (this->frame) { + del_panel(this->framePanel); + delwin(this->frame); + + if (this->content != this->frame) { + del_panel(this->contentPanel); + delwin(this->content); + } + } + + this->framePanel = this->contentPanel = 0; + this->content = this->frame = 0; +} + +void Window::SetFrameVisible(bool enabled) { + if (enabled != this->drawFrame) { + this->drawFrame = enabled; + + if (this->frame || this->content) { + this->Destroy(); + this->Show(); + } + } +} + +bool Window::IsFrameVisible() { + return this->drawFrame; +} + +IWindow* Window::GetParent() const { + return this->parent; +} + +void Window::Clear() { + werase(this->content); +} + +void Window::Repaint() { + if (this->isVisible) { + if (this->frame) { + drawPending = true; + } + } +} + +void Window::Focus() { + +} + +void Window::Blur() { + +} diff --git a/src/musikbox/cursespp/WindowLayout.cpp b/src/musikbox/cursespp/WindowLayout.cpp index df0d6309b..40a6ea0a2 100755 --- a/src/musikbox/cursespp/WindowLayout.cpp +++ b/src/musikbox/cursespp/WindowLayout.cpp @@ -30,77 +30,77 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -////////////////////////////////////////////////////////////////////////////// - -#include -#include "WindowLayout.h" -#include "LayoutStack.h" - -using namespace cursespp; - -WindowLayout::WindowLayout(IWindowPtr window) { - this->window = window; - this->stack = 0; -} - -WindowLayout::~WindowLayout() { -} - -bool WindowLayout::AddWindow(IWindowPtr window) { - throw std::runtime_error("AddWindow() not supported in LayoutStack. Use Push()"); -} - -bool WindowLayout::RemoveWindow(IWindowPtr window) { - throw std::runtime_error("RemoveWindow() not supported in LayoutStack. Use Push()"); -} - -size_t WindowLayout::GetWindowCount() { - throw std::runtime_error("GetWindowCount() not supported in LayoutStack."); -} - -IWindowPtr WindowLayout::GetWindowAt(size_t position) { - throw std::runtime_error("GetWindowAt() not supported in LayoutStack."); -} - -void WindowLayout::Show() { - this->window->Show(); -} - -void WindowLayout::Hide() { - this->window->Hide(); -} - -bool WindowLayout::KeyPress(int64 ch) { - if (ch == 27 && this->GetLayoutStack()) { - this->GetLayoutStack()->Pop(shared_from_this()); - } - return false; -} - -IWindowPtr WindowLayout::FocusNext() { - return this->window; -} - -IWindowPtr WindowLayout::FocusPrev() { - return this->window; -} - -IWindowPtr WindowLayout::GetFocus() { - return this->window; -} - -void WindowLayout::BringToTop() { - this->window->BringToTop(); -} - -void WindowLayout::SendToBottom() { - this->window->SendToBottom(); -} - -ILayoutStack* WindowLayout::GetLayoutStack() { - return this->stack; -} - -void WindowLayout::SetLayoutStack(ILayoutStack* stack) { - this->stack = stack; -} +////////////////////////////////////////////////////////////////////////////// + +#include +#include "WindowLayout.h" +#include "LayoutStack.h" + +using namespace cursespp; + +WindowLayout::WindowLayout(IWindowPtr window) { + this->window = window; + this->stack = 0; +} + +WindowLayout::~WindowLayout() { +} + +bool WindowLayout::AddWindow(IWindowPtr window) { + throw std::runtime_error("AddWindow() not supported in LayoutStack. Use Push()"); +} + +bool WindowLayout::RemoveWindow(IWindowPtr window) { + throw std::runtime_error("RemoveWindow() not supported in LayoutStack. Use Push()"); +} + +size_t WindowLayout::GetWindowCount() { + throw std::runtime_error("GetWindowCount() not supported in LayoutStack."); +} + +IWindowPtr WindowLayout::GetWindowAt(size_t position) { + throw std::runtime_error("GetWindowAt() not supported in LayoutStack."); +} + +void WindowLayout::Show() { + this->window->Show(); +} + +void WindowLayout::Hide() { + this->window->Hide(); +} + +bool WindowLayout::KeyPress(int64 ch) { + if (ch == 27 && this->GetLayoutStack()) { + this->GetLayoutStack()->Pop(shared_from_this()); + } + return false; +} + +IWindowPtr WindowLayout::FocusNext() { + return this->window; +} + +IWindowPtr WindowLayout::FocusPrev() { + return this->window; +} + +IWindowPtr WindowLayout::GetFocus() { + return this->window; +} + +void WindowLayout::BringToTop() { + this->window->BringToTop(); +} + +void WindowLayout::SendToBottom() { + this->window->SendToBottom(); +} + +ILayoutStack* WindowLayout::GetLayoutStack() { + return this->stack; +} + +void WindowLayout::SetLayoutStack(ILayoutStack* stack) { + this->stack = stack; +} diff --git a/src/musikbox/stdafx.cpp b/src/musikbox/stdafx.cpp index 81baa09cb..e11ae5fce 100644 --- a/src/musikbox/stdafx.cpp +++ b/src/musikbox/stdafx.cpp @@ -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"