mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-31 03:27:40 +01:00
Add Echoprint library and a new fingerprinting classs that uses it.
This commit is contained in:
parent
2ef9ab6f6e
commit
1df5db5ee3
3
3rdparty/echoprint-codegen/.gitignore
vendored
Normal file
3
3rdparty/echoprint-codegen/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.o
|
||||
*.dylib
|
||||
*.so
|
8
3rdparty/echoprint-codegen/AUTHORS
vendored
Normal file
8
3rdparty/echoprint-codegen/AUTHORS
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
echoprint-codegen was written by
|
||||
|
||||
Dan Ellis <dpwe@ee.columbia.edu>
|
||||
Brian Whitman <brian@echonest.com>
|
||||
Alastair Porter <alastair@porter.net.nz>
|
||||
|
||||
and is Copyright 2011 The Echo Nest Corporation.
|
||||
|
18
3rdparty/echoprint-codegen/CMakeLists.txt
vendored
Normal file
18
3rdparty/echoprint-codegen/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
set(ECHOPRINT-SOURCES
|
||||
src/AudioBufferInput.cxx
|
||||
src/AudioStreamInput.cxx
|
||||
src/Base64.cxx
|
||||
src/Codegen.cxx
|
||||
src/Fingerprint.cxx
|
||||
src/MatrixUtility.cxx
|
||||
src/Metadata.cxx
|
||||
src/SubbandAnalysis.cxx
|
||||
src/Whitening.cxx
|
||||
)
|
||||
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
add_library(echoprint STATIC ${ECHOPRINT-SOURCES})
|
||||
|
||||
set_target_properties(echoprint PROPERTIES
|
||||
COMPILE_DEFINITIONS "BOOST_UBLAS_NDEBUG;NDEBUG"
|
||||
COMPILE_FLAGS "-O3 -fPIC")
|
44
3rdparty/echoprint-codegen/LICENSE
vendored
Normal file
44
3rdparty/echoprint-codegen/LICENSE
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
echoprint-codegen is open source software licensed under the "MIT License"
|
||||
More information about the MIT License: http://en.wikipedia.org/wiki/MIT_License
|
||||
|
||||
Copyright (c) 2011 The Echo Nest Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
libcodegen makes use of the following pieces of software:
|
||||
|
||||
- Murmurhash by Austin Appleby (Public Domain / MIT)
|
||||
http://sites.google.com/site/murmurhash/
|
||||
|
||||
- Boost (Boost Software License)
|
||||
http://www.boost.org/users/license.html
|
||||
|
||||
- Base64.cpp and Base64.h, see source files for license
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
|
||||
|
||||
codegen (the example binary that the makefile also builds) makes use of libcodegen and:
|
||||
|
||||
- Taglib (LGPL)
|
||||
http://developer.kde.org/~wheeler/taglib.html
|
||||
|
||||
- ffmpeg (via system shell, you must install ffmpeg on your own)
|
||||
http://www.ffmpeg.org/legal.html
|
||||
|
83
3rdparty/echoprint-codegen/README.md
vendored
Normal file
83
3rdparty/echoprint-codegen/README.md
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
# Codegen for Echoprint
|
||||
|
||||
Echoprint is an open source music fingerprint and resolving framework powered by the [The Echo Nest](http://the.echonest.com/ "The Echo Nest"). The [code generator](http://github.com/echonest/echoprint-codegen "echoprint-codegen") (library to convert PCM samples from a microphone or file into Echoprint codes) is open source (MIT licensed) and free for any use. The [server component](http://github.com/echonest/echoprint-server "echoprint-server") that stores and resolves queries is open source (Apache 2 licensed) and free for any use. The [data for resolving to millions of songs](http://echoprint.me/data "Echoprint Data") is free for any use provided any changes or additions are merged back to the community.
|
||||
|
||||
[Read more about Echoprint here](http://echoprint.me)
|
||||
|
||||
There are two modes of operation of the Echoprint codegen:
|
||||
|
||||
1. the codegen library (libcodegen) is meant to be linked into code that passes it a buffer of PCM data and will output a code string.
|
||||
|
||||
2. the codegen binary runs standalone, accepts filenames as inputs and runs in a multithreaded worker mode.
|
||||
|
||||
## Requirements for libcodegen
|
||||
|
||||
* Boost >= 1.35
|
||||
|
||||
## Additional requirements for the codegen binary
|
||||
|
||||
* [TagLib](http://developer.kde.org/~wheeler/taglib.html "TagLib")
|
||||
* ffmpeg - this is called via shell and is not linked into codegen
|
||||
|
||||
## Notes about libcodegen:
|
||||
|
||||
Code generation takes a buffer of floating point PCM data sampled at 11025 Hz and mono.
|
||||
|
||||
Codegen * pCodegen = new Codegen(const float* pcm, uint numSamples, int start_offset);
|
||||
|
||||
pcm: a buffer of floats, mono, 11025 Hz
|
||||
numSamples: the number of samples
|
||||
start_offset: creates a hint to the server on where the sample is taken from in the original file if known
|
||||
|
||||
string code = pCodegen->getCodeString();
|
||||
|
||||
The code string is just a base64 encoding of a zlib compression of the original code string, which is a hex encoded series of ASCII numbers. See API/fp.py in echoprint-server for decoding help.
|
||||
|
||||
You only need to query for 20 seconds of audio to get a result.
|
||||
|
||||
## Notes about the codegen binary
|
||||
|
||||
The makefile builds an example code generator that uses libcodegen, called "codegen." This code generator has more features -- it will output ID3 tag information and uses ffmpeg to decode any type of file. If you don't need to compile libcodegen into your app you can rely on this. Note that you need to have ffmpeg installed and accessible on your path for this to work.
|
||||
|
||||
./echoprint-codegen billie_jean.mp3 10 30
|
||||
|
||||
Will take 30 seconds of audio from 10 seconds into the file and output JSON suitable for querying:
|
||||
|
||||
{"metadata":{"artist":"Michael jackson", "release":"800 chansons des annes 80", "title":"Billie jean", "genre":"", "bitrate":192, "sample_rate":44100, "seconds":294, "filename":"billie_jean.mp3", "samples_decoded":220598, "given_duration":30, "start_offset":10, "version":4.00}, "code_count":846, "code":"JxVlIuNwzAMQ1fxCDL133+xo1rnGqNAEcWy/ERa2aKeZmW...
|
||||
|
||||
You can POST this JSON directly to the Echo Nest's [song/identify](http://developer.echonest.com/docs/v4/song.html#identify "song/identify") (who has an Echoprint server booted), for example:
|
||||
|
||||
curl -F "query=@post_string" http://developer.echonest.com/api/v4/song/identify?api_key=YOUR_KEY
|
||||
{"fp_lookup_time_ms": 21, "results": [{"songID": "SOAFVGQ1280ED4E371", "match_type": "fp", "title": "Billie Jean", "artist": "Michael Jackson", "artistID": "ARXPPEY1187FB51DF4", "score": 63, "release": "Thriller"}]
|
||||
(you can also use GET, see the API description)
|
||||
|
||||
Or you can host your own [Echoprint server](http://github.com/echonest/echoprint-server "echoprint-server") and ingest or query to that.
|
||||
|
||||
Codegen also runs in a multithreaded mode for bulk resolving:
|
||||
|
||||
./echoprint-codegen -s 10 30 < file_list
|
||||
|
||||
Will compute codes for every file in file_list for 30 seconds starting at 10 seconds. (It tries to be smart about the number of threads to use.) It will output a JSON list. Note that song/identify can accept lists in the JSON, which will be faster than sending each code one at a time. The "tag" parameter is added to each code dictionary to match the resolving material.
|
||||
|
||||
## Statistics
|
||||
|
||||
### Speed
|
||||
|
||||
Codegen scans audio at roughly 250x real time per processor after decoding and resampling to 11025 Hz. This means a full song can be scanned in less than 0.5s on an average computer, and an amount of audio suitable for querying (30s) can be scanned in less than 0.04s.
|
||||
|
||||
Decoding from MP3 will be the bottleneck for most implementations. Decoders like mpg123 or ffmpeg can decode 30s mp3 audio to 11025 PCM in under 0.10s.
|
||||
|
||||
clump:echoprint-codegen bwhitman$ time mpg123 -q -s -4 -n 1200 song.mp3 > /dev/null
|
||||
real 0m0.079s
|
||||
user 0m0.067s
|
||||
sys 0m0.007s
|
||||
|
||||
### Accuracy
|
||||
|
||||
Look at http://echoprint.me for information on the accuracy of the echoprint system.
|
||||
|
||||
## FAQ
|
||||
|
||||
Q: I get "Couldn't decode any samples with: ffmpeg" when running codegen
|
||||
|
||||
A: When running the example code generator (echoprint-codegen) make sure ffmpeg is accessible to your path. Try running ffmpeg filename.mp3 on the file you are testing the code generator with. If it doesn't work, codegen won't work.
|
28
3rdparty/echoprint-codegen/src/AudioBufferInput.cxx
vendored
Normal file
28
3rdparty/echoprint-codegen/src/AudioBufferInput.cxx
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
#include "AudioBufferInput.h"
|
||||
|
||||
AudioBufferInput::AudioBufferInput() { }
|
||||
|
||||
void AudioBufferInput::SetBuffer(const float* pBuffer, uint numSamples) {
|
||||
_NumberSamples = numSamples;
|
||||
_pSamples = new float[_NumberSamples]; // base-class destructor will clean this up.
|
||||
memcpy(_pSamples, pBuffer, numSamples*sizeof(float));
|
||||
}
|
||||
|
||||
void AudioBufferInput::SaveBuffer(const char*filename) {
|
||||
FILE *out = fopen(filename,"wb");
|
||||
fwrite(&_NumberSamples, sizeof(int), 1, out);
|
||||
uint mn = 1;
|
||||
fwrite(&mn, sizeof(int), 1, out);
|
||||
fwrite(_pSamples, 4, _NumberSamples, out);
|
||||
fclose(out);
|
||||
}
|
30
3rdparty/echoprint-codegen/src/AudioBufferInput.h
vendored
Normal file
30
3rdparty/echoprint-codegen/src/AudioBufferInput.h
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include "Common.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#ifndef AUDIOBUFFERINPUT_H
|
||||
#define AUDIOBUFFERINPUT_H
|
||||
#include "Params.h"
|
||||
#include "AudioStreamInput.h"
|
||||
|
||||
|
||||
class AudioBufferInput : public AudioStreamInput {
|
||||
public:
|
||||
AudioBufferInput();
|
||||
std::string GetName() {return "direct buffer";}
|
||||
void SaveBuffer(const char*filename);
|
||||
void SetBuffer(const float* pBuffer, uint numSamples);
|
||||
protected:
|
||||
std::string GetCommandLine(const char*){return "";}
|
||||
private:
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
132
3rdparty/echoprint-codegen/src/AudioStreamInput.cxx
vendored
Normal file
132
3rdparty/echoprint-codegen/src/AudioStreamInput.cxx
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#define POPEN_MODE "r"
|
||||
#else
|
||||
#include "win_unistd.h"
|
||||
#include <winsock.h>
|
||||
#define POPEN_MODE "rb"
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
#include "AudioStreamInput.h"
|
||||
#include "Common.h"
|
||||
#include "Params.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace FFMPEG {
|
||||
// Do we think FFmpeg will read this as an audio file?
|
||||
bool IsAudioFile(const char* pFileName) {
|
||||
static const char* supportedExtensions[] = {".mp3", ".m4a", ".mp4", ".aif", ".aiff", ".flac", ".au", ".wav", ".aac", ".flv"};
|
||||
// Not an exhaustive list. ogg and rm could be added if tested.
|
||||
for (uint i = 0; i < NELEM(supportedExtensions); i++) {
|
||||
if (File::ends_with(pFileName, supportedExtensions[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioStreamInput::IsSupported(const char *path) {
|
||||
return true; // Take a crack at anything, by default. The worst thing that will happen is that we fail.
|
||||
}
|
||||
|
||||
AudioStreamInput::AudioStreamInput() : _pSamples(NULL), _NumberSamples(0), _Offset_s(0), _Seconds(0) {}
|
||||
|
||||
AudioStreamInput::~AudioStreamInput() {
|
||||
if (_pSamples != NULL)
|
||||
delete [] _pSamples, _pSamples = NULL;
|
||||
}
|
||||
|
||||
|
||||
bool AudioStreamInput::ProcessFile(const char* filename, int offset_s/*=0*/, int seconds/*=0*/) {
|
||||
if (!File::Exists(filename) || !IsSupported(filename))
|
||||
return false;
|
||||
|
||||
_Offset_s = offset_s;
|
||||
_Seconds = seconds;
|
||||
std::string message = GetCommandLine(filename);
|
||||
|
||||
FILE* fp = popen(message.c_str(), POPEN_MODE);
|
||||
bool ok = (fp != NULL);
|
||||
if (ok)
|
||||
{
|
||||
bool did_work = ProcessFilePointer(fp);
|
||||
bool succeeded = !pclose(fp);
|
||||
ok = did_work && succeeded;
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "AudioStreamInput::ProcessFile can't open %s\n", filename);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
// reads raw signed 16-bit shorts from a file
|
||||
bool AudioStreamInput::ProcessRawFile(const char* rawFilename) {
|
||||
FILE* fp = fopen(rawFilename, "r"); // TODO: Windows
|
||||
bool ok = (fp != NULL);
|
||||
if (ok)
|
||||
{
|
||||
ok = ProcessFilePointer(fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
// reads raw signed 16-bit shorts from stdin, for example:
|
||||
// ffmpeg -i fille.mp3 -f s16le -ac 1 -ar 11025 - | TestAudioSTreamInput
|
||||
bool AudioStreamInput::ProcessStandardInput(void) {
|
||||
// TODO - Windows will explodey at not setting O_BINARY on stdin.
|
||||
return ProcessFilePointer(stdin);
|
||||
}
|
||||
|
||||
bool AudioStreamInput::ProcessFilePointer(FILE* pFile) {
|
||||
std::vector<short*> vChunks;
|
||||
uint nSamplesPerChunk = (uint) Params::AudioStreamInput::SamplingRate * Params::AudioStreamInput::SecondsPerChunk;
|
||||
uint samplesRead = 0;
|
||||
do {
|
||||
short* pChunk = new short[nSamplesPerChunk];
|
||||
samplesRead = fread(pChunk, sizeof (short), nSamplesPerChunk, pFile);
|
||||
_NumberSamples += samplesRead;
|
||||
vChunks.push_back(pChunk);
|
||||
} while (samplesRead > 0);
|
||||
|
||||
// Convert from shorts to 16-bit floats and copy into sample buffer.
|
||||
uint sampleCounter = 0;
|
||||
_pSamples = new float[_NumberSamples];
|
||||
uint samplesLeft = _NumberSamples;
|
||||
for (uint i = 0; i < vChunks.size(); i++)
|
||||
{
|
||||
short* pChunk = vChunks[i];
|
||||
uint numSamples = samplesLeft < nSamplesPerChunk ? samplesLeft : nSamplesPerChunk;
|
||||
|
||||
for (uint j = 0; j < numSamples; j++)
|
||||
_pSamples[sampleCounter++] = (float) pChunk[j] / 32768.0f;
|
||||
|
||||
samplesLeft -= numSamples;
|
||||
delete [] pChunk, vChunks[i] = NULL;
|
||||
}
|
||||
assert(samplesLeft == 0);
|
||||
|
||||
int error = ferror(pFile);
|
||||
bool success = error == 0;
|
||||
|
||||
if (!success)
|
||||
perror("ProcessFilePointer error");
|
||||
return success;
|
||||
}
|
||||
|
||||
|
98
3rdparty/echoprint-codegen/src/AudioStreamInput.h
vendored
Normal file
98
3rdparty/echoprint-codegen/src/AudioStreamInput.h
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#ifndef AUDIOSTREAMINPUT_H
|
||||
#define AUDIOSTREAMINPUT_H
|
||||
#include "Common.h"
|
||||
#include "Params.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
#include "File.h"
|
||||
#ifdef _WIN32
|
||||
#define and &&
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
class AudioStreamInput {
|
||||
public:
|
||||
AudioStreamInput();
|
||||
virtual ~AudioStreamInput();
|
||||
virtual bool ProcessFile(const char* filename, int offset_s=0, int seconds=0);
|
||||
virtual std::string GetName() = 0;
|
||||
bool ProcessRawFile(const char* rawFilename);
|
||||
bool ProcessStandardInput(void);
|
||||
bool ProcessFilePointer(FILE* pFile);
|
||||
int getNumSamples() const {return _NumberSamples;}
|
||||
const float* getSamples() {return _pSamples;}
|
||||
double getDuration() { return (double)getNumSamples() / Params::AudioStreamInput::SamplingRate; }
|
||||
virtual bool IsSupported(const char* pFileName); //Everything ffmpeg can do, by default
|
||||
int GetOffset() const { return _Offset_s;}
|
||||
int GetSeconds() const { return _Seconds;}
|
||||
protected:
|
||||
|
||||
virtual std::string GetCommandLine(const char* filename) = 0;
|
||||
static bool ends_with(const char *s, const char *ends_with);
|
||||
float* _pSamples;
|
||||
uint _NumberSamples;
|
||||
int _Offset_s;
|
||||
int _Seconds;
|
||||
|
||||
};
|
||||
|
||||
class StdinStreamInput : public AudioStreamInput {
|
||||
public:
|
||||
std::string GetName(){return "stdin";};
|
||||
protected:
|
||||
bool IsSupported(const char* pFileName){ return (std::string("stdin") == pFileName);};
|
||||
bool ProcessFile(const char* filename, int offset_s=0, int seconds=0){ return ProcessStandardInput();}
|
||||
virtual std::string GetCommandLine(const char* filename){return "";} // hack
|
||||
};
|
||||
|
||||
class FfmpegStreamInput : public AudioStreamInput {
|
||||
public:
|
||||
std::string GetName(){return "ffmpeg";};
|
||||
protected:
|
||||
std::string GetCommandLine(const char* filename) {
|
||||
// TODO: Windows
|
||||
char message[4096] = {0};
|
||||
if (_Offset_s == 0 and _Seconds == 0)
|
||||
snprintf(message, NELEM(message), "ffmpeg -i \"%s\" -ac %d -ar %d -f s16le - 2>/dev/null",
|
||||
filename, Params::AudioStreamInput::Channels, (uint) Params::AudioStreamInput::SamplingRate);
|
||||
else
|
||||
snprintf(message, NELEM(message), "ffmpeg -i \"%s\" -ac %d -ar %d -f s16le -t %d -ss %d - 2>/dev/null",
|
||||
filename, Params::AudioStreamInput::Channels, (uint) Params::AudioStreamInput::SamplingRate, _Seconds, _Offset_s);
|
||||
|
||||
printf("%s\n", message);
|
||||
return std::string(message);
|
||||
}
|
||||
};
|
||||
|
||||
namespace FFMPEG {
|
||||
bool IsAudioFile(const char* pFileName);
|
||||
};
|
||||
|
||||
class Mpg123StreamInput : public AudioStreamInput {
|
||||
public:
|
||||
std::string GetName(){return "mpg123";};
|
||||
protected:
|
||||
#define FRAMES_PER_SECOND 38.2813f
|
||||
bool IsSupported(const char* pFileName){ return File::ends_with(pFileName, ".mp3");};
|
||||
std::string GetCommandLine(const char* filename) {
|
||||
char message[4096] = {0};
|
||||
if (_Offset_s == 0 and _Seconds == 0)
|
||||
snprintf(message, NELEM(message), "mpg123 --quiet --singlemix --stdout --rate %d \"%s\"",
|
||||
(uint) Params::AudioStreamInput::SamplingRate, filename);
|
||||
else
|
||||
snprintf(message, NELEM(message), "mpg123 --quiet --singlemix --stdout --rate %d --skip %d --frames %d \"%s\"",
|
||||
(uint) Params::AudioStreamInput::SamplingRate, (uint)(_Offset_s * FRAMES_PER_SECOND) /* unprecise */, (uint)ceilf(_Seconds * FRAMES_PER_SECOND) /* unprecise */, filename);
|
||||
return std::string(message);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
140
3rdparty/echoprint-codegen/src/Base64.cxx
vendored
Normal file
140
3rdparty/echoprint-codegen/src/Base64.cxx
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author 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 source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
*/
|
||||
|
||||
#include "Base64.h"
|
||||
#include <iostream>
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
static const std::string base64_chars_url =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789-_";
|
||||
|
||||
|
||||
|
||||
static inline bool is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
static inline bool is_base64_url(unsigned char c) {
|
||||
return (isalnum(c) || (c == '-') || (c == '_'));
|
||||
}
|
||||
|
||||
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len, bool url) {
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--) {
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if (i == 3) {
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for(i = 0; (i <4) ; i++) {
|
||||
if (url)
|
||||
ret += base64_chars_url[char_array_4[i]];
|
||||
else
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
char_array_3[j] = '\0';
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); j++){
|
||||
if (url)
|
||||
ret += base64_chars_url[char_array_4[j]];
|
||||
else
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
}
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string const& encoded_string) {
|
||||
int in_len = encoded_string.size();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3];
|
||||
std::string ret;
|
||||
|
||||
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||
if (i ==4) {
|
||||
for (i = 0; i <4; i++)
|
||||
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (i = 0; (i < 3); i++)
|
||||
ret += char_array_3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j <4; j++)
|
||||
char_array_4[j] = 0;
|
||||
|
||||
for (j = 0; j <4; j++)
|
||||
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
9
3rdparty/echoprint-codegen/src/Base64.h
vendored
Normal file
9
3rdparty/echoprint-codegen/src/Base64.h
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string base64_encode(unsigned char const* , unsigned int len, bool url);
|
||||
std::string base64_decode(std::string const& s);
|
||||
|
||||
#endif
|
84
3rdparty/echoprint-codegen/src/Codegen.cxx
vendored
Normal file
84
3rdparty/echoprint-codegen/src/Codegen.cxx
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "Codegen.h"
|
||||
#include "AudioBufferInput.h"
|
||||
#include "Fingerprint.h"
|
||||
#include "Whitening.h"
|
||||
|
||||
#include "Base64.h"
|
||||
#include <zlib.h>
|
||||
|
||||
Codegen::Codegen(const float* pcm, uint numSamples, int start_offset) {
|
||||
if (Params::AudioStreamInput::MaxSamples < (uint)numSamples)
|
||||
throw std::runtime_error("File was too big\n");
|
||||
|
||||
Whitening *pWhitening = new Whitening(pcm, numSamples);
|
||||
pWhitening->Compute();
|
||||
|
||||
AudioBufferInput *pAudio = new AudioBufferInput();
|
||||
pAudio->SetBuffer(pWhitening->getWhitenedSamples(), pWhitening->getNumSamples());
|
||||
|
||||
SubbandAnalysis *pSubbandAnalysis = new SubbandAnalysis(pAudio);
|
||||
pSubbandAnalysis->Compute();
|
||||
|
||||
Fingerprint *pFingerprint = new Fingerprint(pSubbandAnalysis, start_offset);
|
||||
pFingerprint->Compute();
|
||||
|
||||
_CodeString = createCodeString(pFingerprint->getCodes());
|
||||
_NumCodes = pFingerprint->getCodes().size();
|
||||
|
||||
delete pFingerprint;
|
||||
delete pSubbandAnalysis;
|
||||
delete pWhitening;
|
||||
delete pAudio;
|
||||
}
|
||||
|
||||
string Codegen::createCodeString(vector<FPCode> vCodes) {
|
||||
if (vCodes.size() < 3) {
|
||||
return "";
|
||||
}
|
||||
std::ostringstream codestream;
|
||||
codestream << std::setfill('0') << std::hex;
|
||||
for (uint i = 0; i < vCodes.size(); i++)
|
||||
codestream << std::setw(5) << vCodes[i].frame;
|
||||
|
||||
for (uint i = 0; i < vCodes.size(); i++) {
|
||||
int hash = vCodes[i].code;
|
||||
codestream << std::setw(5) << hash;
|
||||
}
|
||||
return compress(codestream.str());
|
||||
}
|
||||
|
||||
|
||||
string Codegen::compress(const string& s) {
|
||||
long max_compressed_length = s.size()*2;
|
||||
unsigned char *compressed = new unsigned char[max_compressed_length];
|
||||
|
||||
// zlib the code string
|
||||
z_stream stream;
|
||||
stream.next_in = (Bytef*)(unsigned char*)s.c_str();
|
||||
stream.avail_in = (uInt)s.size();
|
||||
stream.zalloc = (alloc_func)0;
|
||||
stream.zfree = (free_func)0;
|
||||
stream.opaque = (voidpf)0;
|
||||
deflateInit(&stream, Z_DEFAULT_COMPRESSION);
|
||||
do {
|
||||
stream.next_out = compressed;
|
||||
stream.avail_out = max_compressed_length;
|
||||
if(deflate(&stream, Z_FINISH) == Z_STREAM_END) break;
|
||||
} while (stream.avail_out == 0);
|
||||
uint compressed_length = stream.total_out;
|
||||
deflateEnd(&stream);
|
||||
|
||||
// base64 the zlib'd code string
|
||||
string encoded = base64_encode(compressed, compressed_length, true);
|
||||
delete [] compressed;
|
||||
return encoded;
|
||||
}
|
53
3rdparty/echoprint-codegen/src/Codegen.h
vendored
Normal file
53
3rdparty/echoprint-codegen/src/Codegen.h
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#ifndef CODEGEN_H
|
||||
#define CODEGEN_H
|
||||
|
||||
// Entry point for generating codes from PCM data.
|
||||
#define VERSION 4.11
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Common.h"
|
||||
#include "AudioBufferInput.h"
|
||||
#include "SubbandAnalysis.h"
|
||||
#include "Fingerprint.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifdef CODEGEN_EXPORTS
|
||||
#define CODEGEN_API __declspec(dllexport)
|
||||
#pragma message("Exporting codegen.dll")
|
||||
#else
|
||||
#define CODEGEN_API __declspec(dllimport)
|
||||
#pragma message("Importing codegen.dll")
|
||||
#endif
|
||||
#else
|
||||
#define CODEGEN_API
|
||||
#endif
|
||||
|
||||
class Fingerprint;
|
||||
|
||||
class CODEGEN_API Codegen {
|
||||
public:
|
||||
Codegen(const float* pcm, uint numSamples, int start_offset);
|
||||
|
||||
string getCodeString(){return _CodeString;}
|
||||
int getNumCodes(){return _NumCodes;}
|
||||
float getVersion() { return VERSION; }
|
||||
private:
|
||||
Fingerprint* computeFingerprint(SubbandAnalysis *pSubbandAnalysis, int start_offset);
|
||||
string createCodeString(vector<FPCode> vCodes);
|
||||
|
||||
string compress(const string& s);
|
||||
string _CodeString;
|
||||
int _NumCodes;
|
||||
};
|
||||
|
||||
#endif
|
55
3rdparty/echoprint-codegen/src/Common.h
vendored
Normal file
55
3rdparty/echoprint-codegen/src/Common.h
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include "win_funcs.h"
|
||||
#include <sys/types.h>
|
||||
/* for STL*/
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <float.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
|
||||
// Returns the current date in seconds. The precision is in microseconds.
|
||||
static inline double now (void) {
|
||||
struct timeval tv;
|
||||
double now;
|
||||
gettimeofday (&tv, NULL);
|
||||
now = 1e-6 * tv.tv_usec + tv.tv_sec;
|
||||
return now;
|
||||
}
|
||||
|
||||
typedef unsigned int uint;
|
||||
#define NELEM(array) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
#ifndef _WIN32
|
||||
#define EN_ARRAY(type,var,size) type var[size]
|
||||
#else
|
||||
#define EN_ARRAY(type,var,size) type* var = (type*) _alloca((size)*sizeof(type))
|
||||
#endif
|
||||
|
||||
#endif
|
50
3rdparty/echoprint-codegen/src/File.h
vendored
Normal file
50
3rdparty/echoprint-codegen/src/File.h
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#ifndef FILE_H
|
||||
#define FILE_H
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include "win_unistd.h"
|
||||
#endif
|
||||
/*
|
||||
This makes file writing a bit easier (makes sure we don't forget to fclose, basically). Use it like this:
|
||||
|
||||
bool WriteStuffToFile(const char* filename)
|
||||
{
|
||||
File f(filename);
|
||||
if (f)
|
||||
fprintf(f, "stuff I want to print: %s", stuff);
|
||||
return f; // success/failure
|
||||
}
|
||||
*/
|
||||
class File {
|
||||
public:
|
||||
File(const char* filename){_f = fopen(filename, "w");};
|
||||
~File(){fclose(_f); _f = NULL;}
|
||||
operator bool(){return _f != NULL;}
|
||||
operator FILE*(){return _f;}
|
||||
static bool Exists(const char* filename){return (access(filename, F_OK) == 0);}
|
||||
static bool ends_with(const char* filename, const char* ending) {
|
||||
int nFilename = strlen(filename);
|
||||
int nEnding = strlen(ending);
|
||||
|
||||
bool same = false;
|
||||
if (nEnding <= nFilename) {
|
||||
const char* file_end = filename + strlen(filename) - strlen(ending);
|
||||
|
||||
for (int i = 0; i < nEnding; i++)
|
||||
if (tolower(file_end[i]) != tolower(ending[i])) return false;
|
||||
|
||||
same = true;
|
||||
}
|
||||
return same;
|
||||
}
|
||||
private:
|
||||
FILE* _f;
|
||||
};
|
||||
|
||||
#endif
|
238
3rdparty/echoprint-codegen/src/Fingerprint.cxx
vendored
Normal file
238
3rdparty/echoprint-codegen/src/Fingerprint.cxx
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include "Fingerprint.h"
|
||||
#include "Params.h"
|
||||
#include <string.h>
|
||||
|
||||
unsigned int MurmurHash2 ( const void * key, int len, unsigned int seed ) {
|
||||
// MurmurHash2, by Austin Appleby http://sites.google.com/site/murmurhash/
|
||||
// m and r are constants set by austin
|
||||
const unsigned int m = 0x5bd1e995;
|
||||
const int r = 24;
|
||||
// Initialize the hash to a 'random' value
|
||||
unsigned int h = seed ^ len;
|
||||
// Mix 4 bytes at a time into the hash
|
||||
const unsigned char * data = (const unsigned char *)key;
|
||||
while(len >= 4) {
|
||||
unsigned int k = *(unsigned int *)data;
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
h *= m;
|
||||
h ^= k;
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
// Handle the last few bytes of the input array
|
||||
switch(len) {
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0];
|
||||
h *= m;
|
||||
};
|
||||
|
||||
// Do a few final mixes of the hash to ensure the last few
|
||||
// bytes are well-incorporated.
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
return h;
|
||||
}
|
||||
|
||||
Fingerprint::Fingerprint(SubbandAnalysis* pSubbandAnalysis, int offset)
|
||||
: _pSubbandAnalysis(pSubbandAnalysis), _Offset(offset) { }
|
||||
|
||||
|
||||
uint Fingerprint::adaptiveOnsets(int ttarg, matrix_u&out, uint*&onset_counter_for_band) {
|
||||
// E is a sgram-like matrix of energies.
|
||||
const float *pE;
|
||||
int bands, frames, i, j, k;
|
||||
int deadtime = 128;
|
||||
double H[SUBBANDS],taus[SUBBANDS], N[SUBBANDS];
|
||||
int contact[SUBBANDS], lcontact[SUBBANDS], tsince[SUBBANDS];
|
||||
double overfact = 1.1; /* threshold rel. to actual peak */
|
||||
uint onset_counter = 0;
|
||||
|
||||
matrix_f E = _pSubbandAnalysis->getMatrix();
|
||||
|
||||
// Take successive stretches of 8 subband samples and sum their energy under a hann window, then hop by 4 samples (50% window overlap).
|
||||
int hop = 4;
|
||||
int nsm = 8;
|
||||
float ham[nsm];
|
||||
for(int i = 0 ; i != nsm ; i++)
|
||||
ham[i] = .5 - .5*cos( (2.*M_PI/(nsm-1))*i);
|
||||
|
||||
int nc = floor((float)E.size2()/(float)hop)-(floor((float)nsm/(float)hop)-1);
|
||||
matrix_f Eb = matrix_f(nc, 8);
|
||||
MatrixUtility::clear(Eb);
|
||||
for(i=0;i<nc;i++) {
|
||||
for(j=0;j<SUBBANDS;j++) {
|
||||
for(k=0;k<nsm;k++) Eb(i,j) = Eb(i,j) + ( E(j,(i*hop)+k) * ham[k]);
|
||||
Eb(i,j) = sqrtf(Eb(i,j));
|
||||
}
|
||||
}
|
||||
|
||||
frames = Eb.size1();
|
||||
bands = Eb.size2();
|
||||
pE = &Eb.data()[0];
|
||||
|
||||
out = matrix_u(SUBBANDS, frames);
|
||||
onset_counter_for_band = new uint[SUBBANDS];
|
||||
|
||||
double bn[] = {0.1883, 0.4230, 0.3392}; /* preemph filter */ // new
|
||||
int nbn = 3;
|
||||
double a1 = 0.98;
|
||||
double Y0[SUBBANDS];
|
||||
|
||||
for (j = 0; j < bands; ++j) {
|
||||
onset_counter_for_band[j] = 0;
|
||||
N[j] = 0.0;
|
||||
taus[j] = 1.0;
|
||||
H[j] = pE[j];
|
||||
contact[j] = 0;
|
||||
lcontact[j] = 0;
|
||||
tsince[j] = 0;
|
||||
Y0[j] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (j = 0; j < SUBBANDS; ++j) {
|
||||
|
||||
double xn = 0;
|
||||
/* calculate the filter - FIR part */
|
||||
if (i >= 2*nbn) {
|
||||
for (int k = 0; k < nbn; ++k) {
|
||||
xn += bn[k]*(pE[j-SUBBANDS*k] - pE[j-SUBBANDS*(2*nbn-k)]);
|
||||
}
|
||||
}
|
||||
/* IIR part */
|
||||
xn = xn + a1*Y0[j];
|
||||
/* remember the last filtered level */
|
||||
Y0[j] = xn;
|
||||
|
||||
contact[j] = (xn > H[j])? 1 : 0;
|
||||
|
||||
if (contact[j] == 1 && lcontact[j] == 0) {
|
||||
/* attach - record the threshold level unless we have one */
|
||||
if(N[j] == 0) {
|
||||
N[j] = H[j];
|
||||
}
|
||||
}
|
||||
if (contact[j] == 1) {
|
||||
/* update with new threshold */
|
||||
H[j] = xn * overfact;
|
||||
} else {
|
||||
/* apply decays */
|
||||
H[j] = H[j] * exp(-1.0/(double)taus[j]);
|
||||
}
|
||||
|
||||
if (contact[j] == 0 && lcontact[j] == 1) {
|
||||
/* detach */
|
||||
if (onset_counter_for_band[j] > 0 && (int)out(j, onset_counter_for_band[j]-1) > i - deadtime) {
|
||||
// overwrite last-written time
|
||||
--onset_counter_for_band[j];
|
||||
--onset_counter;
|
||||
}
|
||||
out(j, onset_counter_for_band[j]++) = i;
|
||||
++onset_counter;
|
||||
tsince[j] = 0;
|
||||
}
|
||||
++tsince[j];
|
||||
if (tsince[j] > ttarg) {
|
||||
taus[j] = taus[j] - 1;
|
||||
if (taus[j] < 1) taus[j] = 1;
|
||||
} else {
|
||||
taus[j] = taus[j] + 1;
|
||||
}
|
||||
|
||||
if ( (contact[j] == 0) && (tsince[j] > deadtime)) {
|
||||
/* forget the threshold where we recently hit */
|
||||
N[j] = 0;
|
||||
}
|
||||
lcontact[j] = contact[j];
|
||||
}
|
||||
pE += bands;
|
||||
}
|
||||
|
||||
return onset_counter;
|
||||
}
|
||||
|
||||
|
||||
// dan is going to beat me if i call this "decimated_time_for_frame" like i want to
|
||||
uint Fingerprint::quantized_time_for_frame_delta(uint frame_delta) {
|
||||
double time_for_frame_delta = (double)frame_delta / ((double)Params::AudioStreamInput::SamplingRate / 32.0);
|
||||
return ((int)floor((time_for_frame_delta * 1000.0) / (float)QUANTIZE_DT_S) * QUANTIZE_DT_S) / floor(QUANTIZE_DT_S*1000.0);
|
||||
}
|
||||
|
||||
uint Fingerprint::quantized_time_for_frame_absolute(uint frame) {
|
||||
double time_for_frame = _Offset + (double)frame / ((double)Params::AudioStreamInput::SamplingRate / 32.0);
|
||||
return ((int)rint((time_for_frame * 1000.0) / (float)QUANTIZE_A_S) * QUANTIZE_A_S) / floor(QUANTIZE_A_S*1000.0);
|
||||
}
|
||||
|
||||
|
||||
void Fingerprint::Compute() {
|
||||
uint actual_codes = 0;
|
||||
unsigned char hash_material[5];
|
||||
for(uint i=0;i<5;i++) hash_material[i] = 0;
|
||||
uint * onset_counter_for_band;
|
||||
matrix_u out;
|
||||
uint onset_count = adaptiveOnsets(345, out, onset_counter_for_band);
|
||||
_Codes.resize(onset_count*6);
|
||||
|
||||
for(unsigned char band=0;band<SUBBANDS;band++) {
|
||||
if (onset_counter_for_band[band]>2) {
|
||||
for(uint onset=0;onset<onset_counter_for_band[band]-2;onset++) {
|
||||
// What time was this onset at?
|
||||
uint time_for_onset_ms_quantized = quantized_time_for_frame_absolute(out(band,onset));
|
||||
|
||||
uint p[2][6];
|
||||
int nhashes = 6;
|
||||
|
||||
if ((int)onset == (int)onset_counter_for_band[band]-4) { nhashes = 3; }
|
||||
if ((int)onset == (int)onset_counter_for_band[band]-3) { nhashes = 1; }
|
||||
p[0][0] = (out(band,onset+1) - out(band,onset));
|
||||
p[1][0] = (out(band,onset+2) - out(band,onset+1));
|
||||
if(nhashes > 1) {
|
||||
p[0][1] = (out(band,onset+1) - out(band,onset));
|
||||
p[1][1] = (out(band,onset+3) - out(band,onset+1));
|
||||
p[0][2] = (out(band,onset+2) - out(band,onset));
|
||||
p[1][2] = (out(band,onset+3) - out(band,onset+2));
|
||||
if(nhashes > 3) {
|
||||
p[0][3] = (out(band,onset+1) - out(band,onset));
|
||||
p[1][3] = (out(band,onset+4) - out(band,onset+1));
|
||||
p[0][4] = (out(band,onset+2) - out(band,onset));
|
||||
p[1][4] = (out(band,onset+4) - out(band,onset+2));
|
||||
p[0][5] = (out(band,onset+3) - out(band,onset));
|
||||
p[1][5] = (out(band,onset+4) - out(band,onset+3));
|
||||
}
|
||||
}
|
||||
|
||||
// For each pair emit a code
|
||||
for(uint k=0;k<6;k++) {
|
||||
// Quantize the time deltas to 23ms
|
||||
short time_delta0 = (short)quantized_time_for_frame_delta(p[0][k]);
|
||||
short time_delta1 = (short)quantized_time_for_frame_delta(p[1][k]);
|
||||
// Create a key from the time deltas and the band index
|
||||
memcpy(hash_material+0, (const void*)&time_delta0, 2);
|
||||
memcpy(hash_material+2, (const void*)&time_delta1, 2);
|
||||
memcpy(hash_material+4, (const void*)&band, 1);
|
||||
uint hashed_code = MurmurHash2(&hash_material, 5, HASH_SEED) & HASH_BITMASK;
|
||||
|
||||
// Set the code alongside the time of onset
|
||||
_Codes[actual_codes++] = FPCode(time_for_onset_ms_quantized, hashed_code);
|
||||
//fprintf(stderr, "whee %d,%d: [%d, %d] (%d, %d), %d = %u at %d\n", actual_codes, k, time_delta0, time_delta1, p[0][k], p[1][k], band, hashed_code, time_for_onset_ms_quantized);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_Codes.resize(actual_codes);
|
||||
delete [] onset_counter_for_band;
|
||||
}
|
||||
|
||||
|
45
3rdparty/echoprint-codegen/src/Fingerprint.h
vendored
Normal file
45
3rdparty/echoprint-codegen/src/Fingerprint.h
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#ifndef FINGERPRINT_H
|
||||
#define FINGERPRINT_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "SubbandAnalysis.h"
|
||||
#include "MatrixUtility.h"
|
||||
#include <vector>
|
||||
|
||||
#define HASH_SEED 0x9ea5fa36
|
||||
#define QUANTIZE_DT_S (256.0/11025.0)
|
||||
#define QUANTIZE_A_S (256.0/11025.0)
|
||||
#define HASH_BITMASK 0x000fffff
|
||||
#define SUBBANDS 8
|
||||
|
||||
struct FPCode {
|
||||
FPCode() : frame(0), code(0) {}
|
||||
FPCode(uint f, int c) : frame(f), code(c) {}
|
||||
uint frame;
|
||||
uint code;
|
||||
};
|
||||
|
||||
unsigned int MurmurHash2 ( const void * key, int len, unsigned int seed );
|
||||
|
||||
class Fingerprint {
|
||||
public:
|
||||
uint quantized_time_for_frame_delta(uint frame_delta);
|
||||
uint quantized_time_for_frame_absolute(uint frame);
|
||||
Fingerprint(SubbandAnalysis* pSubbandAnalysis, int offset);
|
||||
void Compute();
|
||||
uint adaptiveOnsets(int ttarg, matrix_u&out, uint*&onset_counter_for_band) ;
|
||||
std::vector<FPCode>& getCodes(){return _Codes;}
|
||||
protected:
|
||||
SubbandAnalysis *_pSubbandAnalysis;
|
||||
int _Offset;
|
||||
std::vector<FPCode> _Codes;
|
||||
};
|
||||
|
||||
#endif
|
48
3rdparty/echoprint-codegen/src/Makefile
vendored
Normal file
48
3rdparty/echoprint-codegen/src/Makefile
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
UNAME := $(shell uname -s)
|
||||
CXX=g++
|
||||
CC=gcc
|
||||
ARCH=`uname -m`
|
||||
#OPTFLAGS=-g -O0
|
||||
OPTFLAGS=-O3 -DBOOST_UBLAS_NDEBUG -DNDEBUG
|
||||
CXXFLAGS=-Wall -I/usr/local/include/boost-1_35 `taglib-config --cflags` -fPIC $(OPTFLAGS)
|
||||
CFLAGS=-Wall -fPIC $(OPTFLAGS)
|
||||
LDFLAGS=`taglib-config --libs` -lz -lpthread $(OPTFLAGS)
|
||||
|
||||
MODULES_LIB = \
|
||||
AudioBufferInput.o \
|
||||
AudioStreamInput.o \
|
||||
Base64.o \
|
||||
Codegen.o \
|
||||
Fingerprint.o \
|
||||
MatrixUtility.o \
|
||||
SubbandAnalysis.o \
|
||||
Whitening.o
|
||||
MODULES = $(MODULES_LIB) Metadata.o
|
||||
|
||||
main: $(MODULES) main.o
|
||||
$(CXX) $(MODULES) $(LDFLAGS) main.o -o ../echoprint-codegen
|
||||
$(CXX) -shared -fPIC -o libcodegen.so $(MODULES_LIB) -lz
|
||||
ifeq ($(UNAME),Darwin)
|
||||
libtool -dynamic -flat_namespace -install_name libcodegen.4.1.1.dylib -lSystem -compatibility_version 4.1 -macosx_version_min 10.6 \
|
||||
-current_version 4.1.1 -o libcodegen.4.1.1.dylib -undefined suppress \
|
||||
$(MODULES) -framework vecLib -framework Accelerate
|
||||
endif
|
||||
|
||||
%.o: %.c %.h
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.cxx %.h
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.cxx
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o ../echoprint-codegen
|
||||
rm -f *.so
|
||||
ifeq ($(UNAME),Darwin)
|
||||
rm -f *.dylib
|
||||
endif
|
54
3rdparty/echoprint-codegen/src/MatrixUtility.cxx
vendored
Normal file
54
3rdparty/echoprint-codegen/src/MatrixUtility.cxx
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include "MatrixUtility.h"
|
||||
|
||||
// http://www.boost.org/doc/libs/1_35_0/libs/numeric/ublas/doc/matrix.htm
|
||||
|
||||
namespace MatrixUtility {
|
||||
|
||||
bool TextFileOutput(const matrix_f& A, const char* filename) {
|
||||
FILE *matrix_file = fopen(filename, "w");
|
||||
bool success = (matrix_file != NULL);
|
||||
if (success) {
|
||||
const float *d = &A.data()[0];
|
||||
for (uint i = 0; i < A.size1(); i++) {
|
||||
for (uint j = 0; j < A.size2(); j++)
|
||||
fprintf(matrix_file, "%2.3f ", d[i*A.size2() + j]);
|
||||
|
||||
fprintf(matrix_file, "\n");
|
||||
}
|
||||
fclose(matrix_file);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool FileOutput(const matrix_f& A, const char* filename) {
|
||||
FILE *matrix_file = fopen(filename, "wb");
|
||||
bool success = (matrix_file != NULL);
|
||||
if (success) {
|
||||
uint mm = A.size1();
|
||||
uint mn = A.size2();
|
||||
fwrite(&mm, sizeof(int), 1, matrix_file);
|
||||
fwrite(&mn, sizeof(int), 1, matrix_file);
|
||||
|
||||
for (uint i = 0; i< A.size1(); i++) {
|
||||
for(uint j=0;j<A.size2(); j++) {
|
||||
const float d = A(i, j);
|
||||
fwrite(&d, sizeof(float), 1, matrix_file);
|
||||
}
|
||||
}
|
||||
fclose(matrix_file);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void clear(matrix_f A) {
|
||||
for(uint i=0;i<A.size1();i++) for(uint j=0;j<A.size2();j++) A(i,j) = 0.0;
|
||||
}
|
||||
|
||||
} // namespace
|
32
3rdparty/echoprint-codegen/src/MatrixUtility.h
vendored
Normal file
32
3rdparty/echoprint-codegen/src/MatrixUtility.h
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#ifndef MATRIXUTILITY_H
|
||||
#define MATRIXUTILITY_H
|
||||
|
||||
#include "Common.h"
|
||||
#include <boost/numeric/ublas/matrix.hpp>
|
||||
#include <boost/numeric/ublas/matrix_proxy.hpp>
|
||||
|
||||
namespace ublas = boost::numeric::ublas;
|
||||
|
||||
typedef ublas::matrix<float> matrix_f;
|
||||
typedef ublas::matrix<uint> matrix_u;
|
||||
|
||||
typedef ublas::matrix_row<matrix_f> matrix_row_f;
|
||||
typedef ublas::matrix_row<const ublas::matrix<float> > const_matrix_row_f;
|
||||
typedef ublas::matrix_column<matrix_f> matrix_column_f;
|
||||
typedef ublas::matrix_range<matrix_f> matrix_range_f;
|
||||
|
||||
namespace MatrixUtility {
|
||||
inline uint rows(matrix_f A){ return A.size1();}
|
||||
inline uint cols(matrix_f A){ return A.size2();}
|
||||
bool FileOutput(const matrix_f& A, const char* filename);
|
||||
bool TextFileOutput(const matrix_f& A, const char* filename);
|
||||
void clear(matrix_f A);
|
||||
}
|
||||
#endif
|
33
3rdparty/echoprint-codegen/src/Metadata.cxx
vendored
Normal file
33
3rdparty/echoprint-codegen/src/Metadata.cxx
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include "Metadata.h"
|
||||
#include <taglib/fileref.h>
|
||||
#include <taglib/tag.h>
|
||||
#include <iostream>
|
||||
|
||||
Metadata::Metadata(const string& file) : _Filename(file), _Artist(""), _Album(""), _Title(""), _Genre(""), _Bitrate(0), _SampleRate(0), _Seconds(0) {
|
||||
if (file != "stdin") {
|
||||
// TODO: Consider removing the path from the filename -- not sure if we can do this in a platform-independent way.
|
||||
TagLib::FileRef f(_Filename.c_str());
|
||||
|
||||
TagLib::Tag* tag = f.isNull() ? NULL : f.tag();
|
||||
if (tag != NULL) {
|
||||
_Artist = tag->artist().toCString();
|
||||
_Album = tag->album().toCString();
|
||||
_Title = tag->title().toCString();
|
||||
_Genre = tag->genre().toCString();
|
||||
}
|
||||
|
||||
TagLib::AudioProperties* properties = f.isNull() ? NULL : f.audioProperties();
|
||||
if (properties != NULL) {
|
||||
_Bitrate = properties->bitrate();
|
||||
_SampleRate = properties->sampleRate();
|
||||
_Seconds = properties->length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
37
3rdparty/echoprint-codegen/src/Metadata.h
vendored
Normal file
37
3rdparty/echoprint-codegen/src/Metadata.h
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#ifndef METADATA_H
|
||||
#define METADATA_H
|
||||
|
||||
|
||||
#include <string>
|
||||
using std::string;
|
||||
|
||||
class Metadata {
|
||||
public:
|
||||
Metadata(const std::string& file);
|
||||
string Filename() {return _Filename;}
|
||||
string Artist(){ return _Artist;}
|
||||
string Album() { return _Album;}
|
||||
string Title() { return _Title;}
|
||||
string Genre() { return _Genre;}
|
||||
int Bitrate() { return _Bitrate;}
|
||||
int SampleRate(){ return _SampleRate;}
|
||||
int Seconds() { return _Seconds;}
|
||||
private:
|
||||
string _Filename;
|
||||
string _Artist;
|
||||
string _Album;
|
||||
string _Title;
|
||||
string _Genre;
|
||||
|
||||
int _Bitrate;
|
||||
int _SampleRate;
|
||||
int _Seconds;
|
||||
};
|
||||
#endif
|
23
3rdparty/echoprint-codegen/src/Params.h
vendored
Normal file
23
3rdparty/echoprint-codegen/src/Params.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#ifndef PARAMS_H
|
||||
#define PARAMS_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
namespace Params {
|
||||
namespace AudioStreamInput {
|
||||
const float SamplingRate = 11025.0f;
|
||||
const uint Channels = 1;
|
||||
const float SecondsPerChunk = 10.0f;
|
||||
const uint BytesPerSample = 4; // floats
|
||||
const uint MaxSamples = 66977792; // TODO: Remove this, or set it intelligently, at least. Good for 32-bit analyzer.
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
70
3rdparty/echoprint-codegen/src/SubbandAnalysis.cxx
vendored
Normal file
70
3rdparty/echoprint-codegen/src/SubbandAnalysis.cxx
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include "SubbandAnalysis.h"
|
||||
#include "AudioStreamInput.h"
|
||||
|
||||
SubbandAnalysis::SubbandAnalysis(AudioStreamInput* pAudio) {
|
||||
_pSamples = pAudio->getSamples();
|
||||
_NumSamples = pAudio->getNumSamples();
|
||||
Init();
|
||||
}
|
||||
|
||||
SubbandAnalysis::SubbandAnalysis(const float* pSamples, uint numSamples) :
|
||||
_pSamples(pSamples), _NumSamples(numSamples) {
|
||||
Init();
|
||||
}
|
||||
|
||||
SubbandAnalysis::~SubbandAnalysis() {
|
||||
}
|
||||
|
||||
void SubbandAnalysis::Init() {
|
||||
// Calculate the analysis filter bank coefficients
|
||||
_Mr = matrix_f(M_ROWS, M_COLS);
|
||||
_Mi = matrix_f(M_ROWS, M_COLS);
|
||||
for (uint i = 0; i < M_ROWS; ++i) {
|
||||
for (uint k = 0; k < M_COLS; ++k) {
|
||||
_Mr(i,k) = cos((2*i + 1)*(k-4)*(M_PI/16.0));
|
||||
_Mi(i,k) = sin((2*i + 1)*(k-4)*(M_PI/16.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SubbandAnalysis::Compute() {
|
||||
uint t, i, j;
|
||||
|
||||
float Z[C_LEN];
|
||||
float Y[M_COLS];
|
||||
|
||||
_NumFrames = (_NumSamples - C_LEN + 1)/SUBBANDS;
|
||||
assert(_NumFrames > 0);
|
||||
|
||||
_Data = matrix_f(SUBBANDS, _NumFrames);
|
||||
|
||||
for (t = 0; t < _NumFrames; ++t) {
|
||||
for (i = 0; i < C_LEN; ++i) {
|
||||
Z[i] = _pSamples[ t*SUBBANDS + i] * SubbandFilterBank::C[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < M_COLS; ++i) {
|
||||
Y[i] = Z[i];
|
||||
}
|
||||
for (i = 0; i < M_COLS; ++i) {
|
||||
for (j = 1; j < M_ROWS; ++j) {
|
||||
Y[i] += Z[i + M_COLS*j];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < M_ROWS; ++i) {
|
||||
float Dr = 0, Di = 0;
|
||||
for (j = 0; j < M_COLS; ++j) {
|
||||
Dr += _Mr(i,j) * Y[j];
|
||||
Di -= _Mi(i,j) * Y[j];
|
||||
}
|
||||
_Data(i,t) = Dr*Dr + Di*Di;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
65
3rdparty/echoprint-codegen/src/SubbandAnalysis.h
vendored
Normal file
65
3rdparty/echoprint-codegen/src/SubbandAnalysis.h
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#ifndef SUBBANDANALYSIS_H
|
||||
#define SUBBANDANALYSIS_H
|
||||
#include "Common.h"
|
||||
#include "Params.h"
|
||||
#include "MatrixUtility.h"
|
||||
|
||||
#define C_LEN 128
|
||||
#define SUBBANDS 8
|
||||
#define M_ROWS 8
|
||||
#define M_COLS 16
|
||||
|
||||
namespace SubbandFilterBank {
|
||||
// 128pt, 1/8th band low-pass prototype subsampled from Table_analysis_window
|
||||
static const float C[C_LEN] = {
|
||||
0.000000477, 0.000000954, 0.000001431, 0.000002384, 0.000003815, 0.000006199, 0.000009060, 0.000013828,
|
||||
0.000019550, 0.000027657, 0.000037670, 0.000049591, 0.000062943, 0.000076771, 0.000090599, 0.000101566,
|
||||
-0.000108242, -0.000106812, -0.000095367, -0.000069618, -0.000027180, 0.000034332, 0.000116348, 0.000218868,
|
||||
0.000339031, 0.000472546, 0.000611782, 0.000747204, 0.000866413, 0.000954151, 0.000994205, 0.000971317,
|
||||
-0.000868797, -0.000674248, -0.000378609, 0.000021458, 0.000522137, 0.001111031, 0.001766682, 0.002457142,
|
||||
0.003141880, 0.003771782, 0.004290581, 0.004638195, 0.004752159, 0.004573822, 0.004049301, 0.003134727,
|
||||
-0.001800537, -0.000033379, 0.002161503, 0.004756451, 0.007703304, 0.010933399, 0.014358521, 0.017876148,
|
||||
0.021372318, 0.024725437, 0.027815342, 0.030526638, 0.032754898, 0.034412861, 0.035435200, 0.035780907,
|
||||
-0.035435200, -0.034412861, -0.032754898, -0.030526638, -0.027815342, -0.024725437, -0.021372318, -0.017876148,
|
||||
-0.014358521, -0.010933399, -0.007703304, -0.004756451, -0.002161503, 0.000033379, 0.001800537, 0.003134727,
|
||||
-0.004049301, -0.004573822, -0.004752159, -0.004638195, -0.004290581, -0.003771782, -0.003141880, -0.002457142,
|
||||
-0.001766682, -0.001111031, -0.000522137, -0.000021458, 0.000378609, 0.000674248, 0.000868797, 0.000971317,
|
||||
-0.000994205, -0.000954151, -0.000866413, -0.000747204, -0.000611782, -0.000472546, -0.000339031, -0.000218868,
|
||||
-0.000116348, -0.000034332, 0.000027180, 0.000069618, 0.000095367, 0.000106812, 0.000108242, 0.000101566,
|
||||
-0.000090599, -0.000076771, -0.000062943, -0.000049591, -0.000037670, -0.000027657, -0.000019550, -0.000013828,
|
||||
-0.000009060, -0.000006199, -0.000003815, -0.000002384, -0.000001431, -0.000000954, -0.000000477, 0};
|
||||
}
|
||||
|
||||
class AudioStreamInput;
|
||||
|
||||
class SubbandAnalysis {
|
||||
public:
|
||||
inline SubbandAnalysis() {};
|
||||
SubbandAnalysis(AudioStreamInput* pAudio);
|
||||
SubbandAnalysis(const float* pSamples, uint numSamples);
|
||||
virtual ~SubbandAnalysis();
|
||||
void Compute();
|
||||
public:
|
||||
inline uint getNumFrames() const {return _NumFrames;}
|
||||
inline uint getNumBands() const {return SUBBANDS;}
|
||||
const matrix_f& getMatrix() {return _Data;}
|
||||
|
||||
protected:
|
||||
const float* _pSamples;
|
||||
uint _NumSamples;
|
||||
uint _NumFrames;
|
||||
matrix_f _Mi;
|
||||
matrix_f _Mr;
|
||||
matrix_f _Data;
|
||||
|
||||
private:
|
||||
void Init();
|
||||
};
|
||||
|
||||
#endif
|
109
3rdparty/echoprint-codegen/src/Whitening.cxx
vendored
Normal file
109
3rdparty/echoprint-codegen/src/Whitening.cxx
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include "Whitening.h"
|
||||
#include "AudioStreamInput.h"
|
||||
|
||||
Whitening::Whitening(AudioStreamInput* pAudio) {
|
||||
_pSamples = pAudio->getSamples();
|
||||
_NumSamples = pAudio->getNumSamples();
|
||||
Init();
|
||||
}
|
||||
|
||||
Whitening::Whitening(const float* pSamples, uint numSamples) :
|
||||
_pSamples(pSamples), _NumSamples(numSamples) {
|
||||
Init();
|
||||
}
|
||||
|
||||
Whitening::~Whitening() {
|
||||
free(_R);
|
||||
free(_Xo);
|
||||
free(_ai);
|
||||
free(_whitened);
|
||||
}
|
||||
|
||||
void Whitening::Init() {
|
||||
int i;
|
||||
_P = 40;
|
||||
|
||||
_R = (float *)malloc((_P+1)*sizeof(float));
|
||||
for (i = 0; i <= _P; ++i) { _R[i] = 0.0; }
|
||||
_R[0] = 0.001;
|
||||
|
||||
_Xo = (float *)malloc((_P+1)*sizeof(float));
|
||||
for (i = 0; i < _P; ++i) { _Xo[i] = 0.0; }
|
||||
|
||||
_ai = (float *)malloc((_P+1)*sizeof(float));
|
||||
_whitened = (float*) malloc(sizeof(float)*_NumSamples);
|
||||
}
|
||||
|
||||
void Whitening::Compute() {
|
||||
int blocklen = 10000;
|
||||
int i, newblocklen;
|
||||
for(i=0;i<(int)_NumSamples;i=i+blocklen) {
|
||||
if (i+blocklen >= (int)_NumSamples) {
|
||||
newblocklen = _NumSamples -i - 1;
|
||||
} else { newblocklen = blocklen; }
|
||||
ComputeBlock(i, newblocklen);
|
||||
}
|
||||
}
|
||||
|
||||
void Whitening::ComputeBlock(int start, int blockSize) {
|
||||
int i, j;
|
||||
float alpha, E, ki;
|
||||
float T = 8;
|
||||
alpha = 1.0/T;
|
||||
|
||||
// calculate autocorrelation of current block
|
||||
|
||||
for (i = 0; i <= _P; ++i) {
|
||||
float acc = 0;
|
||||
for (j = 0; j < (int)blockSize; ++j) {
|
||||
if (j >= i) {
|
||||
acc += _pSamples[j+start] * _pSamples[j-i+start];
|
||||
}
|
||||
}
|
||||
// smoothed update
|
||||
_R[i] += alpha*(acc - _R[i]);
|
||||
}
|
||||
|
||||
// calculate new filter coefficients
|
||||
// Durbin's recursion, per p. 411 of Rabiner & Schafer 1978
|
||||
E = _R[0];
|
||||
for (i = 1; i <= _P; ++i) {
|
||||
float sumalphaR = 0;
|
||||
for (j = 1; j < i; ++j) {
|
||||
sumalphaR += _ai[j]*_R[i-j];
|
||||
}
|
||||
ki = (_R[i] - sumalphaR)/E;
|
||||
_ai[i] = ki;
|
||||
for (j = 1; j <= i/2; ++j) {
|
||||
float aj = _ai[j];
|
||||
float aimj = _ai[i-j];
|
||||
_ai[j] = aj - ki*aimj;
|
||||
_ai[i-j] = aimj - ki*aj;
|
||||
}
|
||||
E = (1-ki*ki)*E;
|
||||
}
|
||||
// calculate new output
|
||||
for (i = 0; i < (int)blockSize; ++i) {
|
||||
float acc = _pSamples[i+start];
|
||||
for (j = 1; j <= _P; ++j) {
|
||||
if (i-j < 0) {
|
||||
acc -= _ai[j]*_Xo[_P + i-j];
|
||||
} else {
|
||||
acc -= _ai[j]*_pSamples[i-j+start];
|
||||
}
|
||||
}
|
||||
_whitened[i+start] = acc;
|
||||
}
|
||||
// save last few frames of input
|
||||
for (i = 0; i <= _P; ++i) {
|
||||
_Xo[i] = _pSamples[blockSize-1-_P+i+start];
|
||||
}
|
||||
}
|
||||
|
||||
|
41
3rdparty/echoprint-codegen/src/Whitening.h
vendored
Normal file
41
3rdparty/echoprint-codegen/src/Whitening.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#ifndef WHITENING_H
|
||||
#define WHITENING_H
|
||||
#include "Common.h"
|
||||
#include "Params.h"
|
||||
#include "MatrixUtility.h"
|
||||
|
||||
|
||||
class AudioStreamInput;
|
||||
|
||||
class Whitening {
|
||||
public:
|
||||
inline Whitening() {};
|
||||
Whitening(AudioStreamInput* pAudio);
|
||||
Whitening(const float* pSamples, uint numSamples);
|
||||
virtual ~Whitening();
|
||||
void Compute();
|
||||
void ComputeBlock(int start, int blockSize);
|
||||
|
||||
public:
|
||||
float* getWhitenedSamples() const {return _whitened;}
|
||||
inline uint getNumSamples() const {return _NumSamples;}
|
||||
|
||||
protected:
|
||||
const float* _pSamples;
|
||||
float* _whitened;
|
||||
uint _NumSamples;
|
||||
float* _R;
|
||||
float *_Xo;
|
||||
float *_ai;
|
||||
int _P;
|
||||
private:
|
||||
void Init();
|
||||
};
|
||||
|
||||
#endif
|
22
3rdparty/echoprint-codegen/src/echoprint-codegen-ios/README.md
vendored
Normal file
22
3rdparty/echoprint-codegen/src/echoprint-codegen-ios/README.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
------------------------------
|
||||
Building echoprint-codegen-ios
|
||||
------------------------------
|
||||
|
||||
0. If you don't have it, get [boost](http://www.boost.org/)
|
||||
**Note:** echoprint-codegen only uses boost headers for some numeric operations,
|
||||
so you don't need to compile boost.
|
||||
|
||||
|
||||
1. Open `echoprint-codegen-ios.xconfig` and set up your boost include directory, e.g.,
|
||||
|
||||
HEADER_SEARCH_PATHS = /Users/artgillespie/dev/src/boost_1_46_1
|
||||
|
||||
2. Build!
|
||||
|
||||
3. If you get a bunch of build errors, goto 2.
|
||||
|
||||
-------------------------------------------
|
||||
Using echoprint-codegen-ios in your iOS app
|
||||
-------------------------------------------
|
||||
|
||||
Check out the sample app!
|
8
3rdparty/echoprint-codegen/src/echoprint-codegen-ios/echoprint-codegen-ios.xcconfig
vendored
Normal file
8
3rdparty/echoprint-codegen/src/echoprint-codegen-ios/echoprint-codegen-ios.xcconfig
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// echoprint-codegen-ios.xcconfig
|
||||
// echoprint-codegen-ios
|
||||
//
|
||||
// Created by Art Gillespie on 5/14/11.
|
||||
//
|
||||
|
||||
HEADER_SEARCH_PATHS = /usr/local/include/boost-1_35
|
303
3rdparty/echoprint-codegen/src/echoprint-codegen-ios/echoprint-codegen-ios.xcodeproj/project.pbxproj
vendored
Normal file
303
3rdparty/echoprint-codegen/src/echoprint-codegen-ios/echoprint-codegen-ios.xcodeproj/project.pbxproj
vendored
Normal file
@ -0,0 +1,303 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
9AE5AA19139FB2620009FCD8 /* SubbandAnalysis.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AE5AA17139FB2620009FCD8 /* SubbandAnalysis.h */; };
|
||||
9AE5AA1A139FB2620009FCD8 /* SubbandAnalysis.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 9AE5AA18139FB2620009FCD8 /* SubbandAnalysis.cxx */; };
|
||||
C8D4AD20137EE85F00CD506D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8D4AD1F137EE85F00CD506D /* Foundation.framework */; };
|
||||
C8D4AD4F137EF41100CD506D /* AudioBufferInput.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C8D4AD29137EF41100CD506D /* AudioBufferInput.cxx */; };
|
||||
C8D4AD50137EF41100CD506D /* AudioBufferInput.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD2A137EF41100CD506D /* AudioBufferInput.h */; };
|
||||
C8D4AD51137EF41100CD506D /* AudioStreamInput.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C8D4AD2B137EF41100CD506D /* AudioStreamInput.cxx */; };
|
||||
C8D4AD52137EF41100CD506D /* AudioStreamInput.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD2C137EF41100CD506D /* AudioStreamInput.h */; };
|
||||
C8D4AD53137EF41100CD506D /* Base64.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C8D4AD2D137EF41100CD506D /* Base64.cxx */; };
|
||||
C8D4AD54137EF41100CD506D /* Base64.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD2E137EF41100CD506D /* Base64.h */; };
|
||||
C8D4AD55137EF41100CD506D /* Codegen.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C8D4AD2F137EF41100CD506D /* Codegen.cxx */; };
|
||||
C8D4AD56137EF41100CD506D /* Codegen.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD30137EF41100CD506D /* Codegen.h */; };
|
||||
C8D4AD57137EF41100CD506D /* Common.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD31137EF41100CD506D /* Common.h */; };
|
||||
C8D4AD60137EF41100CD506D /* File.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD40137EF41100CD506D /* File.h */; };
|
||||
C8D4AD62137EF41100CD506D /* Fingerprint.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C8D4AD42137EF41100CD506D /* Fingerprint.cxx */; };
|
||||
C8D4AD63137EF41100CD506D /* Fingerprint.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD43137EF41100CD506D /* Fingerprint.h */; };
|
||||
C8D4AD64137EF41100CD506D /* MatrixUtility.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C8D4AD44137EF41100CD506D /* MatrixUtility.cxx */; };
|
||||
C8D4AD65137EF41100CD506D /* MatrixUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD45137EF41100CD506D /* MatrixUtility.h */; };
|
||||
C8D4AD68137EF41100CD506D /* Params.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD48137EF41100CD506D /* Params.h */; };
|
||||
C8D4AD6D137EF41100CD506D /* win_funcs.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD4D137EF41100CD506D /* win_funcs.h */; };
|
||||
C8D4AD6E137EF41100CD506D /* win_unistd.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D4AD4E137EF41100CD506D /* win_unistd.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
9AE5AA17139FB2620009FCD8 /* SubbandAnalysis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SubbandAnalysis.h; path = ../SubbandAnalysis.h; sourceTree = SOURCE_ROOT; };
|
||||
9AE5AA18139FB2620009FCD8 /* SubbandAnalysis.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SubbandAnalysis.cxx; path = ../SubbandAnalysis.cxx; sourceTree = SOURCE_ROOT; };
|
||||
C8D4AD1C137EE85F00CD506D /* libechoprint-codegen-ios.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libechoprint-codegen-ios.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C8D4AD1F137EE85F00CD506D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
C8D4AD23137EE85F00CD506D /* echoprint-codegen-ios-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "echoprint-codegen-ios-Prefix.pch"; sourceTree = "<group>"; };
|
||||
C8D4AD29137EF41100CD506D /* AudioBufferInput.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AudioBufferInput.cxx; path = ../../AudioBufferInput.cxx; sourceTree = "<group>"; };
|
||||
C8D4AD2A137EF41100CD506D /* AudioBufferInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioBufferInput.h; path = ../../AudioBufferInput.h; sourceTree = "<group>"; };
|
||||
C8D4AD2B137EF41100CD506D /* AudioStreamInput.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AudioStreamInput.cxx; path = ../../AudioStreamInput.cxx; sourceTree = "<group>"; };
|
||||
C8D4AD2C137EF41100CD506D /* AudioStreamInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioStreamInput.h; path = ../../AudioStreamInput.h; sourceTree = "<group>"; };
|
||||
C8D4AD2D137EF41100CD506D /* Base64.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Base64.cxx; path = ../../Base64.cxx; sourceTree = "<group>"; };
|
||||
C8D4AD2E137EF41100CD506D /* Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Base64.h; path = ../../Base64.h; sourceTree = "<group>"; };
|
||||
C8D4AD2F137EF41100CD506D /* Codegen.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Codegen.cxx; path = ../../Codegen.cxx; sourceTree = "<group>"; };
|
||||
C8D4AD30137EF41100CD506D /* Codegen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Codegen.h; path = ../../Codegen.h; sourceTree = "<group>"; };
|
||||
C8D4AD31137EF41100CD506D /* Common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Common.h; path = ../../Common.h; sourceTree = "<group>"; };
|
||||
C8D4AD40137EF41100CD506D /* File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = File.h; path = ../../File.h; sourceTree = "<group>"; };
|
||||
C8D4AD42137EF41100CD506D /* Fingerprint.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Fingerprint.cxx; path = ../../Fingerprint.cxx; sourceTree = "<group>"; };
|
||||
C8D4AD43137EF41100CD506D /* Fingerprint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Fingerprint.h; path = ../../Fingerprint.h; sourceTree = "<group>"; };
|
||||
C8D4AD44137EF41100CD506D /* MatrixUtility.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MatrixUtility.cxx; path = ../../MatrixUtility.cxx; sourceTree = "<group>"; };
|
||||
C8D4AD45137EF41100CD506D /* MatrixUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MatrixUtility.h; path = ../../MatrixUtility.h; sourceTree = "<group>"; };
|
||||
C8D4AD48137EF41100CD506D /* Params.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Params.h; path = ../../Params.h; sourceTree = "<group>"; };
|
||||
C8D4AD4D137EF41100CD506D /* win_funcs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = win_funcs.h; path = ../../win_funcs.h; sourceTree = "<group>"; };
|
||||
C8D4AD4E137EF41100CD506D /* win_unistd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = win_unistd.h; path = ../../win_unistd.h; sourceTree = "<group>"; };
|
||||
C8D4AD6F137EF5AC00CD506D /* echoprint-codegen-ios.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "echoprint-codegen-ios.xcconfig"; sourceTree = "<group>"; };
|
||||
C8D4AD70137EF68F00CD506D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
C8D4AD19137EE85F00CD506D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C8D4AD20137EE85F00CD506D /* Foundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
C8D4AD11137EE85F00CD506D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C8D4AD70137EF68F00CD506D /* README.md */,
|
||||
C8D4AD6F137EF5AC00CD506D /* echoprint-codegen-ios.xcconfig */,
|
||||
C8D4AD21137EE85F00CD506D /* echoprint-codegen-ios */,
|
||||
C8D4AD1E137EE85F00CD506D /* Frameworks */,
|
||||
C8D4AD1D137EE85F00CD506D /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C8D4AD1D137EE85F00CD506D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C8D4AD1C137EE85F00CD506D /* libechoprint-codegen-ios.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C8D4AD1E137EE85F00CD506D /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C8D4AD1F137EE85F00CD506D /* Foundation.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C8D4AD21137EE85F00CD506D /* echoprint-codegen-ios */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9AE5AA17139FB2620009FCD8 /* SubbandAnalysis.h */,
|
||||
9AE5AA18139FB2620009FCD8 /* SubbandAnalysis.cxx */,
|
||||
C8D4AD29137EF41100CD506D /* AudioBufferInput.cxx */,
|
||||
C8D4AD2A137EF41100CD506D /* AudioBufferInput.h */,
|
||||
C8D4AD2B137EF41100CD506D /* AudioStreamInput.cxx */,
|
||||
C8D4AD2C137EF41100CD506D /* AudioStreamInput.h */,
|
||||
C8D4AD2D137EF41100CD506D /* Base64.cxx */,
|
||||
C8D4AD2E137EF41100CD506D /* Base64.h */,
|
||||
C8D4AD2F137EF41100CD506D /* Codegen.cxx */,
|
||||
C8D4AD30137EF41100CD506D /* Codegen.h */,
|
||||
C8D4AD31137EF41100CD506D /* Common.h */,
|
||||
C8D4AD3A137EF41100CD506D /* fft */,
|
||||
C8D4AD40137EF41100CD506D /* File.h */,
|
||||
C8D4AD42137EF41100CD506D /* Fingerprint.cxx */,
|
||||
C8D4AD43137EF41100CD506D /* Fingerprint.h */,
|
||||
C8D4AD44137EF41100CD506D /* MatrixUtility.cxx */,
|
||||
C8D4AD45137EF41100CD506D /* MatrixUtility.h */,
|
||||
C8D4AD48137EF41100CD506D /* Params.h */,
|
||||
C8D4AD4D137EF41100CD506D /* win_funcs.h */,
|
||||
C8D4AD4E137EF41100CD506D /* win_unistd.h */,
|
||||
C8D4AD22137EE85F00CD506D /* Supporting Files */,
|
||||
);
|
||||
path = "echoprint-codegen-ios";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C8D4AD22137EE85F00CD506D /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C8D4AD23137EE85F00CD506D /* echoprint-codegen-ios-Prefix.pch */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C8D4AD3A137EF41100CD506D /* fft */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = fft;
|
||||
path = ../../fft;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
C8D4AD1A137EE85F00CD506D /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C8D4AD50137EF41100CD506D /* AudioBufferInput.h in Headers */,
|
||||
C8D4AD52137EF41100CD506D /* AudioStreamInput.h in Headers */,
|
||||
C8D4AD54137EF41100CD506D /* Base64.h in Headers */,
|
||||
C8D4AD56137EF41100CD506D /* Codegen.h in Headers */,
|
||||
C8D4AD57137EF41100CD506D /* Common.h in Headers */,
|
||||
C8D4AD60137EF41100CD506D /* File.h in Headers */,
|
||||
C8D4AD63137EF41100CD506D /* Fingerprint.h in Headers */,
|
||||
C8D4AD65137EF41100CD506D /* MatrixUtility.h in Headers */,
|
||||
C8D4AD68137EF41100CD506D /* Params.h in Headers */,
|
||||
C8D4AD6D137EF41100CD506D /* win_funcs.h in Headers */,
|
||||
C8D4AD6E137EF41100CD506D /* win_unistd.h in Headers */,
|
||||
9AE5AA19139FB2620009FCD8 /* SubbandAnalysis.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
C8D4AD1B137EE85F00CD506D /* echoprint-codegen-ios */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C8D4AD26137EE85F00CD506D /* Build configuration list for PBXNativeTarget "echoprint-codegen-ios" */;
|
||||
buildPhases = (
|
||||
C8D4AD18137EE85F00CD506D /* Sources */,
|
||||
C8D4AD19137EE85F00CD506D /* Frameworks */,
|
||||
C8D4AD1A137EE85F00CD506D /* Headers */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "echoprint-codegen-ios";
|
||||
productName = "echoprint-codegen-ios";
|
||||
productReference = C8D4AD1C137EE85F00CD506D /* libechoprint-codegen-ios.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
C8D4AD13137EE85F00CD506D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = C8D4AD16137EE85F00CD506D /* Build configuration list for PBXProject "echoprint-codegen-ios" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = C8D4AD11137EE85F00CD506D;
|
||||
productRefGroup = C8D4AD1D137EE85F00CD506D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
C8D4AD1B137EE85F00CD506D /* echoprint-codegen-ios */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
C8D4AD18137EE85F00CD506D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C8D4AD4F137EF41100CD506D /* AudioBufferInput.cxx in Sources */,
|
||||
C8D4AD51137EF41100CD506D /* AudioStreamInput.cxx in Sources */,
|
||||
C8D4AD53137EF41100CD506D /* Base64.cxx in Sources */,
|
||||
C8D4AD55137EF41100CD506D /* Codegen.cxx in Sources */,
|
||||
C8D4AD62137EF41100CD506D /* Fingerprint.cxx in Sources */,
|
||||
C8D4AD64137EF41100CD506D /* MatrixUtility.cxx in Sources */,
|
||||
9AE5AA1A139FB2620009FCD8 /* SubbandAnalysis.cxx in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
C8D4AD24137EE85F00CD506D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = C8D4AD6F137EF5AC00CD506D /* echoprint-codegen-ios.xcconfig */;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_VERSION = com.apple.compilers.llvmgcc42;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C8D4AD25137EE85F00CD506D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = C8D4AD6F137EF5AC00CD506D /* echoprint-codegen-ios.xcconfig */;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_VERSION = com.apple.compilers.llvmgcc42;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.2;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C8D4AD27137EE85F00CD506D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
DSTROOT = /tmp/echoprint_codegen_ios.dst;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "echoprint-codegen-ios/echoprint-codegen-ios-Prefix.pch";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C8D4AD28137EE85F00CD506D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
DSTROOT = /tmp/echoprint_codegen_ios.dst;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "echoprint-codegen-ios/echoprint-codegen-ios-Prefix.pch";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
C8D4AD16137EE85F00CD506D /* Build configuration list for PBXProject "echoprint-codegen-ios" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C8D4AD24137EE85F00CD506D /* Debug */,
|
||||
C8D4AD25137EE85F00CD506D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C8D4AD26137EE85F00CD506D /* Build configuration list for PBXNativeTarget "echoprint-codegen-ios" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C8D4AD27137EE85F00CD506D /* Debug */,
|
||||
C8D4AD28137EE85F00CD506D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = C8D4AD13137EE85F00CD506D /* Project object */;
|
||||
}
|
7
3rdparty/echoprint-codegen/src/echoprint-codegen-ios/echoprint-codegen-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
vendored
Normal file
7
3rdparty/echoprint-codegen/src/echoprint-codegen-ios/echoprint-codegen-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:echoprint-codegen-ios.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -0,0 +1,4 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'echoprint-codegen-ios' target in the 'echoprint-codegen-ios' project
|
||||
//
|
||||
|
298
3rdparty/echoprint-codegen/src/main.cxx
vendored
Normal file
298
3rdparty/echoprint-codegen/src/main.cxx
vendored
Normal file
@ -0,0 +1,298 @@
|
||||
//
|
||||
// echoprint-codegen
|
||||
// Copyright 2011 The Echo Nest Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <memory>
|
||||
#ifndef _WIN32
|
||||
#include <libgen.h>
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AudioStreamInput.h"
|
||||
#include "Metadata.h"
|
||||
#include "Codegen.h"
|
||||
#include <string>
|
||||
#define MAX_FILES 200000
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
// Struct to pass to the worker threads
|
||||
typedef struct {
|
||||
char *filename;
|
||||
int start_offset;
|
||||
int duration;
|
||||
int done ;
|
||||
int tag;
|
||||
char *output;
|
||||
} thread_parm_t;
|
||||
|
||||
// Thank you http://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#elif MACOS
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int getNumCores() {
|
||||
#ifdef WIN32
|
||||
SYSTEM_INFO sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
#elif MACOS
|
||||
int nm[2];
|
||||
size_t len = 4;
|
||||
uint32_t count;
|
||||
|
||||
nm[0] = CTL_HW; nm[1] = HW_AVAILCPU;
|
||||
sysctl(nm, 2, &count, &len, NULL, 0);
|
||||
|
||||
if(count < 1) {
|
||||
nm[1] = HW_NCPU;
|
||||
sysctl(nm, 2, &count, &len, NULL, 0);
|
||||
if(count < 1) { count = 1; }
|
||||
}
|
||||
return count;
|
||||
#else
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
// deal with quotes etc in json
|
||||
std::string escape(const string& value) {
|
||||
std::string s(value);
|
||||
std::string out = "";
|
||||
out.reserve(s.size());
|
||||
for (size_t i = 0; i < s.size(); i++) {
|
||||
char c = s[i];
|
||||
if (c <= 31)
|
||||
continue;
|
||||
|
||||
switch (c) {
|
||||
case '"' : out += "\\\""; break;
|
||||
case '\\': out += "\\\\"; break;
|
||||
case '\b': out += "\\b" ; break;
|
||||
case '\f': out += "\\f" ; break;
|
||||
case '\n': out += "\\n" ; break;
|
||||
case '\r': out += "\\r" ; break;
|
||||
case '\t': out += "\\t" ; break;
|
||||
// case '/' : out += "\\/" ; break; // Unnecessary?
|
||||
default:
|
||||
out += c;
|
||||
// TODO: do something with unicode?
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
char* json_string_for_file(char* filename, int start_offset, int duration, int tag) {
|
||||
// Given a filename, do all the work to get a JSON string output.
|
||||
// This is called by a thread
|
||||
double t1 = now();
|
||||
|
||||
auto_ptr<FfmpegStreamInput> pAudio(new FfmpegStreamInput());
|
||||
pAudio->ProcessFile(filename, start_offset, duration);
|
||||
|
||||
if (pAudio.get() == NULL) { // Unable to decode!
|
||||
char* output = (char*) malloc(16384);
|
||||
sprintf(output,"{\"error\":\"could not create decoder\", \"tag\":%d, \"metadata\":{\"filename\":\"%s\"}}",
|
||||
tag,
|
||||
escape(filename).c_str());
|
||||
return output;
|
||||
}
|
||||
|
||||
int numSamples = pAudio->getNumSamples();
|
||||
|
||||
if (numSamples < 1) {
|
||||
char* output = (char*) malloc(16384);
|
||||
sprintf(output,"{\"error\":\"could not decode\", \"tag\":%d, \"metadata\":{\"filename\":\"%s\"}}",
|
||||
tag,
|
||||
escape(filename).c_str());
|
||||
return output;
|
||||
}
|
||||
t1 = now() - t1;
|
||||
|
||||
double t2 = now();
|
||||
auto_ptr<Codegen> pCodegen(new Codegen(pAudio->getSamples(), numSamples, start_offset));
|
||||
t2 = now() - t2;
|
||||
|
||||
// Get the ID3 tag information.
|
||||
auto_ptr<Metadata> pMetadata(new Metadata(filename));
|
||||
|
||||
// preamble + codelen
|
||||
char* output = (char*) malloc(sizeof(char)*(16384 + strlen(pCodegen->getCodeString().c_str()) ));
|
||||
|
||||
sprintf(output,"{\"metadata\":{\"artist\":\"%s\", \"release\":\"%s\", \"title\":\"%s\", \"genre\":\"%s\", \"bitrate\":%d,"
|
||||
"\"sample_rate\":%d, \"duration\":%d, \"filename\":\"%s\", \"samples_decoded\":%d, \"given_duration\":%d,"
|
||||
" \"start_offset\":%d, \"version\":%2.2f, \"codegen_time\":%2.6f, \"decode_time\":%2.6f}, \"code_count\":%d,"
|
||||
" \"code\":\"%s\", \"tag\":%d}",
|
||||
escape(pMetadata->Artist()).c_str(),
|
||||
escape(pMetadata->Album()).c_str(),
|
||||
escape(pMetadata->Title()).c_str(),
|
||||
escape(pMetadata->Genre()).c_str(),
|
||||
pMetadata->Bitrate(),
|
||||
pMetadata->SampleRate(),
|
||||
pMetadata->Seconds(),
|
||||
escape(filename).c_str(),
|
||||
numSamples,
|
||||
duration,
|
||||
start_offset,
|
||||
pCodegen->getVersion(),
|
||||
t2,
|
||||
t1,
|
||||
pCodegen->getNumCodes(),
|
||||
pCodegen->getCodeString().c_str(),
|
||||
tag
|
||||
);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
void *threaded_json_string_for_file(void *parm) {
|
||||
// pthread stub to invoke json_string_for_file
|
||||
thread_parm_t *p = (thread_parm_t *)parm;
|
||||
char * output = json_string_for_file(p->filename, p->start_offset, p->duration, p->tag);
|
||||
p->output = output;
|
||||
// mark when we're done so the controlling thread can move on.
|
||||
p->done = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void print_json_to_screen(char* output, int count, int done) {
|
||||
// Print a json block depending on how many there are and where we are.
|
||||
if(done==1 && count>1) printf("[\n%s,\n", output);
|
||||
else if(done==1 && count == 1) printf("[\n%s\n]\n", output);
|
||||
else if(done == count) printf("%s\n]\n", output);
|
||||
else printf("%s,\n", output);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s [ filename | -s ] [seconds_start] [seconds_duration] [< file_list (if -s is set)]\n", argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
try {
|
||||
string files[MAX_FILES];
|
||||
char *filename = argv[1];
|
||||
int count = 0;
|
||||
int start_offset = 0;
|
||||
int duration = 0;
|
||||
int already = 0;
|
||||
if (argc > 2) start_offset = atoi(argv[2]);
|
||||
if (argc > 3) duration = atoi(argv[3]);
|
||||
if (argc > 4) already = atoi(argv[4]);
|
||||
// If you give it -s, it means to read in a list of files from stdin.
|
||||
if (strcmp(filename, "-s") == 0) {
|
||||
while(cin) {
|
||||
if (count < MAX_FILES) {
|
||||
string temp_str;
|
||||
getline(cin, temp_str);
|
||||
if (temp_str.size() > 2)
|
||||
files[count++] = temp_str;
|
||||
} else {
|
||||
throw std::runtime_error("Too many files on stdin to process\n");
|
||||
}
|
||||
}
|
||||
} else files[count++] = filename;
|
||||
|
||||
if(count == 0) throw std::runtime_error("No files given.\n");
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
//#ifdef __APPLE__
|
||||
fprintf(stderr, "no thread mode\n");
|
||||
// Threading doesn't work in windows yet.
|
||||
for(int i=0;i<count;i++) {
|
||||
char* output = json_string_for_file((char*)files[i].c_str(), start_offset, duration, i);
|
||||
print_json_to_screen(output, count, i);
|
||||
free(output);
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
// FIgure out how many threads to use based on # of cores
|
||||
int num_threads = getNumCores();
|
||||
if (num_threads > 8) num_threads = 8;
|
||||
if (num_threads < 2) num_threads = 2;
|
||||
if (num_threads > count) num_threads = count;
|
||||
|
||||
// Setup threading
|
||||
pthread_t *t = (pthread_t*)malloc(sizeof(pthread_t)*num_threads);
|
||||
thread_parm_t **parm = (thread_parm_t**)malloc(sizeof(thread_parm_t*)*num_threads);
|
||||
pthread_attr_t *attr = (pthread_attr_t*)malloc(sizeof(pthread_attr_t)*num_threads);
|
||||
|
||||
// Kick off the first N threads
|
||||
int still_left = count-1-already;
|
||||
for(int i=0;i<num_threads;i++) {
|
||||
parm[i] = (thread_parm_t *)malloc(sizeof(thread_parm_t));
|
||||
parm[i]->filename = (char*)files[still_left].c_str();
|
||||
parm[i]->start_offset = start_offset;
|
||||
parm[i]->tag = still_left;
|
||||
parm[i]->duration = duration;
|
||||
parm[i]->done = 0;
|
||||
still_left--;
|
||||
pthread_attr_init(&attr[i]);
|
||||
pthread_attr_setdetachstate(&attr[i], PTHREAD_CREATE_DETACHED);
|
||||
// Kick off the thread
|
||||
if (pthread_create(&t[i], &attr[i], threaded_json_string_for_file, (void*)parm[i]))
|
||||
throw std::runtime_error("Problem creating thread\n");
|
||||
}
|
||||
|
||||
int done = 0;
|
||||
// Now wait for the threads to come back, and also kick off new ones
|
||||
while(done<count) {
|
||||
// Check which threads are done
|
||||
for(int i=0;i<num_threads;i++) {
|
||||
if (parm[i]->done) {
|
||||
parm[i]->done = 0;
|
||||
done++;
|
||||
print_json_to_screen(parm[i]->output, count, done);
|
||||
free(parm[i]->output);
|
||||
// More to do? Start a new one on this just finished thread
|
||||
if(still_left >= 0) {
|
||||
parm[i]->tag = still_left;
|
||||
parm[i]->filename = (char*)files[still_left].c_str();
|
||||
still_left--;
|
||||
int err= pthread_create(&t[i], &attr[i], threaded_json_string_for_file, (void*)parm[i]);
|
||||
if(err)
|
||||
throw std::runtime_error("Problem creating thread\n");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up threads
|
||||
for(int i=0;i<num_threads;i++) {
|
||||
free(parm[i]);
|
||||
}
|
||||
free(t);
|
||||
free(parm);
|
||||
free(attr);
|
||||
return 0;
|
||||
}
|
||||
catch(std::runtime_error& ex) {
|
||||
fprintf(stderr, "%s\n", ex.what());
|
||||
return 1;
|
||||
}
|
||||
catch(...) {
|
||||
fprintf(stderr, "Unknown failure occurred\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
}
|
34
3rdparty/echoprint-codegen/src/win_funcs.h
vendored
Normal file
34
3rdparty/echoprint-codegen/src/win_funcs.h
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef __WIN_FUNCS_H
|
||||
#define __WIN_FUNCS_H 1
|
||||
|
||||
#include <math.h>
|
||||
#include <winsock.h>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
#define ROUND_FUNC(type,suff) inline type round##suff(type x) \
|
||||
{ \
|
||||
if (x >= 0.0##suff){ \
|
||||
type y = floor##suff(x); \
|
||||
if (x - y >= 0.5##suff) \
|
||||
y += 1.0##suff; \
|
||||
return y; \
|
||||
}else{ \
|
||||
type y = ceil##suff(x); \
|
||||
if (y - x >= 0.5##suff) \
|
||||
y -= 1.0##suff; \
|
||||
return y; \
|
||||
} \
|
||||
}
|
||||
|
||||
ROUND_FUNC(float,f)
|
||||
ROUND_FUNC(double,)
|
||||
|
||||
|
||||
inline void gettimeofday(struct timeval* t,void* timezone)
|
||||
{ struct _timeb timebuffer;
|
||||
_ftime( &timebuffer );
|
||||
t->tv_sec=timebuffer.time;
|
||||
t->tv_usec=1000*timebuffer.millitm;
|
||||
}
|
||||
|
||||
#endif /* __WIN_FUNCS_H */
|
36
3rdparty/echoprint-codegen/src/win_unistd.h
vendored
Normal file
36
3rdparty/echoprint-codegen/src/win_unistd.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef _UNISTD_H
|
||||
#define _UNISTD_H 1
|
||||
|
||||
/* This file intended to serve as a drop-in replacement for
|
||||
* unistd.h on Windows
|
||||
* Please add functionality as neeeded
|
||||
http://stackoverflow.com/questions/341817/is-there-a-replacement-for-unistd-h-for-windows-visual-c
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <io.h>
|
||||
/*
|
||||
#include <getopt.h>
|
||||
|
||||
getopt from: http://www.pwilson.net/sample.html. */
|
||||
|
||||
#define srandom srand
|
||||
#define random rand
|
||||
|
||||
const int F_OK = 0;
|
||||
|
||||
const int W_OK = 2;
|
||||
const int R_OK = 4;
|
||||
|
||||
#define access _access
|
||||
#define ftruncate _chsize
|
||||
|
||||
/* stdio */
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
/* float.h */
|
||||
#define finite _finite
|
||||
|
||||
#define ssize_t int
|
||||
|
||||
#endif /* unistd.h */
|
@ -365,6 +365,7 @@ add_subdirectory(src)
|
||||
if (WIN32)
|
||||
add_subdirectory(3rdparty/qtwin)
|
||||
endif (WIN32)
|
||||
add_subdirectory(3rdparty/echoprint-codegen)
|
||||
add_subdirectory(3rdparty/universalchardet)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(dist)
|
||||
|
@ -130,6 +130,7 @@ set(SOURCES
|
||||
library/librarywatcher.cpp
|
||||
library/sqlrow.cpp
|
||||
|
||||
musicbrainz/echoprinter.cpp
|
||||
musicbrainz/fingerprinter.cpp
|
||||
musicbrainz/musicbrainzclient.cpp
|
||||
musicbrainz/musicdnsclient.cpp
|
||||
@ -989,6 +990,7 @@ add_dependencies(clementine_lib pot)
|
||||
|
||||
target_link_libraries(clementine_lib
|
||||
chardet
|
||||
echoprint
|
||||
${ECHONEST_LIBRARIES}
|
||||
${GOBJECT_LIBRARIES}
|
||||
${GLIB_LIBRARIES}
|
||||
|
213
src/musicbrainz/echoprinter.cpp
Normal file
213
src/musicbrainz/echoprinter.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "echoprinter.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/timeconstants.h"
|
||||
|
||||
#include "3rdparty/echoprint-codegen/src/Codegen.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QEventLoop>
|
||||
#include <QtDebug>
|
||||
#include <QTime>
|
||||
|
||||
Echoprinter::Echoprinter(const QString& filename)
|
||||
: filename_(filename),
|
||||
event_loop_(NULL),
|
||||
convert_element_(NULL),
|
||||
finishing_(false)
|
||||
{
|
||||
buffer_.open(QIODevice::WriteOnly);
|
||||
}
|
||||
|
||||
Echoprinter::~Echoprinter() {
|
||||
}
|
||||
|
||||
GstElement* Echoprinter::CreateElement(const QString &factory_name,
|
||||
GstElement *bin) {
|
||||
GstElement* ret = gst_element_factory_make(
|
||||
factory_name.toAscii().constData(),
|
||||
factory_name.toAscii().constData());
|
||||
|
||||
if (ret && bin)
|
||||
gst_bin_add(GST_BIN(bin), ret);
|
||||
|
||||
if (!ret) {
|
||||
qLog(Warning) << "Couldn't create the gstreamer element" << factory_name;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString Echoprinter::CreateFingerprint() {
|
||||
GMainContext* context = g_main_context_new();
|
||||
g_main_context_push_thread_default(context);
|
||||
event_loop_ = g_main_loop_new(context, FALSE);
|
||||
|
||||
pipeline_ = gst_pipeline_new("pipeline");
|
||||
GstElement* src = CreateElement("filesrc", pipeline_);
|
||||
GstElement* decode = CreateElement("decodebin2", pipeline_);
|
||||
GstElement* convert = CreateElement("audioconvert", pipeline_);
|
||||
GstElement* resample = CreateElement("audioresample", pipeline_);
|
||||
GstElement* sink = CreateElement("appsink", pipeline_);
|
||||
|
||||
if (!src || !decode || !convert || !resample || !sink) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
convert_element_ = convert;
|
||||
|
||||
// Connect the elements
|
||||
gst_element_link_many(src, decode, NULL);
|
||||
gst_element_link_many(convert, resample, NULL);
|
||||
|
||||
// Echoprint expects mono floats at a sample rate of 11025Hz.
|
||||
GstCaps* caps = gst_caps_new_simple(
|
||||
"audio/x-raw-float",
|
||||
"width", G_TYPE_INT, 32,
|
||||
"channels", G_TYPE_INT, 1,
|
||||
"rate", G_TYPE_INT, 11025,
|
||||
NULL);
|
||||
gst_element_link_filtered(resample, sink, caps);
|
||||
gst_caps_unref(caps);
|
||||
|
||||
GstAppSinkCallbacks callbacks;
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.new_buffer = NewBufferCallback;
|
||||
gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(sink), &callbacks, this, NULL);
|
||||
g_object_set(G_OBJECT(sink), "sync", FALSE, NULL);
|
||||
g_object_set(G_OBJECT(sink), "emit-signals", TRUE, NULL);
|
||||
|
||||
// Set the filename
|
||||
g_object_set(src, "location", filename_.toLocal8Bit().constData(), NULL);
|
||||
|
||||
// Connect signals
|
||||
g_signal_connect(decode, "new-decoded-pad", G_CALLBACK(NewPadCallback), this);
|
||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
|
||||
guint bus_callback_id = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
|
||||
|
||||
QTime time;
|
||||
time.start();
|
||||
|
||||
// Start playing
|
||||
gst_element_set_state(pipeline_, GST_STATE_PLAYING);
|
||||
|
||||
g_main_loop_run(event_loop_);
|
||||
g_main_loop_unref(event_loop_);
|
||||
g_main_context_unref(context);
|
||||
|
||||
int decode_time = time.restart();
|
||||
|
||||
buffer_.close();
|
||||
QByteArray data = buffer_.data();
|
||||
Codegen codegen(reinterpret_cast<const float*>(data.constData()),
|
||||
data.size() / sizeof(float), 0);
|
||||
QString fingerprint = QString::fromAscii(codegen.getCodeString().c_str());
|
||||
int codegen_time = time.elapsed();
|
||||
|
||||
qLog(Debug) << "Decode time:" << decode_time << "Codegen time:" << codegen_time;
|
||||
|
||||
qLog(Debug) << "Echoprint:" << fingerprint;
|
||||
|
||||
// Cleanup
|
||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL);
|
||||
g_source_remove(bus_callback_id);
|
||||
gst_object_unref(pipeline_);
|
||||
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
void Echoprinter::NewPadCallback(GstElement*, GstPad* pad, gboolean, gpointer data) {
|
||||
Echoprinter* instance = reinterpret_cast<Echoprinter*>(data);
|
||||
GstPad* const audiopad = gst_element_get_pad(instance->convert_element_, "sink");
|
||||
|
||||
if (GST_PAD_IS_LINKED(audiopad)) {
|
||||
qLog(Warning) << "audiopad is already linked, unlinking old pad";
|
||||
gst_pad_unlink(audiopad, GST_PAD_PEER(audiopad));
|
||||
}
|
||||
|
||||
gst_pad_link(pad, audiopad);
|
||||
gst_object_unref(audiopad);
|
||||
}
|
||||
|
||||
void Echoprinter::ReportError(GstMessage* msg) {
|
||||
GError* error;
|
||||
gchar* debugs;
|
||||
|
||||
gst_message_parse_error(msg, &error, &debugs);
|
||||
QString message = QString::fromLocal8Bit(error->message);
|
||||
|
||||
g_error_free(error);
|
||||
free(debugs);
|
||||
|
||||
qLog(Error) << "Error processing" << filename_ << ":" << message;
|
||||
}
|
||||
|
||||
gboolean Echoprinter::BusCallback(GstBus*, GstMessage* msg, gpointer data) {
|
||||
Echoprinter* instance = reinterpret_cast<Echoprinter*>(data);
|
||||
|
||||
switch (GST_MESSAGE_TYPE(msg)) {
|
||||
case GST_MESSAGE_ERROR:
|
||||
instance->ReportError(msg);
|
||||
g_main_loop_quit(instance->event_loop_);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return GST_BUS_DROP;
|
||||
}
|
||||
|
||||
GstBusSyncReply Echoprinter::BusCallbackSync(GstBus*, GstMessage* msg, gpointer data) {
|
||||
Echoprinter* instance = reinterpret_cast<Echoprinter*>(data);
|
||||
|
||||
switch (GST_MESSAGE_TYPE(msg)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
g_main_loop_quit(instance->event_loop_);
|
||||
break;
|
||||
|
||||
case GST_MESSAGE_ERROR:
|
||||
instance->ReportError(msg);
|
||||
g_main_loop_quit(instance->event_loop_);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return GST_BUS_PASS;
|
||||
}
|
||||
|
||||
GstFlowReturn Echoprinter::NewBufferCallback(GstAppSink* app_sink, gpointer self) {
|
||||
Echoprinter* me = reinterpret_cast<Echoprinter*>(self);
|
||||
if (me->finishing_) {
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
GstBuffer* buffer = gst_app_sink_pull_buffer(app_sink);
|
||||
me->buffer_.write(reinterpret_cast<const char*>(buffer->data), buffer->size);
|
||||
gst_buffer_unref(buffer);
|
||||
|
||||
gint64 pos = 0;
|
||||
GstFormat format = GST_FORMAT_TIME;
|
||||
gboolean ret = gst_element_query_position(me->pipeline_, &format, &pos);
|
||||
if (ret && pos > 30 * kNsecPerSec) {
|
||||
me->finishing_ = true;
|
||||
g_main_loop_quit(me->event_loop_);
|
||||
}
|
||||
return GST_FLOW_OK;
|
||||
}
|
68
src/musicbrainz/echoprinter.h
Normal file
68
src/musicbrainz/echoprinter.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ECHOPRINTER_H
|
||||
#define ECHOPRINTER_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/app/gstappsink.h>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QString>
|
||||
|
||||
class QEventLoop;
|
||||
|
||||
class Echoprinter {
|
||||
// Creates an Echoprint fingerprint from a song.
|
||||
// Uses GStreamer to open and decode the file as PCM data and passes this
|
||||
// to Echoprint's code generator. The generated code can be used to identify
|
||||
// a song via Echonest's identify method.
|
||||
// You should create one Echoprinter for each file you want to fingerprint.
|
||||
// This class works well with QtConcurrentMap.
|
||||
|
||||
public:
|
||||
Echoprinter(const QString& filename);
|
||||
~Echoprinter();
|
||||
|
||||
// Creates a fingerprint from the song. This method is blocking, so you want
|
||||
// to call it in another thread. Returns an empty string if no fingerprint
|
||||
// could be created.
|
||||
QString CreateFingerprint();
|
||||
|
||||
private:
|
||||
GstElement* CreateElement(const QString& factory_name, GstElement* bin = NULL);
|
||||
|
||||
void ReportError(GstMessage* message);
|
||||
|
||||
static void NewPadCallback(GstElement*, GstPad* pad, gboolean, gpointer data);
|
||||
static gboolean BusCallback(GstBus*, GstMessage* msg, gpointer data);
|
||||
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage* msg, gpointer data);
|
||||
static GstFlowReturn NewBufferCallback(GstAppSink* app_sink, gpointer self);
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
GMainLoop* event_loop_;
|
||||
|
||||
GstElement* convert_element_;
|
||||
GstElement* pipeline_;
|
||||
|
||||
QBuffer buffer_;
|
||||
bool finishing_;
|
||||
};
|
||||
|
||||
#endif // ECHOPRINTER_H
|
Loading…
x
Reference in New Issue
Block a user