Bump echoprint version to 5d9847d86e513ca943e7
This commit is contained in:
parent
c7fef47e61
commit
55b001c400
|
@ -1,8 +1,26 @@
|
|||
echoprint-codegen was written by
|
||||
See the LICENSE file for important license information.
|
||||
|
||||
Whitening, SubbandAnalysis, Fingerprint
|
||||
Dan Ellis <dpwe@ee.columbia.edu>
|
||||
Brian Whitman <brian@echonest.com>
|
||||
|
||||
AudioBufferInput, AudioStreamInput, Codegen, Common, File, MatrixUtility, Metadata
|
||||
Tristan Jehan <tristan@echonest.com>
|
||||
Paul Lamere <paul@echonest.com>
|
||||
Jason Sundram <jsundram@gmail.com>
|
||||
Brian Whitman <brian@echonest.com>
|
||||
|
||||
Murmurhash2
|
||||
Austin Appleby
|
||||
|
||||
Base64
|
||||
Rene Nyffenegger
|
||||
|
||||
Contributors
|
||||
Alastair Porter <alastair@porter.net.nz>
|
||||
|
||||
and is Copyright 2011 The Echo Nest Corporation.
|
||||
efsavage
|
||||
alsuren
|
||||
artgillespie
|
||||
yhorng
|
||||
divan
|
||||
|
||||
|
|
|
@ -10,15 +10,25 @@ There are two modes of operation of the Echoprint codegen:
|
|||
|
||||
2. the codegen binary runs standalone, accepts filenames as inputs and runs in a multithreaded worker mode.
|
||||
|
||||
## Requirements for libcodegen
|
||||
## Requirements
|
||||
|
||||
### For libcodegen
|
||||
|
||||
* Boost >= 1.35
|
||||
* zlib
|
||||
|
||||
## Additional requirements for the codegen binary
|
||||
### 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
|
||||
|
||||
On Ubuntu or Debian you can install these dependencies with:
|
||||
|
||||
sudo apt-get install ffmpeg libboost1.42-dev libtag1-dev zlib1g-dev
|
||||
On OS-X with homebrew you can use:
|
||||
|
||||
brew install ffmpeg boost taglib
|
||||
|
||||
## Notes about libcodegen:
|
||||
|
||||
Code generation takes a buffer of floating point PCM data sampled at 11025 Hz and mono.
|
||||
|
|
|
@ -18,11 +18,12 @@ void AudioBufferInput::SetBuffer(const float* pBuffer, uint numSamples) {
|
|||
memcpy(_pSamples, pBuffer, numSamples*sizeof(float));
|
||||
}
|
||||
|
||||
void AudioBufferInput::SaveBuffer(const char*filename) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,27 +10,21 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#define POPEN_MODE "r"
|
||||
#else
|
||||
#include "win_unistd.h"
|
||||
#include <winsock.h>
|
||||
#define POPEN_MODE "rb"
|
||||
#else
|
||||
#define POPEN_MODE "r"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
#include "win_unistd.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "AudioStreamInput.h"
|
||||
#include "Common.h"
|
||||
#include "Params.h"
|
||||
|
||||
using std::string;
|
||||
using std::string;
|
||||
|
||||
namespace FFMPEG {
|
||||
// Do we think FFmpeg will read this as an audio file?
|
||||
|
@ -41,7 +35,7 @@ namespace FFMPEG {
|
|||
if (File::ends_with(pFileName, supportedExtensions[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,20 +54,20 @@ AudioStreamInput::~AudioStreamInput() {
|
|||
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)
|
||||
if (ok)
|
||||
{
|
||||
bool did_work = ProcessFilePointer(fp);
|
||||
bool succeeded = !pclose(fp);
|
||||
ok = did_work && succeeded;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "AudioStreamInput::ProcessFile can't open %s\n", filename);
|
||||
|
||||
return ok;
|
||||
|
@ -83,16 +77,16 @@ bool AudioStreamInput::ProcessFile(const char* filename, int offset_s/*=0*/, int
|
|||
bool AudioStreamInput::ProcessRawFile(const char* rawFilename) {
|
||||
FILE* fp = fopen(rawFilename, "r"); // TODO: Windows
|
||||
bool ok = (fp != NULL);
|
||||
if (ok)
|
||||
if (ok)
|
||||
{
|
||||
ok = ProcessFilePointer(fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
// reads raw signed 16-bit shorts from stdin, for example:
|
||||
// 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.
|
||||
|
@ -114,7 +108,7 @@ bool AudioStreamInput::ProcessFilePointer(FILE* pFile) {
|
|||
uint sampleCounter = 0;
|
||||
_pSamples = new float[_NumberSamples];
|
||||
uint samplesLeft = _NumberSamples;
|
||||
for (uint i = 0; i < vChunks.size(); i++)
|
||||
for (uint i = 0; i < vChunks.size(); i++)
|
||||
{
|
||||
short* pChunk = vChunks[i];
|
||||
uint numSamples = samplesLeft < nSamplesPerChunk ? samplesLeft : nSamplesPerChunk;
|
||||
|
@ -134,3 +128,5 @@ bool AudioStreamInput::ProcessFilePointer(FILE* pFile) {
|
|||
perror("ProcessFilePointer error");
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,20 +20,20 @@
|
|||
class AudioStreamInput {
|
||||
public:
|
||||
AudioStreamInput();
|
||||
virtual ~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;}
|
||||
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;
|
||||
|
@ -44,19 +44,19 @@ protected:
|
|||
};
|
||||
|
||||
class StdinStreamInput : public AudioStreamInput {
|
||||
public:
|
||||
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();}
|
||||
bool ProcessFile(const char* filename){ return ProcessStandardInput();}
|
||||
virtual std::string GetCommandLine(const char* filename){return "";} // hack
|
||||
};
|
||||
|
||||
class FfmpegStreamInput : public AudioStreamInput {
|
||||
public:
|
||||
public:
|
||||
std::string GetName(){return "ffmpeg";};
|
||||
protected:
|
||||
std::string GetCommandLine(const char* filename) {
|
||||
std::string GetCommandLine(const char* filename) {
|
||||
// TODO: Windows
|
||||
char message[4096] = {0};
|
||||
if (_Offset_s == 0 and _Seconds == 0)
|
||||
|
@ -65,8 +65,7 @@ protected:
|
|||
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);
|
||||
}
|
||||
};
|
||||
|
@ -76,12 +75,12 @@ namespace FFMPEG {
|
|||
};
|
||||
|
||||
class Mpg123StreamInput : public AudioStreamInput {
|
||||
public:
|
||||
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) {
|
||||
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\"",
|
||||
|
@ -94,3 +93,5 @@ protected:
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
|
@ -28,12 +28,12 @@
|
|||
#include "Base64.h"
|
||||
#include <iostream>
|
||||
|
||||
static const std::string base64_chars =
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
static const std::string base64_chars_url =
|
||||
static const std::string base64_chars_url =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789-_";
|
||||
|
@ -67,7 +67,7 @@ std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_
|
|||
if (url)
|
||||
ret += base64_chars_url[char_array_4[i]];
|
||||
else
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_
|
|||
if (url)
|
||||
ret += base64_chars_url[char_array_4[j]];
|
||||
else
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
}
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
|
@ -137,4 +137,5 @@ std::string base64_decode(std::string const& encoded_string) {
|
|||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,4 +6,5 @@
|
|||
std::string base64_encode(unsigned char const* , unsigned int len, bool url);
|
||||
std::string base64_decode(std::string const& s);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -7,21 +7,33 @@
|
|||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include "Codegen.h"
|
||||
#include "AudioBufferInput.h"
|
||||
#include "Fingerprint.h"
|
||||
#include "Whitening.h"
|
||||
#include "SubbandAnalysis.h"
|
||||
#include "Fingerprint.h"
|
||||
#include "Common.h"
|
||||
|
||||
#include "Base64.h"
|
||||
#include <zlib.h>
|
||||
|
||||
#define VERSION 4.11
|
||||
|
||||
using namespace std;
|
||||
|
||||
float Codegen::getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
|
@ -30,7 +42,7 @@ Codegen::Codegen(const float* pcm, uint numSamples, int start_offset) {
|
|||
|
||||
Fingerprint *pFingerprint = new Fingerprint(pSubbandAnalysis, start_offset);
|
||||
pFingerprint->Compute();
|
||||
|
||||
|
||||
_CodeString = createCodeString(pFingerprint->getCodes());
|
||||
_NumCodes = pFingerprint->getCodes().size();
|
||||
|
||||
|
@ -48,7 +60,7 @@ string Codegen::createCodeString(vector<FPCode> vCodes) {
|
|||
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;
|
||||
|
|
|
@ -8,17 +8,10 @@
|
|||
#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;
|
||||
#include <vector>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifdef CODEGEN_EXPORTS
|
||||
|
@ -33,20 +26,22 @@ using namespace std;
|
|||
#endif
|
||||
|
||||
class Fingerprint;
|
||||
class SubbandAnalysis;
|
||||
struct FPCode;
|
||||
|
||||
class CODEGEN_API Codegen {
|
||||
public:
|
||||
Codegen(const float* pcm, uint numSamples, int start_offset);
|
||||
Codegen(const float* pcm, unsigned int numSamples, int start_offset);
|
||||
|
||||
string getCodeString(){return _CodeString;}
|
||||
std::string getCodeString(){return _CodeString;}
|
||||
int getNumCodes(){return _NumCodes;}
|
||||
float getVersion() { return VERSION; }
|
||||
static float getVersion();
|
||||
private:
|
||||
Fingerprint* computeFingerprint(SubbandAnalysis *pSubbandAnalysis, int start_offset);
|
||||
string createCodeString(vector<FPCode> vCodes);
|
||||
std::string createCodeString(std::vector<FPCode> vCodes);
|
||||
|
||||
string compress(const string& s);
|
||||
string _CodeString;
|
||||
std::string compress(const std::string& s);
|
||||
std::string _CodeString;
|
||||
int _NumCodes;
|
||||
};
|
||||
|
||||
|
|
|
@ -11,17 +11,17 @@
|
|||
|
||||
#include <assert.h>
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#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
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <float.h>
|
||||
|
@ -43,13 +43,13 @@ static inline double now (void) {
|
|||
return now;
|
||||
}
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned int uint;
|
||||
#define NELEM(array) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
#ifndef _WIN32
|
||||
#define EN_ARRAY(type,var,size) type var[size]
|
||||
#define EN_ARRAY(type,var,size) type var[size]
|
||||
#else
|
||||
#define EN_ARRAY(type,var,size) type* var = (type*) _alloca((size)*sizeof(type))
|
||||
#define EN_ARRAY(type,var,size) type* var = (type*) _alloca((size)*sizeof(type))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,14 +7,9 @@
|
|||
#ifndef FILE_H
|
||||
#define FILE_H
|
||||
#include <string.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
#ifdef _WIN32
|
||||
#include "win_unistd.h"
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <io.h> // For F_OK
|
||||
#endif
|
||||
|
||||
/*
|
||||
This makes file writing a bit easier (makes sure we don't forget to fclose, basically). Use it like this:
|
||||
|
||||
|
|
|
@ -11,40 +11,40 @@
|
|||
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;
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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)
|
||||
Fingerprint::Fingerprint(SubbandAnalysis* pSubbandAnalysis, int offset)
|
||||
: _pSubbandAnalysis(pSubbandAnalysis), _Offset(offset) { }
|
||||
|
||||
|
||||
|
@ -69,19 +69,20 @@ uint Fingerprint::adaptiveOnsets(int ttarg, matrix_u&out, uint*&onset_counter_fo
|
|||
|
||||
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(uint r=0;r<Eb.size1();r++) for(uint c=0;c<Eb.size2();c++) Eb(r,c) = 0.0;
|
||||
|
||||
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();
|
||||
|
||||
frames = Eb.size1();
|
||||
bands = Eb.size2();
|
||||
pE = &Eb.data()[0];
|
||||
|
||||
out = matrix_u(SUBBANDS, frames);
|
||||
out = matrix_u(SUBBANDS, frames);
|
||||
onset_counter_for_band = new uint[SUBBANDS];
|
||||
|
||||
double bn[] = {0.1883, 0.4230, 0.3392}; /* preemph filter */ // new
|
||||
|
@ -97,13 +98,13 @@ uint Fingerprint::adaptiveOnsets(int ttarg, matrix_u&out, uint*&onset_counter_fo
|
|||
contact[j] = 0;
|
||||
lcontact[j] = 0;
|
||||
tsince[j] = 0;
|
||||
Y0[j] = 0;
|
||||
Y0[j] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (j = 0; j < SUBBANDS; ++j) {
|
||||
for (j = 0; j < SUBBANDS; ++j) {
|
||||
|
||||
double xn = 0;
|
||||
double xn = 0;
|
||||
/* calculate the filter - FIR part */
|
||||
if (i >= 2*nbn) {
|
||||
for (int k = 0; k < nbn; ++k) {
|
||||
|
@ -117,21 +118,21 @@ uint Fingerprint::adaptiveOnsets(int ttarg, matrix_u&out, uint*&onset_counter_fo
|
|||
|
||||
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 */
|
||||
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]);
|
||||
}
|
||||
} else {
|
||||
/* apply decays */
|
||||
H[j] = H[j] * exp(-1.0/(double)taus[j]);
|
||||
}
|
||||
|
||||
if (contact[j] == 0 && lcontact[j] == 1) {
|
||||
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
|
||||
|
@ -140,23 +141,23 @@ uint Fingerprint::adaptiveOnsets(int ttarg, matrix_u&out, uint*&onset_counter_fo
|
|||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
|
@ -184,15 +185,15 @@ void Fingerprint::Compute() {
|
|||
uint onset_count = adaptiveOnsets(345, out, onset_counter_for_band);
|
||||
_Codes.resize(onset_count*6);
|
||||
|
||||
for(unsigned char band=0;band<SUBBANDS;band++) {
|
||||
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));
|
||||
|
@ -230,8 +231,8 @@ void Fingerprint::Compute() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
_Codes.resize(actual_codes);
|
||||
|
||||
_Codes.resize(actual_codes);
|
||||
delete [] onset_counter_for_band;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
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)
|
||||
|
@ -19,15 +18,19 @@ MODULES_LIB = \
|
|||
Whitening.o
|
||||
MODULES = $(MODULES_LIB) Metadata.o
|
||||
|
||||
main: $(MODULES) main.o
|
||||
$(CXX) $(MODULES) $(LDFLAGS) main.o -o ../echoprint-codegen
|
||||
all: libcodegen echoprint-codegen
|
||||
|
||||
libcodegen: $(MODULES_LIB)
|
||||
$(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
|
||||
-current_version 4.1.1 -o libcodegen.4.1.1.dylib -undefined suppress \
|
||||
$(MODULES_LIB) -framework vecLib -framework Accelerate
|
||||
endif
|
||||
|
||||
echoprint-codegen: $(MODULES) main.o
|
||||
$(CXX) $(MODULES) $(LDFLAGS) main.o -o ../echoprint-codegen
|
||||
|
||||
%.o: %.c %.h
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
|
@ -46,3 +49,13 @@ clean:
|
|||
ifeq ($(UNAME),Darwin)
|
||||
rm -f *.dylib
|
||||
endif
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
# todo: dylib
|
||||
install: all
|
||||
install ../echoprint-codegen $(PREFIX)/bin
|
||||
install -d $(PREFIX)/include/echoprint
|
||||
install -m 644 Codegen.h $(PREFIX)/include/echoprint
|
||||
install -m 644 libcodegen.so $(PREFIX)/lib
|
||||
|
||||
.PHONY: clean all libcodegen echoprint-codegen install
|
|
@ -17,7 +17,7 @@ bool TextFileOutput(const matrix_f& A, const char* filename) {
|
|||
if (success) {
|
||||
const float *d = &A.data()[0];
|
||||
for (uint i = 0; i < A.size1(); i++) {
|
||||
for (uint j = 0; j < A.size2(); j++)
|
||||
for (uint j = 0; j < A.size2(); j++)
|
||||
fprintf(matrix_file, "%2.3f ", d[i*A.size2() + j]);
|
||||
|
||||
fprintf(matrix_file, "\n");
|
||||
|
@ -31,11 +31,11 @@ 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 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);
|
||||
|
@ -47,8 +47,6 @@ bool FileOutput(const matrix_f& A, const char* filename) {
|
|||
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
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
#define MATRIXUTILITY_H
|
||||
|
||||
#include "Common.h"
|
||||
#include <boost/numeric/ublas/matrix.hpp>
|
||||
#include <boost/numeric/ublas/matrix.hpp>
|
||||
#include <boost/numeric/ublas/matrix_proxy.hpp>
|
||||
|
||||
namespace ublas = boost::numeric::ublas;
|
||||
namespace ublas = boost::numeric::ublas;
|
||||
|
||||
typedef ublas::matrix<float> matrix_f;
|
||||
typedef ublas::matrix<uint> matrix_u;
|
||||
|
@ -27,6 +27,5 @@ namespace MatrixUtility {
|
|||
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
|
||||
|
|
|
@ -13,7 +13,7 @@ Metadata::Metadata(const string& file) : _Filename(file), _Artist(""), _Album(""
|
|||
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();
|
||||
|
@ -21,7 +21,7 @@ Metadata::Metadata(const string& file) : _Filename(file), _Artist(""), _Album(""
|
|||
_Title = tag->title().toCString();
|
||||
_Genre = tag->genre().toCString();
|
||||
}
|
||||
|
||||
|
||||
TagLib::AudioProperties* properties = f.isNull() ? NULL : f.audioProperties();
|
||||
if (properties != NULL) {
|
||||
_Bitrate = properties->bitrate();
|
||||
|
|
|
@ -29,9 +29,10 @@ private:
|
|||
string _Album;
|
||||
string _Title;
|
||||
string _Genre;
|
||||
|
||||
|
||||
int _Bitrate;
|
||||
int _SampleRate;
|
||||
int _Seconds;
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ SubbandAnalysis::SubbandAnalysis(AudioStreamInput* pAudio) {
|
|||
Init();
|
||||
}
|
||||
|
||||
SubbandAnalysis::SubbandAnalysis(const float* pSamples, uint numSamples) :
|
||||
SubbandAnalysis::SubbandAnalysis(const float* pSamples, uint numSamples) :
|
||||
_pSamples(pSamples), _NumSamples(numSamples) {
|
||||
Init();
|
||||
}
|
||||
|
@ -28,17 +28,17 @@ void SubbandAnalysis::Init() {
|
|||
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));
|
||||
_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);
|
||||
|
||||
|
@ -55,7 +55,7 @@ void SubbandAnalysis::Compute() {
|
|||
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;
|
||||
|
|
|
@ -18,21 +18,21 @@
|
|||
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.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};
|
||||
}
|
||||
|
||||
|
@ -42,14 +42,14 @@ class SubbandAnalysis {
|
|||
public:
|
||||
inline SubbandAnalysis() {};
|
||||
SubbandAnalysis(AudioStreamInput* pAudio);
|
||||
SubbandAnalysis(const float* pSamples, uint numSamples);
|
||||
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;}
|
||||
|
||||
const matrix_f& getMatrix() {return _Data;}
|
||||
|
||||
protected:
|
||||
const float* _pSamples;
|
||||
uint _NumSamples;
|
||||
|
|
|
@ -13,7 +13,7 @@ Whitening::Whitening(AudioStreamInput* pAudio) {
|
|||
Init();
|
||||
}
|
||||
|
||||
Whitening::Whitening(const float* pSamples, uint numSamples) :
|
||||
Whitening::Whitening(const float* pSamples, uint numSamples) :
|
||||
_pSamples(pSamples), _NumSamples(numSamples) {
|
||||
Init();
|
||||
}
|
||||
|
@ -27,16 +27,16 @@ Whitening::~Whitening() {
|
|||
|
||||
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; }
|
||||
_p = 40;
|
||||
|
||||
_ai = (float *)malloc((_P+1)*sizeof(float));
|
||||
_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);
|
||||
}
|
||||
|
||||
|
@ -57,23 +57,23 @@ void Whitening::ComputeBlock(int start, int blockSize) {
|
|||
float T = 8;
|
||||
alpha = 1.0/T;
|
||||
|
||||
// calculate autocorrelation of current block
|
||||
|
||||
for (i = 0; i <= _P; ++i) {
|
||||
// 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
|
||||
// smoothed update
|
||||
_R[i] += alpha*(acc - _R[i]);
|
||||
}
|
||||
|
||||
// calculate new filter coefficients
|
||||
// Durbin's recursion, per p. 411 of Rabiner & Schafer 1978
|
||||
|
||||
// calculate new filter coefficients
|
||||
// Durbin's recursion, per p. 411 of Rabiner & Schafer 1978
|
||||
E = _R[0];
|
||||
for (i = 1; i <= _P; ++i) {
|
||||
for (i = 1; i <= _p; ++i) {
|
||||
float sumalphaR = 0;
|
||||
for (j = 1; j < i; ++j) {
|
||||
sumalphaR += _ai[j]*_R[i-j];
|
||||
|
@ -88,21 +88,21 @@ void Whitening::ComputeBlock(int start, int blockSize) {
|
|||
}
|
||||
E = (1-ki*ki)*E;
|
||||
}
|
||||
// calculate new output
|
||||
// calculate new output
|
||||
for (i = 0; i < (int)blockSize; ++i) {
|
||||
float acc = _pSamples[i+start];
|
||||
for (j = 1; j <= _P; ++j) {
|
||||
for (j = 1; j <= _p; ++j) {
|
||||
if (i-j < 0) {
|
||||
acc -= _ai[j]*_Xo[_P + i-j];
|
||||
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];
|
||||
// save last few frames of input
|
||||
for (i = 0; i <= _p; ++i) {
|
||||
_Xo[i] = _pSamples[blockSize-1-_p+i+start];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,15 +17,15 @@ class Whitening {
|
|||
public:
|
||||
inline Whitening() {};
|
||||
Whitening(AudioStreamInput* pAudio);
|
||||
Whitening(const float* pSamples, uint numSamples);
|
||||
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;
|
||||
|
@ -33,7 +33,7 @@ protected:
|
|||
float* _R;
|
||||
float *_Xo;
|
||||
float *_ai;
|
||||
int _P;
|
||||
int _p;
|
||||
private:
|
||||
void Init();
|
||||
};
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
9AAF51A713B2557800099790 /* Whitening.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 9AAF51A513B2557800099790 /* Whitening.cxx */; };
|
||||
9AAF51A813B2557800099790 /* Whitening.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AAF51A613B2557800099790 /* Whitening.h */; };
|
||||
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 */; };
|
||||
|
@ -30,6 +32,8 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
9AAF51A513B2557800099790 /* Whitening.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Whitening.cxx; path = ../Whitening.cxx; sourceTree = SOURCE_ROOT; };
|
||||
9AAF51A613B2557800099790 /* Whitening.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Whitening.h; path = ../Whitening.h; sourceTree = SOURCE_ROOT; };
|
||||
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; };
|
||||
|
@ -98,6 +102,8 @@
|
|||
C8D4AD21137EE85F00CD506D /* echoprint-codegen-ios */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9AAF51A513B2557800099790 /* Whitening.cxx */,
|
||||
9AAF51A613B2557800099790 /* Whitening.h */,
|
||||
9AE5AA17139FB2620009FCD8 /* SubbandAnalysis.h */,
|
||||
9AE5AA18139FB2620009FCD8 /* SubbandAnalysis.cxx */,
|
||||
C8D4AD29137EF41100CD506D /* AudioBufferInput.cxx */,
|
||||
|
@ -109,7 +115,6 @@
|
|||
C8D4AD2F137EF41100CD506D /* Codegen.cxx */,
|
||||
C8D4AD30137EF41100CD506D /* Codegen.h */,
|
||||
C8D4AD31137EF41100CD506D /* Common.h */,
|
||||
C8D4AD3A137EF41100CD506D /* fft */,
|
||||
C8D4AD40137EF41100CD506D /* File.h */,
|
||||
C8D4AD42137EF41100CD506D /* Fingerprint.cxx */,
|
||||
C8D4AD43137EF41100CD506D /* Fingerprint.h */,
|
||||
|
@ -131,14 +136,6 @@
|
|||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C8D4AD3A137EF41100CD506D /* fft */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = fft;
|
||||
path = ../../fft;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
|
@ -158,6 +155,7 @@
|
|||
C8D4AD6D137EF41100CD506D /* win_funcs.h in Headers */,
|
||||
C8D4AD6E137EF41100CD506D /* win_unistd.h in Headers */,
|
||||
9AE5AA19139FB2620009FCD8 /* SubbandAnalysis.h in Headers */,
|
||||
9AAF51A813B2557800099790 /* Whitening.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -215,6 +213,7 @@
|
|||
C8D4AD62137EF41100CD506D /* Fingerprint.cxx in Sources */,
|
||||
C8D4AD64137EF41100CD506D /* MatrixUtility.cxx in Sources */,
|
||||
9AE5AA1A139FB2620009FCD8 /* SubbandAnalysis.cxx in Sources */,
|
||||
9AAF51A713B2557800099790 /* Whitening.cxx in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -20,18 +20,30 @@
|
|||
#include <string>
|
||||
#define MAX_FILES 200000
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
using namespace std;
|
||||
|
||||
// The response from the codegen. Contains all the fields necessary
|
||||
// to create a json string.
|
||||
typedef struct {
|
||||
char *error;
|
||||
char *filename;
|
||||
int start_offset;
|
||||
int duration;
|
||||
int tag;
|
||||
double t1;
|
||||
double t2;
|
||||
int numSamples;
|
||||
Codegen* codegen;
|
||||
} codegen_response_t;
|
||||
|
||||
// Struct to pass to the worker threads
|
||||
typedef struct {
|
||||
char *filename;
|
||||
char *filename;
|
||||
int start_offset;
|
||||
int duration;
|
||||
int done ;
|
||||
int tag;
|
||||
char *output;
|
||||
int done;
|
||||
codegen_response_t *response;
|
||||
} thread_parm_t;
|
||||
|
||||
// Thank you http://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine
|
||||
|
@ -68,8 +80,6 @@ int getNumCores() {
|
|||
#endif
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
// deal with quotes etc in json
|
||||
std::string escape(const string& value) {
|
||||
std::string s(value);
|
||||
|
@ -79,7 +89,7 @@ std::string escape(const string& value) {
|
|||
char c = s[i];
|
||||
if (c <= 31)
|
||||
continue;
|
||||
|
||||
|
||||
switch (c) {
|
||||
case '"' : out += "\\\""; break;
|
||||
case '\\': out += "\\\\"; break;
|
||||
|
@ -94,78 +104,63 @@ std::string escape(const string& value) {
|
|||
// 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.
|
||||
codegen_response_t *codegen_file(char* filename, int start_offset, int duration, int tag) {
|
||||
// Given a filename, perform a codegen on it and get the response
|
||||
// This is called by a thread
|
||||
double t1 = now();
|
||||
|
||||
codegen_response_t *response = (codegen_response_t *)malloc(sizeof(codegen_response_t));
|
||||
response->error = NULL;
|
||||
|
||||
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,
|
||||
sprintf(output,"{\"error\":\"could not create decoder\", \"tag\":%d, \"metadata\":{\"filename\":\"%s\"}}",
|
||||
tag,
|
||||
escape(filename).c_str());
|
||||
return output;
|
||||
response->error = output;
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
int numSamples = pAudio->getNumSamples();
|
||||
|
||||
|
||||
if (numSamples < 1) {
|
||||
char* output = (char*) malloc(16384);
|
||||
sprintf(output,"{\"error\":\"could not decode\", \"tag\":%d, \"metadata\":{\"filename\":\"%s\"}}",
|
||||
tag,
|
||||
sprintf(output,"{\"error\":\"could not decode\", \"tag\":%d, \"metadata\":{\"filename\":\"%s\"}}",
|
||||
tag,
|
||||
escape(filename).c_str());
|
||||
return output;
|
||||
response->error = output;
|
||||
return response;
|
||||
}
|
||||
t1 = now() - t1;
|
||||
|
||||
double t2 = now();
|
||||
auto_ptr<Codegen> pCodegen(new Codegen(pAudio->getSamples(), numSamples, start_offset));
|
||||
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;
|
||||
response->t1 = t1;
|
||||
response->t2 = t2;
|
||||
response->numSamples = numSamples;
|
||||
response->codegen = pCodegen;
|
||||
response->start_offset = start_offset;
|
||||
response->duration = duration;
|
||||
response->tag = tag;
|
||||
response->filename = filename;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
void *threaded_json_string_for_file(void *parm) {
|
||||
void *threaded_codegen_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;
|
||||
codegen_response_t *response = codegen_file(p->filename, p->start_offset, p->duration, p->tag);
|
||||
p->response = response;
|
||||
// mark when we're done so the controlling thread can move on.
|
||||
p->done = 1;
|
||||
return NULL;
|
||||
|
@ -179,13 +174,49 @@ void print_json_to_screen(char* output, int count, int done) {
|
|||
else printf("%s,\n", output);
|
||||
}
|
||||
|
||||
char *make_json_string(codegen_response_t* response) {
|
||||
|
||||
if (response->error != NULL) {
|
||||
return response->error;
|
||||
}
|
||||
|
||||
// Get the ID3 tag information.
|
||||
auto_ptr<Metadata> pMetadata(new Metadata(response->filename));
|
||||
|
||||
// preamble + codelen
|
||||
char* output = (char*) malloc(sizeof(char)*(16384 + strlen(response->codegen->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(response->filename).c_str(),
|
||||
response->numSamples,
|
||||
response->duration,
|
||||
response->start_offset,
|
||||
response->codegen->getVersion(),
|
||||
response->t2,
|
||||
response->t1,
|
||||
response->codegen->getNumCodes(),
|
||||
response->codegen->getCodeString().c_str(),
|
||||
response->tag
|
||||
);
|
||||
return 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];
|
||||
|
@ -209,23 +240,24 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
}
|
||||
} 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);
|
||||
codegen_response_t* response = codegen_file((char*)files[i].c_str(), start_offset, duration, i);
|
||||
char *output = make_json_string(response);
|
||||
print_json_to_screen(output, count, i);
|
||||
free(output);
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
// FIgure out how many threads to use based on # of cores
|
||||
#else
|
||||
|
||||
// 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;
|
||||
|
@ -239,7 +271,7 @@ int main(int argc, char** argv) {
|
|||
// 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] = (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;
|
||||
|
@ -249,10 +281,10 @@ int main(int argc, char** argv) {
|
|||
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]))
|
||||
if (pthread_create(&t[i], &attr[i], threaded_codegen_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) {
|
||||
|
@ -261,20 +293,22 @@ int main(int argc, char** argv) {
|
|||
if (parm[i]->done) {
|
||||
parm[i]->done = 0;
|
||||
done++;
|
||||
print_json_to_screen(parm[i]->output, count, done);
|
||||
free(parm[i]->output);
|
||||
codegen_response_t *response = (codegen_response_t*)parm[i]->response;
|
||||
char *json = make_json_string(response);
|
||||
print_json_to_screen(json, count, done);
|
||||
free(parm[i]->response);
|
||||
// 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)
|
||||
int err= pthread_create(&t[i], &attr[i], threaded_codegen_file, (void*)parm[i]);
|
||||
if(err)
|
||||
throw std::runtime_error("Problem creating thread\n");
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up threads
|
||||
|
@ -285,6 +319,8 @@ int main(int argc, char** argv) {
|
|||
free(parm);
|
||||
free(attr);
|
||||
return 0;
|
||||
|
||||
#endif // _WIN32
|
||||
}
|
||||
catch(std::runtime_error& ex) {
|
||||
fprintf(stderr, "%s\n", ex.what());
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
|
||||
#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; \
|
||||
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; \
|
||||
type y = ceil##suff(x); \
|
||||
if (y - x >= 0.5##suff) \
|
||||
y -= 1.0##suff; \
|
||||
return y; \
|
||||
} \
|
||||
}
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
#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 */
|
||||
#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
|
||||
|
||||
#define F_OK 0
|
||||
|
||||
#define W_OK 2
|
||||
#define 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 */
|
||||
|
||||
|
|
Loading…
Reference in New Issue