update qm-dsp library

This commit is contained in:
Robin Gareus 2016-10-06 00:16:44 +02:00
parent 2a27cc4758
commit f68d2e06bc
100 changed files with 58968 additions and 55091 deletions

View File

@ -7,9 +7,11 @@ This is a C++ library of functions for DSP and Music Informatics
purposes developed at Queen Mary, University of London.
It is used by the QM Vamp Plugins (q.v.) amongst other things.
This code is Copyright (c) 2006-2010 Queen Mary, University of London,
This code is Copyright (c) 2006-2015 Queen Mary, University of London,
with the following exceptions:
ext/kissfft -- Copyright (c) 2003-2010 Mark Borgerding
maths/pca.c -- Fionn Murtagh, from StatLib; with permission
maths/Polyfit.h -- Allen Miller, David J Taylor and others; also for

View File

@ -0,0 +1,65 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP library
Centre for Digital Music, Queen Mary, University of London.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "KaiserWindow.h"
#include "maths/MathUtilities.h"
KaiserWindow::Parameters
KaiserWindow::parametersForTransitionWidth(double attenuation,
double transition)
{
Parameters p;
p.length = 1 + (attenuation > 21.0 ?
ceil((attenuation - 7.95) / (2.285 * transition)) :
ceil(5.79 / transition));
p.beta = (attenuation > 50.0 ?
0.1102 * (attenuation - 8.7) :
attenuation > 21.0 ?
0.5842 * pow(attenuation - 21.0, 0.4) + 0.07886 * (attenuation - 21.0) :
0);
return p;
}
static double besselTerm(double x, int i)
{
if (i == 0) {
return 1;
} else {
double f = MathUtilities::factorial(i);
return pow(x/2, i*2) / (f*f);
}
}
static double bessel0(double x)
{
double b = 0.0;
for (int i = 0; i < 20; ++i) {
b += besselTerm(x, i);
}
return b;
}
void
KaiserWindow::init()
{
double denominator = bessel0(m_beta);
bool even = (m_length % 2 == 0);
for (int i = 0; i < (even ? m_length/2 : (m_length+1)/2); ++i) {
double k = double(2*i) / double(m_length-1) - 1.0;
m_window.push_back(bessel0(m_beta * sqrt(1.0 - k*k)) / denominator);
}
for (int i = 0; i < (even ? m_length/2 : (m_length-1)/2); ++i) {
m_window.push_back(m_window[int(m_length/2) - i - 1]);
}
}

View File

@ -0,0 +1,105 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP library
Centre for Digital Music, Queen Mary, University of London.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef KAISER_WINDOW_H
#define KAISER_WINDOW_H
#include <vector>
#include <cmath>
/**
* Kaiser window: A windower whose bandwidth and sidelobe height
* (signal-noise ratio) can be specified. These parameters are traded
* off against the window length.
*/
class KaiserWindow
{
public:
struct Parameters {
int length;
double beta;
};
/**
* Construct a Kaiser windower with the given length and beta
* parameter.
*/
KaiserWindow(Parameters p) : m_length(p.length), m_beta(p.beta) { init(); }
/**
* Construct a Kaiser windower with the given attenuation in dB
* and transition width in samples.
*/
static KaiserWindow byTransitionWidth(double attenuation,
double transition) {
return KaiserWindow
(parametersForTransitionWidth(attenuation, transition));
}
/**
* Construct a Kaiser windower with the given attenuation in dB
* and transition bandwidth in Hz for the given samplerate.
*/
static KaiserWindow byBandwidth(double attenuation,
double bandwidth,
double samplerate) {
return KaiserWindow
(parametersForBandwidth(attenuation, bandwidth, samplerate));
}
/**
* Obtain the parameters necessary for a Kaiser window of the
* given attenuation in dB and transition width in samples.
*/
static Parameters parametersForTransitionWidth(double attenuation,
double transition);
/**
* Obtain the parameters necessary for a Kaiser window of the
* given attenuation in dB and transition bandwidth in Hz for the
* given samplerate.
*/
static Parameters parametersForBandwidth(double attenuation,
double bandwidth,
double samplerate) {
return parametersForTransitionWidth
(attenuation, (bandwidth * 2 * M_PI) / samplerate);
}
int getLength() const {
return m_length;
}
const double *getWindow() const {
return m_window.data();
}
void cut(double *src) const {
cut(src, src);
}
void cut(const double *src, double *dst) const {
for (int i = 0; i < m_length; ++i) {
dst[i] = src[i] * m_window[i];
}
}
private:
int m_length;
double m_beta;
std::vector<double> m_window;
void init();
};
#endif

View File

@ -39,7 +39,7 @@ Pitch::getPitchForFrequency(float frequency,
midiPitch = midiPitch + 1;
centsOffset = -(100.0 - centsOffset);
}
if (centsOffsetReturn) *centsOffsetReturn = centsOffset;
return midiPitch;
}

View File

@ -15,6 +15,10 @@
#ifndef _PITCH_H_
#define _PITCH_H_
/**
* Convert between musical pitch (i.e. MIDI pitch number) and
* fundamental frequency.
*/
class Pitch
{
public:

View File

@ -0,0 +1,45 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP library
Centre for Digital Music, Queen Mary, University of London.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "SincWindow.h"
#include <cmath>
void
SincWindow::init()
{
if (m_length < 1) {
return;
} else if (m_length < 2) {
m_window.push_back(1);
return;
} else {
int n0 = (m_length % 2 == 0 ? m_length/2 : (m_length - 1)/2);
int n1 = (m_length % 2 == 0 ? m_length/2 : (m_length + 1)/2);
double m = 2 * M_PI / m_p;
for (int i = 0; i < n0; ++i) {
double x = ((m_length / 2) - i) * m;
m_window.push_back(sin(x) / x);
}
m_window.push_back(1.0);
for (int i = 1; i < n1; ++i) {
double x = i * m;
m_window.push_back(sin(x) / x);
}
}
}

View File

@ -0,0 +1,61 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP library
Centre for Digital Music, Queen Mary, University of London.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef SINC_WINDOW_H
#define SINC_WINDOW_H
#include <vector>
/**
* A window containing values of the sinc function, i.e. sin(x)/x with
* sinc(0) == 1, with x == 0 at the centre.
*/
class SincWindow
{
public:
/**
* Construct a windower of the given length, containing the values
* of sinc(x) with x=0 in the middle, i.e. at sample (length-1)/2
* for odd or (length/2)+1 for even length, such that the distance
* from -pi to pi (the nearest zero crossings either side of the
* peak) is p samples.
*/
SincWindow(int length, double p) : m_length(length), m_p(p) { init(); }
int getLength() const {
return m_length;
}
const double *getWindow() const {
return m_window.data();
}
void cut(double *src) const {
cut(src, src);
}
void cut(const double *src, double *dst) const {
for (int i = 0; i < m_length; ++i) {
dst[i] = src[i] * m_window[i];
}
}
private:
int m_length;
double m_p;
std::vector<double> m_window;
void init();
};
#endif

View File

@ -18,6 +18,7 @@
#include <cmath>
#include <iostream>
#include <map>
#include <vector>
enum WindowType {
RectangularWindow,
@ -25,18 +26,28 @@ enum WindowType {
HammingWindow,
HanningWindow,
BlackmanWindow,
GaussianWindow,
ParzenWindow
BlackmanHarrisWindow,
FirstWindow = RectangularWindow,
LastWindow = BlackmanHarrisWindow
};
/**
* Various shaped windows for sample frame conditioning, including
* cosine windows (Hann etc) and triangular and rectangular windows.
*/
template <typename T>
class Window
{
public:
/**
* Construct a windower of the given type.
* Construct a windower of the given type and size.
*
* Note that the cosine windows are periodic by design, rather
* than symmetrical. (A window of size N is equivalent to a
* symmetrical window of size N+1 with the final element missing.)
*/
Window(WindowType type, size_t size) : m_type(type), m_size(size) { encache(); }
Window(WindowType type, int size) : m_type(type), m_size(size) { encache(); }
Window(const Window &w) : m_type(w.m_type), m_size(w.m_size) { encache(); }
Window &operator=(const Window &w) {
if (&w == this) return *this;
@ -46,79 +57,98 @@ public:
return *this;
}
virtual ~Window() { delete[] m_cache; }
void cut(T *src) const { cut(src, src); }
void cut(const T *src, T *dst) const {
for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i];
for (int i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i];
}
WindowType getType() const { return m_type; }
size_t getSize() const { return m_size; }
int getSize() const { return m_size; }
std::vector<T> getWindowData() const {
std::vector<T> d;
for (int i = 0; i < m_size; ++i) {
d.push_back(m_cache[i]);
}
return d;
}
protected:
WindowType m_type;
size_t m_size;
int m_size;
T *m_cache;
void encache();
};
template <typename T>
void Window<T>::encache()
{
size_t n = m_size;
int n = m_size;
T *mult = new T[n];
size_t i;
int i;
for (i = 0; i < n; ++i) mult[i] = 1.0;
switch (m_type) {
case RectangularWindow:
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * 0.5;
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * 0.5;
}
break;
case BartlettWindow:
for (i = 0; i < n/2; ++i) {
mult[i] = mult[i] * (i / T(n/2));
mult[i + n/2] = mult[i + n/2] * (1.0 - (i / T(n/2)));
if (n == 2) {
mult[0] = mult[1] = 0; // "matlab compatible"
} else if (n == 3) {
mult[0] = 0;
mult[1] = mult[2] = 2./3.;
} else if (n > 3) {
for (i = 0; i < n/2; ++i) {
mult[i] = mult[i] * (i / T(n/2));
mult[i + n - n/2] = mult[i + n - n/2] * (1.0 - (i / T(n/2)));
}
}
break;
case HammingWindow:
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * (0.54 - 0.46 * cos(2 * M_PI * i / n));
if (n > 1) {
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * (0.54 - 0.46 * cos(2 * M_PI * i / n));
}
}
break;
case HanningWindow:
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * (0.50 - 0.50 * cos(2 * M_PI * i / n));
if (n > 1) {
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * (0.50 - 0.50 * cos(2 * M_PI * i / n));
}
}
break;
case BlackmanWindow:
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * (0.42 - 0.50 * cos(2 * M_PI * i / n)
+ 0.08 * cos(4 * M_PI * i / n));
if (n > 1) {
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * (0.42 - 0.50 * cos(2 * M_PI * i / n)
+ 0.08 * cos(4 * M_PI * i / n));
}
}
break;
case GaussianWindow:
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * exp((-1.0 / (n*n)) * ((T(2*i) - n) *
(T(2*i) - n)));
}
break;
case ParzenWindow:
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * (1.0 - fabs((T(2*i) - n) / T(n + 1)));
case BlackmanHarrisWindow:
if (n > 1) {
for (i = 0; i < n; ++i) {
mult[i] = mult[i] * (0.35875
- 0.48829 * cos(2 * M_PI * i / n)
+ 0.14128 * cos(4 * M_PI * i / n)
- 0.01168 * cos(6 * M_PI * i / n));
}
}
break;
}
m_cache = mult;
}

View File

@ -1,101 +0,0 @@
#
# qmake configuration for win32-g++
#
# Written for MinGW
#
MAKEFILE_GENERATOR = MINGW
TEMPLATE = app
CONFIG += qt warn_on release link_prl copy_dir_files debug_and_release debug_and_release_target
QT += core gui
DEFINES += UNICODE QT_LARGEFILE_SUPPORT
QMAKE_COMPILER_DEFINES += __GNUC__ WIN32
QMAKE_CC = i586-mingw32msvc-gcc
QMAKE_LEX = flex
QMAKE_LEXFLAGS =
QMAKE_YACC = byacc
QMAKE_YACCFLAGS = -d
QMAKE_CFLAGS =
QMAKE_CFLAGS_DEPS = -M
QMAKE_CFLAGS_WARN_ON = -Wall
QMAKE_CFLAGS_WARN_OFF = -w
QMAKE_CFLAGS_RELEASE = -O2
QMAKE_CFLAGS_DEBUG = -g
QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses
QMAKE_CFLAGS_THREAD = -mthreads
QMAKE_CXX = i586-mingw32msvc-g++
QMAKE_CXXFLAGS = $$QMAKE_CFLAGS
QMAKE_CXXFLAGS_DEPS = $$QMAKE_CFLAGS_DEPS
QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON
QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE
QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG
QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC
QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD
QMAKE_CXXFLAGS_RTTI_ON = -frtti
QMAKE_CXXFLAGS_RTTI_OFF = -fno-rtti
QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions
QMAKE_CXXFLAGS_EXCEPTIONS_OFF = -fno-exceptions
QMAKE_INCDIR =
QMAKE_INCDIR_QT = /home/studio/.wine/fake_windows/Qt/4.3.0/include
QMAKE_LIBDIR_QT = /home/studio/.wine/fake_windows/Qt/4.3.0/lib
QMAKE_RUN_CC = $(CC) -c $(CFLAGS) $(INCPATH) -o $obj $src
QMAKE_RUN_CC_IMP = $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
QMAKE_RUN_CXX = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $obj $src
QMAKE_RUN_CXX_IMP = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
QMAKE_LINK = i586-mingw32msvc-g++
QMAKE_LFLAGS = -Wl,-enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -mwindows
QMAKE_LFLAGS_RELEASE = -Wl,-s
QMAKE_LFLAGS_DEBUG =
QMAKE_LFLAGS_CONSOLE = -Wl,-subsystem,console
QMAKE_LFLAGS_WINDOWS = -Wl,-subsystem,windows
QMAKE_LFLAGS_DLL = -shared
QMAKE_LINK_OBJECT_MAX = 10
QMAKE_LINK_OBJECT_SCRIPT= object_script
QMAKE_LIBS =
QMAKE_LIBS_CORE = -lkernel32 -luser32 -lshell32 -luuid -lole32 -ladvapi32 -lws2_32
#QMAKE_LIBS_CORE = -lkernel32 -luser32 -luuid -lole32 -ladvapi32 -lws2_32
QMAKE_LIBS_GUI = -lgdi32 -lcomdlg32 -loleaut32 -limm32 -lwinmm -lwinspool -lws2_32 -lole32 -luuid -luser32
QMAKE_LIBS_NETWORK = -lws2_32
QMAKE_LIBS_OPENGL = -lopengl32 -lglu32 -lgdi32 -luser32
QMAKE_LIBS_COMPAT = -ladvapi32 -lshell32 -lcomdlg32 -luser32 -lgdi32 -lws2_32
QMAKE_LIBS_QT_ENTRY = -lqtmain
MINGW_IN_SHELL = $$(MINGW_IN_SHELL)
#isEqual(MINGW_IN_SHELL, 1) {
QMAKE_DIR_SEP = /
QMAKE_COPY = cp
QMAKE_COPY_DIR = cp -r
QMAKE_MOVE = mv
QMAKE_DEL_FILE = rm -f
QMAKE_MKDIR = mkdir -p
QMAKE_DEL_DIR = rm -rf
#} else {
# QMAKE_COPY = copy /y
# QMAKE_COPY_DIR = xcopy /s /q /y /i
# QMAKE_MOVE = move
# QMAKE_DEL_FILE = del
# QMAKE_MKDIR = mkdir
# QMAKE_DEL_DIR = rmdir
#}
QMAKE_MOC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}moc
QMAKE_UIC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}uic
QMAKE_IDC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}idc
QMAKE_IDL = midl
QMAKE_LIB = ar -ru
QMAKE_RC = i586-mingw32msvc-windres
QMAKE_ZIP = zip -r -9
QMAKE_STRIP = strip
QMAKE_STRIPFLAGS_LIB += --strip-unneeded
QMAKE_CHK_DIR_EXISTS = test -d
load(qt_config)

View File

@ -1,146 +0,0 @@
/****************************************************************************
**
** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
**
** This file is part of the qmake spec of the Qt Toolkit.
**
** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file. Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
#ifndef QPLATFORMDEFS_H
#define QPLATFORMDEFS_H
#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE
#endif
#endif
// Get Qt defines/settings
#include "qglobal.h"
#include <tchar.h>
#include <io.h>
#include <direct.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <windows.h>
#include <limits.h>
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT-0 < 0x0500)
typedef enum {
NameUnknown = 0,
NameFullyQualifiedDN = 1,
NameSamCompatible = 2,
NameDisplay = 3,
NameUniqueId = 6,
NameCanonical = 7,
NameUserPrincipal = 8,
NameCanonicalEx = 9,
NameServicePrincipal = 10,
NameDnsDomain = 12
} EXTENDED_NAME_FORMAT, *PEXTENDED_NAME_FORMAT;
#endif
#define Q_FS_FAT
#ifdef QT_LARGEFILE_SUPPORT
#define QT_STATBUF struct _stati64 // non-ANSI defs
#define QT_STATBUF4TSTAT struct _stati64 // non-ANSI defs
#define QT_STAT ::_stati64
#define QT_FSTAT ::_fstati64
#else
#define QT_STATBUF struct _stat // non-ANSI defs
#define QT_STATBUF4TSTAT struct _stat // non-ANSI defs
#define QT_STAT ::_stat
#define QT_FSTAT ::_fstat
#endif
#define QT_STAT_REG _S_IFREG
#define QT_STAT_DIR _S_IFDIR
#define QT_STAT_MASK _S_IFMT
#if defined(_S_IFLNK)
# define QT_STAT_LNK _S_IFLNK
#endif
#define QT_FILENO _fileno
#define QT_OPEN ::_open
#define QT_CLOSE ::_close
#ifdef QT_LARGEFILE_SUPPORT
#define QT_LSEEK ::_lseeki64
#ifndef UNICODE
#define QT_TSTAT ::_stati64
#else
#define QT_TSTAT ::_wstati64
#endif
#else
#define QT_LSEEK ::_lseek
#ifndef UNICODE
#define QT_TSTAT ::_stat
#else
#define QT_TSTAT ::_wstat
#endif
#endif
#define QT_READ ::_read
#define QT_WRITE ::_write
#define QT_ACCESS ::_access
#define QT_GETCWD ::_getcwd
#define QT_CHDIR ::_chdir
#define QT_MKDIR ::_mkdir
#define QT_RMDIR ::_rmdir
#define QT_OPEN_LARGEFILE 0
#define QT_OPEN_RDONLY _O_RDONLY
#define QT_OPEN_WRONLY _O_WRONLY
#define QT_OPEN_RDWR _O_RDWR
#define QT_OPEN_CREAT _O_CREAT
#define QT_OPEN_TRUNC _O_TRUNC
#define QT_OPEN_APPEND _O_APPEND
#if defined(O_TEXT)
# define QT_OPEN_TEXT _O_TEXT
# define QT_OPEN_BINARY _O_BINARY
#endif
#define QT_FOPEN ::fopen
#ifdef QT_LARGEFILE_SUPPORT
#define QT_FSEEK ::fseeko64
#define QT_FTELL ::ftello64
#else
#define QT_FSEEK ::fseek
#define QT_FTELL ::ftell
#endif
#define QT_FGETPOS ::fgetpos
#define QT_FSETPOS ::fsetpos
#define QT_FPOS_T fpos_t
#ifdef QT_LARGEFILE_SUPPORT
#define QT_OFF_T off64_t
#else
#define QT_OFF_T long
#endif
#define QT_SIGNAL_ARGS int
#define QT_VSNPRINTF ::_vsnprintf
#define QT_SNPRINTF ::_snprintf
# define F_OK 0
# define X_OK 1
# define W_OK 2
# define R_OK 4
#endif // QPLATFORMDEFS_H

View File

@ -1,106 +0,0 @@
TEMPLATE = lib
CONFIG += warn_on staticlib release
CONFIG -= qt
OBJECTS_DIR = tmp_obj
MOC_DIR = tmp_moc
linux-g++:QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O3 -march=pentium4 -msse -msse2
QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O2 -march=pentium3 -msse
#DEPENDPATH += base \
# dsp/chromagram \
# dsp/keydetection \
# dsp/maths \
# dsp/onsets \
# dsp/phasevocoder \
# dsp/rateconversion \
# dsp/signalconditioning \
# dsp/tempotracking \
# dsp/tonal \
# dsp/transforms
INCLUDEPATH += . include
# Input
HEADERS += base/Pitch.h \
base/Window.h \
dsp/chromagram/Chromagram.h \
dsp/chromagram/ChromaProcess.h \
dsp/chromagram/ConstantQ.h \
dsp/keydetection/GetKeyMode.h \
dsp/mfcc/MFCC.h \
dsp/onsets/DetectionFunction.h \
dsp/onsets/PeakPicking.h \
dsp/phasevocoder/PhaseVocoder.h \
dsp/rateconversion/Decimator.h \
dsp/rhythm/BeatSpectrum.h \
dsp/segmentation/cluster_melt.h \
dsp/segmentation/ClusterMeltSegmenter.h \
dsp/segmentation/cluster_segmenter.h \
dsp/segmentation/SavedFeatureSegmenter.h \
dsp/segmentation/Segmenter.h \
dsp/segmentation/segment.h \
dsp/signalconditioning/DFProcess.h \
dsp/signalconditioning/Filter.h \
dsp/signalconditioning/FiltFilt.h \
dsp/signalconditioning/Framer.h \
dsp/tempotracking/DownBeat.h \
dsp/tempotracking/TempoTrack.h \
dsp/tempotracking/TempoTrackV2.h \
dsp/tonal/ChangeDetectionFunction.h \
dsp/tonal/TCSgram.h \
dsp/tonal/TonalEstimator.h \
dsp/transforms/FFT.h \
dsp/transforms/kissfft/kiss_fft.h \
dsp/transforms/kissfft/kiss_fftr.h \
dsp/transforms/kissfft/_kiss_fft_guts.h \
dsp/wavelet/Wavelet.h \
hmm/hmm.h \
maths/Correlation.h \
maths/CosineDistance.h \
maths/Histogram.h \
maths/KLDivergence.h \
maths/MathAliases.h \
maths/MathUtilities.h \
maths/Polyfit.h \
maths/pca/pca.h \
thread/AsynchronousTask.h \
thread/BlockAllocator.h \
thread/Thread.h
SOURCES += base/Pitch.cpp \
dsp/chromagram/Chromagram.cpp \
dsp/chromagram/ChromaProcess.cpp \
dsp/chromagram/ConstantQ.cpp \
dsp/keydetection/GetKeyMode.cpp \
dsp/mfcc/MFCC.cpp \
dsp/onsets/DetectionFunction.cpp \
dsp/onsets/PeakPicking.cpp \
dsp/phasevocoder/PhaseVocoder.cpp \
dsp/rateconversion/Decimator.cpp \
dsp/rhythm/BeatSpectrum.cpp \
dsp/segmentation/cluster_melt.c \
dsp/segmentation/ClusterMeltSegmenter.cpp \
dsp/segmentation/cluster_segmenter.c \
dsp/segmentation/SavedFeatureSegmenter.cpp \
dsp/segmentation/Segmenter.cpp \
dsp/signalconditioning/DFProcess.cpp \
dsp/signalconditioning/Filter.cpp \
dsp/signalconditioning/FiltFilt.cpp \
dsp/signalconditioning/Framer.cpp \
dsp/tempotracking/DownBeat.cpp \
dsp/tempotracking/TempoTrack.cpp \
dsp/tempotracking/TempoTrackV2.cpp \
dsp/tonal/ChangeDetectionFunction.cpp \
dsp/tonal/TCSgram.cpp \
dsp/tonal/TonalEstimator.cpp \
dsp/transforms/FFT.cpp \
dsp/transforms/kissfft/kiss_fft.c \
dsp/transforms/kissfft/kiss_fftr.c \
dsp/wavelet/Wavelet.cpp \
hmm/hmm.c \
maths/Correlation.cpp \
maths/CosineDistance.cpp \
maths/KLDivergence.cpp \
maths/MathUtilities.cpp \
maths/pca/pca.c \
thread/Thread.cpp

View File

@ -1,91 +0,0 @@
QMAKE_MAC_SDK=/Developer/SDKs/MacOSX10.4u.sdk
TEMPLATE = lib
CONFIG += release warn_on staticlib x86 ppc
CONFIG -= qt
OBJECTS_DIR = tmp_obj
MOC_DIR = tmp_moc
QMAKE_CXXFLAGS_RELEASE += -O2 -g0
linux-g++:QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O3 -march=pentium4 -msse -msse2
#DEPENDPATH += base \
# dsp/chromagram \
# dsp/keydetection \
# dsp/maths \
# dsp/onsets \
# dsp/phasevocoder \
# dsp/rateconversion \
# dsp/signalconditioning \
# dsp/tempotracking \
# dsp/tonal \
# dsp/transforms
INCLUDEPATH += . include
# Input
HEADERS += base/Pitch.h \
base/Window.h \
dsp/chromagram/Chromagram.h \
dsp/chromagram/ChromaProcess.h \
dsp/chromagram/ConstantQ.h \
dsp/keydetection/GetKeyMode.h \
dsp/mfcc/MFCC.h \
dsp/onsets/DetectionFunction.h \
dsp/onsets/PeakPicking.h \
dsp/phasevocoder/PhaseVocoder.h \
dsp/rateconversion/Decimator.h \
dsp/rhythm/BeatSpectrum.h \
dsp/segmentation/cluster_melt.h \
dsp/segmentation/ClusterMeltSegmenter.h \
dsp/segmentation/cluster_segmenter.h \
dsp/segmentation/SavedFeatureSegmenter.h \
dsp/segmentation/Segmenter.h \
dsp/segmentation/segment.h \
dsp/signalconditioning/DFProcess.h \
dsp/signalconditioning/Filter.h \
dsp/signalconditioning/FiltFilt.h \
dsp/signalconditioning/Framer.h \
dsp/tempotracking/TempoTrack.h \
dsp/tonal/ChangeDetectionFunction.h \
dsp/tonal/TCSgram.h \
dsp/tonal/TonalEstimator.h \
dsp/transforms/FFT.h \
hmm/hmm.h \
maths/Correlation.h \
maths/CosineDistance.h \
maths/Histogram.h \
maths/KLDivergence.h \
maths/MathAliases.h \
maths/MathUtilities.h \
maths/Polyfit.h \
maths/pca/pca.h
SOURCES += base/Pitch.cpp \
dsp/chromagram/Chromagram.cpp \
dsp/chromagram/ChromaProcess.cpp \
dsp/chromagram/ConstantQ.cpp \
dsp/keydetection/GetKeyMode.cpp \
dsp/mfcc/MFCC.cpp \
dsp/onsets/DetectionFunction.cpp \
dsp/onsets/PeakPicking.cpp \
dsp/phasevocoder/PhaseVocoder.cpp \
dsp/rateconversion/Decimator.cpp \
dsp/rhythm/BeatSpectrum.cpp \
dsp/segmentation/cluster_melt.c \
dsp/segmentation/ClusterMeltSegmenter.cpp \
dsp/segmentation/cluster_segmenter.c \
dsp/segmentation/SavedFeatureSegmenter.cpp \
dsp/segmentation/Segmenter.cpp \
dsp/signalconditioning/DFProcess.cpp \
dsp/signalconditioning/Filter.cpp \
dsp/signalconditioning/FiltFilt.cpp \
dsp/signalconditioning/Framer.cpp \
dsp/tempotracking/TempoTrack.cpp \
dsp/tonal/ChangeDetectionFunction.cpp \
dsp/tonal/TCSgram.cpp \
dsp/tonal/TonalEstimator.cpp \
dsp/transforms/FFT.cpp \
hmm/hmm.c \
maths/Correlation.cpp \
maths/CosineDistance.cpp \
maths/KLDivergence.cpp \
maths/MathUtilities.cpp \
maths/pca/pca.c

File diff suppressed because it is too large Load Diff

View File

@ -27,14 +27,14 @@ Chromagram::Chromagram( ChromaConfig Config ) :
}
int Chromagram::initialise( ChromaConfig Config )
{
{
m_FMin = Config.min; // min freq
m_FMax = Config.max; // max freq
m_BPO = Config.BPO; // bins per octave
m_normalise = Config.normalise; // if frame normalisation is required
// No. of constant Q bins
m_uK = ( unsigned int ) ceil( m_BPO * log(m_FMax/m_FMin)/log(2.0));
m_uK = ( unsigned int ) ceil( m_BPO * log(m_FMax/m_FMin)/log(2.0));
// Create array for chroma result
m_chromadata = new double[ m_BPO ];
@ -49,7 +49,7 @@ int Chromagram::initialise( ChromaConfig Config )
ConstantQConfig.max = m_FMax;
ConstantQConfig.BPO = m_BPO;
ConstantQConfig.CQThresh = Config.CQThresh;
// Initialise ConstantQ operator
m_ConstantQ = new ConstantQ( ConstantQConfig );
@ -57,7 +57,7 @@ int Chromagram::initialise( ChromaConfig Config )
m_frameSize = m_ConstantQ->getfftlength();
m_hopSize = m_ConstantQ->gethop();
// Initialise FFT object
// Initialise FFT object
m_FFT = new FFTReal(m_frameSize);
m_FFTRe = new double[ m_frameSize ];
@ -124,7 +124,7 @@ void Chromagram::unityNormalise(double *src)
double* Chromagram::process( const double *data )
{
if (!m_skGenerated) {
// Generate CQ Kernel
// Generate CQ Kernel
m_ConstantQ->sparsekernel();
m_skGenerated = true;
}
@ -139,8 +139,7 @@ double* Chromagram::process( const double *data )
}
m_window->cut(m_windowbuf);
// FFT of current frame
m_FFT->process(false, m_windowbuf, m_FFTRe, m_FFTIm);
m_FFT->forward(m_windowbuf, m_FFTRe, m_FFTIm);
return process(m_FFTRe, m_FFTIm);
}
@ -148,7 +147,7 @@ double* Chromagram::process( const double *data )
double* Chromagram::process( const double *real, const double *imag )
{
if (!m_skGenerated) {
// Generate CQ Kernel
// Generate CQ Kernel
m_ConstantQ->sparsekernel();
m_skGenerated = true;
}
@ -158,16 +157,15 @@ double* Chromagram::process( const double *real, const double *imag )
double cmax = 0.0;
double cval = 0;
// Calculate ConstantQ frame
m_ConstantQ->process( real, imag, m_CQRe, m_CQIm );
// add each octave of cq data into Chromagram
const unsigned octaves = (int)floor(double( m_uK/m_BPO))-1;
for (unsigned octave = 0; octave <= octaves; octave++)
for (unsigned octave = 0; octave <= octaves; octave++)
{
unsigned firstBin = octave*m_BPO;
for (unsigned i = 0; i < m_BPO; i++)
for (unsigned i = 0; i < m_BPO; i++)
{
m_chromadata[i] += kabs( m_CQRe[ firstBin + i ], m_CQIm[ firstBin + i ]);
}

View File

@ -29,20 +29,20 @@ struct ChromaConfig{
MathUtilities::NormaliseType normalise;
};
class Chromagram
class Chromagram
{
public:
public:
Chromagram( ChromaConfig Config );
~Chromagram();
double* process( const double *data ); // time domain
double* process( const double *real, const double *imag ); // frequency domain
void unityNormalise( double* src );
// Complex arithmetic
double kabs( double real, double imag );
// Results
unsigned int getK() { return m_uK;}
unsigned int getFrameSize() { return m_frameSize; }
@ -54,7 +54,7 @@ private:
Window<double> *m_window;
double *m_windowbuf;
double* m_chromadata;
double m_FMin;
double m_FMax;

View File

@ -96,7 +96,7 @@ void ConstantQ::sparsekernel()
double* transfHammingWindowRe = new double [ m_FFTLength ];
double* transfHammingWindowIm = new double [ m_FFTLength ];
for (unsigned u=0; u < m_FFTLength; u++)
for (unsigned u=0; u < m_FFTLength; u++)
{
hammingWindowRe[u] = 0;
hammingWindowIm[u] = 0;
@ -109,28 +109,28 @@ void ConstantQ::sparsekernel()
sk->js.reserve( m_FFTLength*2 );
sk->real.reserve( m_FFTLength*2 );
sk->imag.reserve( m_FFTLength*2 );
// for each bin value K, calculate temporal kernel, take its fft to
//calculate the spectral kernel then threshold it to make it sparse and
//calculate the spectral kernel then threshold it to make it sparse and
//add it to the sparse kernels matrix
double squareThreshold = m_CQThresh * m_CQThresh;
FFT m_FFT(m_FFTLength);
for (unsigned k = m_uK; k--; )
for (unsigned k = m_uK; k--; )
{
for (unsigned u=0; u < m_FFTLength; u++)
for (unsigned u=0; u < m_FFTLength; u++)
{
hammingWindowRe[u] = 0;
hammingWindowIm[u] = 0;
}
// Computing a hamming window
const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO)));
unsigned origin = m_FFTLength/2 - hammingLength/2;
for (unsigned i=0; i<hammingLength; i++)
for (unsigned i=0; i<hammingLength; i++)
{
const double angle = 2*PI*m_dQ*i/hammingLength;
const double real = cos(angle);
@ -148,17 +148,17 @@ void ConstantQ::sparsekernel()
hammingWindowIm[i] = hammingWindowIm[i + m_FFTLength/2];
hammingWindowIm[i + m_FFTLength/2] = temp;
}
//do fft of hammingWindow
m_FFT.process( 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm );
for (unsigned j=0; j<( m_FFTLength ); j++)
for (unsigned j=0; j<( m_FFTLength ); j++)
{
// perform thresholding
const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]);
if (squaredBin <= squareThreshold) continue;
// Insert non-zero position indexes, doubled because they are floats
sk->is.push_back(j);
sk->js.push_back(k);
@ -241,7 +241,7 @@ void ConstantQ::sparsekernel()
cout << "}" << endl;
*/
// std::cerr << "done\n -> is: " << sk->is.size() << ", js: " << sk->js.size() << ", reals: " << sk->real.size() << ", imags: " << sk->imag.size() << std::endl;
m_sparseKernel = sk;
return;
}
@ -256,7 +256,7 @@ double* ConstantQ::process( const double* fftdata )
SparseKernel *sk = m_sparseKernel;
for (unsigned row=0; row<2*m_uK; row++)
for (unsigned row=0; row<2*m_uK; row++)
{
m_CQdata[ row ] = 0;
m_CQdata[ row+1 ] = 0;
@ -266,7 +266,7 @@ double* ConstantQ::process( const double* fftdata )
const double *real = &(sk->real[0]);
const double *imag = &(sk->imag[0]);
const unsigned int sparseCells = sk->real.size();
for (unsigned i = 0; i<sparseCells; i++)
{
const unsigned row = cqbin[i];
@ -324,7 +324,7 @@ void ConstantQ::process(const double *FFTRe, const double* FFTIm,
SparseKernel *sk = m_sparseKernel;
for (unsigned row=0; row<m_uK; row++)
for (unsigned row=0; row<m_uK; row++)
{
CQRe[ row ] = 0;
CQIm[ row ] = 0;
@ -335,7 +335,7 @@ void ConstantQ::process(const double *FFTRe, const double* FFTIm,
const double *real = &(sk->real[0]);
const double *imag = &(sk->imag[0]);
const unsigned int sparseCells = sk->real.size();
for (unsigned i = 0; i<sparseCells; i++)
{
const unsigned row = cqbin[i];

View File

@ -29,7 +29,7 @@ struct CQConfig{
};
class ConstantQ {
//public functions incl. sparsekernel so can keep out of loop in main
public:
void process( const double* FFTRe, const double* FFTIm,
@ -46,7 +46,7 @@ public:
double out = 0.54 - 0.46*cos(2*PI*n/len);
return(out);
}
int getnumwin() { return m_numWin;}
double getQ() { return m_dQ;}
int getK() {return m_uK ;}
@ -56,7 +56,7 @@ public:
private:
void initialise( CQConfig Config );
void deInitialise();
double* m_CQdata;
unsigned int m_FS;
double m_FMin;

View File

@ -1,5 +1,14 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Copyright (c) 2005 Centre for Digital Music ( C4DM )
Queen Mary Univesrity of London
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
// GetKeyMode.cpp: implementation of the CGetKeyMode class.
//
//////////////////////////////////////////////////////////////////////

View File

@ -1,17 +1,12 @@
/*
* Author: c.landone
* Description:
*
* Syntax: C++
*
* Copyright (c) 2005 Centre for Digital Music ( C4DM )
* Queen Mary Univesrity of London
*
*
* This program is not free software; you cannot redistribute it
* without the explicit authorization from the centre for digital music,
* queen mary university of london
*
Copyright (c) 2005 Centre for Digital Music ( C4DM )
Queen Mary Univesrity of London
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef GETKEYMODE_H

View File

@ -27,75 +27,75 @@ MFCC::MFCC(MFCCConfig config)
/* Calculate at startup */
double *freqs, *lower, *center, *upper, *triangleHeight, *fftFreqs;
lowestFrequency = 66.6666666;
linearFilters = 13;
linearSpacing = 66.66666666;
logFilters = 27;
logSpacing = 1.0711703;
/* FFT and analysis window sizes */
fftSize = config.fftsize;
fft = new FFTReal(fftSize);
totalFilters = linearFilters + logFilters;
logPower = config.logpower;
samplingRate = config.FS;
/* The number of cepstral componenents */
nceps = config.nceps;
/* Set if user want C0 */
WANT_C0 = (config.want_c0 ? 1 : 0);
/* Allocate space for feature vector */
if (WANT_C0 == 1) {
ceps = (double*)calloc(nceps+1, sizeof(double));
} else {
ceps = (double*)calloc(nceps, sizeof(double));
}
/* Allocate space for local vectors */
mfccDCTMatrix = (double**)calloc(nceps+1, sizeof(double*));
for (i = 0; i < nceps+1; i++) {
mfccDCTMatrix[i]= (double*)calloc(totalFilters, sizeof(double));
mfccDCTMatrix[i]= (double*)calloc(totalFilters, sizeof(double));
}
mfccFilterWeights = (double**)calloc(totalFilters, sizeof(double*));
for (i = 0; i < totalFilters; i++) {
mfccFilterWeights[i] = (double*)calloc(fftSize, sizeof(double));
mfccFilterWeights[i] = (double*)calloc(fftSize, sizeof(double));
}
freqs = (double*)calloc(totalFilters+2,sizeof(double));
lower = (double*)calloc(totalFilters,sizeof(double));
center = (double*)calloc(totalFilters,sizeof(double));
upper = (double*)calloc(totalFilters,sizeof(double));
triangleHeight = (double*)calloc(totalFilters,sizeof(double));
fftFreqs = (double*)calloc(fftSize,sizeof(double));
for (i = 0; i < linearFilters; i++) {
freqs[i] = lowestFrequency + ((double)i) * linearSpacing;
}
for (i = linearFilters; i < totalFilters+2; i++) {
freqs[i] = freqs[linearFilters-1] *
freqs[i] = freqs[linearFilters-1] *
pow(logSpacing, (double)(i-linearFilters+1));
}
/* Define lower, center and upper */
memcpy(lower, freqs,totalFilters*sizeof(double));
memcpy(center, &freqs[1],totalFilters*sizeof(double));
memcpy(upper, &freqs[2],totalFilters*sizeof(double));
for (i=0;i<totalFilters;i++){
triangleHeight[i] = 2./(upper[i]-lower[i]);
}
for (i=0;i<fftSize;i++){
fftFreqs[i] = ((double) i / ((double) fftSize ) *
fftFreqs[i] = ((double) i / ((double) fftSize ) *
(double) samplingRate);
}
@ -103,12 +103,12 @@ MFCC::MFCC(MFCCConfig config)
for (i=0;i<totalFilters;i++){
for (j=0;j<fftSize;j++) {
if ((fftFreqs[j] > lower[i]) && (fftFreqs[j] <= center[i])) {
mfccFilterWeights[i][j] = triangleHeight[i] *
(fftFreqs[j]-lower[i]) / (center[i]-lower[i]);
mfccFilterWeights[i][j] = triangleHeight[i] *
(fftFreqs[j]-lower[i]) / (center[i]-lower[i]);
}
else
{
@ -118,7 +118,7 @@ MFCC::MFCC(MFCCConfig config)
if ((fftFreqs[j]>center[i]) && (fftFreqs[j]<upper[i])) {
mfccFilterWeights[i][j] = mfccFilterWeights[i][j]
+ triangleHeight[i] * (upper[i]-fftFreqs[j])
+ triangleHeight[i] * (upper[i]-fftFreqs[j])
/ (upper[i]-center[i]);
}
else
@ -130,15 +130,15 @@ MFCC::MFCC(MFCCConfig config)
}
/*
* We calculate now mfccDCT matrix
* We calculate now mfccDCT matrix
* NB: +1 because of the DC component
*/
const double pi = 3.14159265358979323846264338327950288;
for (i = 0; i < nceps+1; i++) {
for (j = 0; j < totalFilters; j++) {
mfccDCTMatrix[i][j] = (1./sqrt((double) totalFilters / 2.))
mfccDCTMatrix[i][j] = (1./sqrt((double) totalFilters / 2.))
* cos((double) i * ((double) j + 0.5) / (double) totalFilters * pi);
}
}
@ -146,7 +146,7 @@ MFCC::MFCC(MFCCConfig config)
for (j = 0; j < totalFilters; j++){
mfccDCTMatrix[0][j] = (sqrt(2.)/2.) * mfccDCTMatrix[0][j];
}
/* The analysis window */
window = new Window<double>(config.window, fftSize);
@ -156,7 +156,7 @@ MFCC::MFCC(MFCCConfig config)
earMag = (double*)calloc(totalFilters, sizeof(double));
fftMag = (double*)calloc(fftSize/2, sizeof(double));
free(freqs);
free(lower);
free(center);
@ -168,27 +168,27 @@ MFCC::MFCC(MFCCConfig config)
MFCC::~MFCC()
{
int i;
/* Free the structure */
for (i = 0; i < nceps+1; i++) {
free(mfccDCTMatrix[i]);
}
free(mfccDCTMatrix);
for (i = 0; i < totalFilters; i++) {
free(mfccFilterWeights[i]);
}
free(mfccFilterWeights);
/* Free the feature vector */
free(ceps);
/* The analysis window */
delete window;
free(earMag);
free(fftMag);
/* Free the FFT */
free(realOut);
free(imagOut);
@ -198,19 +198,19 @@ MFCC::~MFCC()
/*
*
* Extract the MFCC on the input frame
*
*/
*
* Extract the MFCC on the input frame
*
*/
int MFCC::process(const double *inframe, double *outceps)
{
double *inputData = (double *)malloc(fftSize * sizeof(double));
for (int i = 0; i < fftSize; ++i) inputData[i] = inframe[i];
window->cut(inputData);
/* Calculate the fft on the input frame */
fft->process(0, inputData, realOut, imagOut);
fft->forward(inputData, realOut, imagOut);
free(inputData);
@ -244,14 +244,14 @@ int MFCC::process(const double *real, const double *imag, double *outceps)
}
/*
*
* Calculate now the cepstral coefficients
*
* Calculate now the cepstral coefficients
* with or without the DC component
*
*/
if (WANT_C0 == 1) {
for (i = 0; i < nceps+1; i++) {
double tmp = 0.;
for (j = 0; j < totalFilters; j++){
@ -260,8 +260,8 @@ int MFCC::process(const double *real, const double *imag, double *outceps)
outceps[i] = tmp;
}
}
else
{
else
{
for (i = 1; i < nceps+1; i++) {
double tmp = 0.;
for (j = 0; j < totalFilters; j++){
@ -270,7 +270,7 @@ int MFCC::process(const double *real, const double *imag, double *outceps)
outceps[i-1] = tmp;
}
}
return nceps;
}

View File

@ -57,31 +57,31 @@ public:
private:
/* Filter bank parameters */
double lowestFrequency;
int linearFilters;
double lowestFrequency;
int linearFilters;
double linearSpacing;
int logFilters;
double logSpacing;
/* FFT length */
int fftSize;
int totalFilters;
double logPower;
/* Misc. */
int samplingRate;
int nceps;
/* MFCC vector */
double *ceps;
double **mfccDCTMatrix;
double **mfccFilterWeights;
/* The analysis window */
Window<double> *window;
/* For the FFT */
double *realOut;
double *imagOut;

View File

@ -40,10 +40,11 @@ DetectionFunction::~DetectionFunction()
void DetectionFunction::initialise( DFConfig Config )
{
m_dataLength = Config.frameLength;
m_halfLength = m_dataLength/2;
m_halfLength = m_dataLength/2 + 1;
m_DFType = Config.DFType;
m_stepSize = Config.stepSize;
m_dbRise = Config.dbRise;
m_whiten = Config.adaptiveWhitening;
m_whitenRelaxCoeff = Config.whiteningRelaxCoeff;
@ -53,7 +54,7 @@ void DetectionFunction::initialise( DFConfig Config )
m_magHistory = new double[ m_halfLength ];
memset(m_magHistory,0, m_halfLength*sizeof(double));
m_phaseHistory = new double[ m_halfLength ];
memset(m_phaseHistory,0, m_halfLength*sizeof(double));
@ -63,15 +64,14 @@ void DetectionFunction::initialise( DFConfig Config )
m_magPeaks = new double[ m_halfLength ];
memset(m_magPeaks,0, m_halfLength*sizeof(double));
// See note in process(const double *) below
int actualLength = MathUtilities::previousPowerOfTwo(m_dataLength);
m_phaseVoc = new PhaseVocoder(actualLength);
m_phaseVoc = new PhaseVocoder(m_dataLength, m_stepSize);
m_DFWindowedFrame = new double[ m_dataLength ];
m_magnitude = new double[ m_halfLength ];
m_thetaAngle = new double[ m_halfLength ];
m_unwrapped = new double[ m_halfLength ];
m_window = new Window<double>(HanningWindow, m_dataLength);
m_windowed = new double[ m_dataLength ];
}
void DetectionFunction::deInitialise()
@ -83,47 +83,31 @@ void DetectionFunction::deInitialise()
delete m_phaseVoc;
delete [] m_DFWindowedFrame;
delete [] m_magnitude;
delete [] m_thetaAngle;
delete [] m_windowed;
delete [] m_unwrapped;
delete m_window;
}
double DetectionFunction::process( const double *TDomain )
double DetectionFunction::processTimeDomain(const double *samples)
{
m_window->cut( TDomain, m_DFWindowedFrame );
m_window->cut(samples, m_windowed);
// Our own FFT implementation supports power-of-two sizes only.
// If we have to use this implementation (as opposed to the
// version of process() below that operates on frequency domain
// data directly), we will have to use the next smallest power of
// two from the block size. Results may vary accordingly!
unsigned int actualLength = MathUtilities::previousPowerOfTwo(m_dataLength);
if (actualLength != m_dataLength) {
// Pre-fill mag and phase vectors with zero, as the FFT output
// will not fill the arrays
for (unsigned int i = actualLength/2; i < m_dataLength/2; ++i) {
m_magnitude[i] = 0;
m_thetaAngle[0] = 0;
}
}
m_phaseVoc->process(m_DFWindowedFrame, m_magnitude, m_thetaAngle);
m_phaseVoc->processTimeDomain(m_windowed,
m_magnitude, m_thetaAngle, m_unwrapped);
if (m_whiten) whiten();
return runDF();
}
double DetectionFunction::process( const double *magnitudes, const double *phases )
double DetectionFunction::processFrequencyDomain(const double *reals,
const double *imags)
{
for (size_t i = 0; i < m_halfLength; ++i) {
m_magnitude[i] = magnitudes[i];
m_thetaAngle[i] = phases[i];
}
m_phaseVoc->processFrequencyDomain(reals, imags,
m_magnitude, m_thetaAngle, m_unwrapped);
if (m_whiten) whiten();
@ -152,15 +136,19 @@ double DetectionFunction::runDF()
case DF_HFC:
retVal = HFC( m_halfLength, m_magnitude);
break;
case DF_SPECDIFF:
retVal = specDiff( m_halfLength, m_magnitude);
break;
case DF_PHASEDEV:
// Using the instantaneous phases here actually provides the
// same results (for these calculations) as if we had used
// unwrapped phases, but without the possible accumulation of
// phase error over time
retVal = phaseDev( m_halfLength, m_thetaAngle);
break;
case DF_COMPLEXSD:
retVal = complexSD( m_halfLength, m_magnitude, m_thetaAngle);
break;
@ -169,7 +157,7 @@ double DetectionFunction::runDF()
retVal = broadband( m_halfLength, m_magnitude);
break;
}
return retVal;
}
@ -195,7 +183,7 @@ double DetectionFunction::specDiff(unsigned int length, double *src)
for( i = 0; i < length; i++)
{
temp = fabs( (src[ i ] * src[ i ]) - (m_magHistory[ i ] * m_magHistory[ i ]) );
diff= sqrt(temp);
// (See note in phaseDev below.)
@ -230,15 +218,14 @@ double DetectionFunction::phaseDev(unsigned int length, double *srcPhase)
// does significantly damage its ability to work with quieter
// music, so I'm removing it and counting the result always.
// Same goes for the spectral difference measure above.
tmpVal = fabs(dev);
val += tmpVal ;
m_phaseHistoryOld[ i ] = m_phaseHistory[ i ] ;
m_phaseHistory[ i ] = srcPhase[ i ];
}
return val;
}
@ -250,7 +237,7 @@ double DetectionFunction::complexSD(unsigned int length, double *srcMagnitude, d
double tmpPhase = 0;
double tmpReal = 0;
double tmpImag = 0;
double dev = 0;
ComplexData meas = ComplexData( 0, 0 );
ComplexData j = ComplexData( 0, 1 );
@ -259,14 +246,14 @@ double DetectionFunction::complexSD(unsigned int length, double *srcMagnitude, d
{
tmpPhase = (srcPhase[ i ]- 2*m_phaseHistory[ i ]+m_phaseHistoryOld[ i ]);
dev= MathUtilities::princarg( tmpPhase );
meas = m_magHistory[i] - ( srcMagnitude[ i ] * exp( j * dev) );
tmpReal = real( meas );
tmpImag = imag( meas );
val += sqrt( (tmpReal * tmpReal) + (tmpImag * tmpImag) );
m_phaseHistoryOld[ i ] = m_phaseHistory[ i ] ;
m_phaseHistory[ i ] = srcPhase[ i ];
m_magHistory[ i ] = srcMagnitude[ i ];
@ -287,7 +274,7 @@ double DetectionFunction::broadband(unsigned int length, double *src)
m_magHistory[i] = sqrmag;
}
return val;
}
}
double* DetectionFunction::getSpectrumMagnitude()
{

View File

@ -29,7 +29,7 @@
struct DFConfig{
unsigned int stepSize; // DF step in samples
unsigned int frameLength; // DF analysis window - usually 2*step
unsigned int frameLength; // DF analysis window - usually 2*step. Must be even!
int DFType; // type of detection function ( see defines )
double dbRise; // only used for broadband df (and required for it)
bool adaptiveWhitening; // perform adaptive whitening
@ -37,14 +37,24 @@ struct DFConfig{
double whiteningFloor; // if < 0, a sensible default will be used
};
class DetectionFunction
class DetectionFunction
{
public:
double* getSpectrumMagnitude();
DetectionFunction( DFConfig Config );
virtual ~DetectionFunction();
double process( const double* TDomain );
double process( const double* magnitudes, const double* phases );
/**
* Process a single time-domain frame of audio, provided as
* frameLength samples.
*/
double processTimeDomain(const double* samples);
/**
* Process a single frequency-domain frame, provided as
* frameLength/2+1 real and imaginary component values.
*/
double processFrequencyDomain(const double* reals, const double* imags);
private:
void whiten();
@ -55,7 +65,7 @@ private:
double phaseDev(unsigned int length, double *srcPhase);
double complexSD(unsigned int length, double *srcMagnitude, double *srcPhase);
double broadband(unsigned int length, double *srcMagnitude);
private:
void initialise( DFConfig Config );
void deInitialise();
@ -74,12 +84,13 @@ private:
double* m_phaseHistoryOld;
double* m_magPeaks;
double* m_DFWindowedFrame; // Array for windowed analysis frame
double* m_windowed; // Array for windowed analysis frame
double* m_magnitude; // Magnitude of analysis frame ( frequency domain )
double* m_thetaAngle;// Phase of analysis frame ( frequency domain )
double* m_unwrapped; // Unwrapped phase of analysis frame
Window<double> *m_window;
PhaseVocoder* m_phaseVoc; // Phase Vocoder
};
#endif
#endif

View File

@ -6,11 +6,19 @@
Centre for Digital Music, Queen Mary, University of London.
This file 2005-2006 Christian Landone.
Modifications:
- delta threshold
Description: add delta threshold used as offset in the smoothed
detection function
Author: Mathieu Barthet
Date: June 2010
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
COPYING included with this distribution for more information.
*/
#include "PeakPicking.h"
@ -41,15 +49,16 @@ void PeakPicking::initialise( PPickParams Config )
Qfilta = Config.QuadThresh.a ;
Qfiltb = Config.QuadThresh.b ;
Qfiltc = Config.QuadThresh.c ;
m_DFProcessingParams.length = m_DFLength;
m_DFProcessingParams.LPOrd = Config.LPOrd;
m_DFProcessingParams.LPACoeffs = Config.LPACoeffs;
m_DFProcessingParams.LPBCoeffs = Config.LPBCoeffs;
m_DFProcessingParams.length = m_DFLength;
m_DFProcessingParams.LPOrd = Config.LPOrd;
m_DFProcessingParams.LPACoeffs = Config.LPACoeffs;
m_DFProcessingParams.LPBCoeffs = Config.LPBCoeffs;
m_DFProcessingParams.winPre = Config.WinT.pre;
m_DFProcessingParams.winPost = Config.WinT.post;
m_DFProcessingParams.winPost = Config.WinT.post;
m_DFProcessingParams.AlphaNormParam = Config.alpha;
m_DFProcessingParams.isMedianPositive = false;
m_DFProcessingParams.delta = Config.delta; //add the delta threshold as an adjustable parameter
m_DFSmoothing = new DFProcess( m_DFProcessingParams );
@ -68,19 +77,19 @@ void PeakPicking::process( double* src, unsigned int len, vector<int> &onsets )
{
if (len < 4) return;
vector <double> m_maxima;
vector <double> m_maxima;
// Signal conditioning
// Signal conditioning
m_DFSmoothing->process( src, m_workBuffer );
for( unsigned int u = 0; u < len; u++)
{
m_maxima.push_back( m_workBuffer[ u ] );
m_maxima.push_back( m_workBuffer[ u ] );
}
quadEval( m_maxima, onsets );
for(unsigned int b = 0; b < m_maxima.size(); b++)
for( int b = 0; b < (int)m_maxima.size(); b++)
{
src[ b ] = m_maxima[ b ];
}
@ -92,7 +101,7 @@ int PeakPicking::quadEval( vector<double> &src, vector<int> &idx )
vector <int> m_maxIndex;
vector <int> m_onsetPosition;
vector <double> m_maxFit;
vector <double> m_poly;
vector <double> m_err;
@ -123,7 +132,7 @@ int PeakPicking::quadEval( vector<double> &src, vector<int> &idx )
for (int k = -2; k <= 2; ++k)
{
selMax = src[ m_maxIndex[j] + k ] ;
m_maxFit.push_back(selMax);
m_maxFit.push_back(selMax);
}
TPolyFit::PolyFit2(m_err, m_maxFit, m_poly);
@ -135,7 +144,7 @@ int PeakPicking::quadEval( vector<double> &src, vector<int> &idx )
{
idx.push_back(m_maxIndex[j]);
}
m_maxFit.clear();
}

View File

@ -6,6 +6,14 @@
Centre for Digital Music, Queen Mary, University of London.
This file 2005-2006 Christian Landone.
Modifications:
- delta threshold
Description: add delta threshold used as offset in the smoothed
detection function
Author: Mathieu Barthet
Date: June 2010
This program 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 2 of the
@ -29,6 +37,12 @@ struct PPWinThresh
{
unsigned int pre;
unsigned int post;
PPWinThresh(unsigned int x, unsigned int y) :
pre(x),
post(y)
{
}
};
struct QFitThresh
@ -36,12 +50,19 @@ struct QFitThresh
double a;
double b;
double c;
QFitThresh(double x, double y, double z) :
a(x),
b(y),
c(z)
{
}
};
struct PPickParams
{
unsigned int length; //Detection FunctionLength
double tau; // time resolution of the detection function:
double tau; // time resolution of the detection function
unsigned int alpha; //alpha-norm parameter
double cutoff;//low-pass Filter cutoff freq
unsigned int LPOrd; // low-pass Filter order
@ -49,14 +70,29 @@ struct PPickParams
double* LPBCoeffs; //low pass Filter num coefficients
PPWinThresh WinT;//window size in frames for adaptive thresholding [pre post]:
QFitThresh QuadThresh;
float delta; //delta threshold used as an offset when computing the smoothed detection function
PPickParams() :
length(0),
tau(0),
alpha(0),
cutoff(0),
LPOrd(0),
LPACoeffs(NULL),
LPBCoeffs(NULL),
WinT(0,0),
QuadThresh(0,0,0),
delta(0)
{
}
};
class PeakPicking
class PeakPicking
{
public:
PeakPicking( PPickParams Config );
virtual ~PeakPicking();
void process( double* src, unsigned int len, vector<int> &onsets );
@ -64,7 +100,7 @@ private:
void initialise( PPickParams Config );
void deInitialise();
int quadEval( vector<double> &src, vector<int> &idx );
DFProcConfig m_DFProcessingParams;
unsigned int m_DFLength ;
@ -74,7 +110,7 @@ private:
double* m_workBuffer;
DFProcess* m_DFSmoothing;
};

View File

@ -4,7 +4,7 @@
QM DSP Library
Centre for Digital Music, Queen Mary, University of London.
This file 2005-2006 Christian Landone.
This file 2005-2006 Christian Landone, copyright 2013 QMUL.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@ -15,30 +15,47 @@
#include "PhaseVocoder.h"
#include "dsp/transforms/FFT.h"
#include "maths/MathUtilities.h"
#include <math.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#include <cassert>
PhaseVocoder::PhaseVocoder(unsigned int n) :
m_n(n)
#include <iostream>
using std::cerr;
using std::endl;
PhaseVocoder::PhaseVocoder(int n, int hop) :
m_n(n),
m_hop(hop)
{
m_fft = new FFTReal(m_n);
m_realOut = new double[m_n];
m_imagOut = new double[m_n];
m_time = new double[m_n];
m_real = new double[m_n];
m_imag = new double[m_n];
m_phase = new double[m_n/2 + 1];
m_unwrapped = new double[m_n/2 + 1];
for (int i = 0; i < m_n/2 + 1; ++i) {
m_phase[i] = 0.0;
m_unwrapped[i] = 0.0;
}
reset();
}
PhaseVocoder::~PhaseVocoder()
{
delete [] m_realOut;
delete [] m_imagOut;
delete[] m_unwrapped;
delete[] m_phase;
delete[] m_real;
delete[] m_imag;
delete[] m_time;
delete m_fft;
}
void PhaseVocoder::FFTShift(unsigned int size, double *src)
void PhaseVocoder::FFTShift(double *src)
{
const int hs = size/2;
const int hs = m_n/2;
for (int i = 0; i < hs; ++i) {
double tmp = src[i];
src[i] = src[i + hs];
@ -46,34 +63,73 @@ void PhaseVocoder::FFTShift(unsigned int size, double *src)
}
}
void PhaseVocoder::process(double *src, double *mag, double *theta)
void PhaseVocoder::processTimeDomain(const double *src,
double *mag, double *theta,
double *unwrapped)
{
FFTShift( m_n, src);
m_fft->process(0, src, m_realOut, m_imagOut);
getMagnitude( m_n/2, mag, m_realOut, m_imagOut);
getPhase( m_n/2, theta, m_realOut, m_imagOut);
for (int i = 0; i < m_n; ++i) {
m_time[i] = src[i];
}
FFTShift(m_time);
m_fft->forward(m_time, m_real, m_imag);
getMagnitudes(mag);
getPhases(theta);
unwrapPhases(theta, unwrapped);
}
void PhaseVocoder::getMagnitude(unsigned int size, double *mag, double *real, double *imag)
void PhaseVocoder::processFrequencyDomain(const double *reals,
const double *imags,
double *mag, double *theta,
double *unwrapped)
{
unsigned int j;
for (int i = 0; i < m_n/2 + 1; ++i) {
m_real[i] = reals[i];
m_imag[i] = imags[i];
}
getMagnitudes(mag);
getPhases(theta);
unwrapPhases(theta, unwrapped);
}
for( j = 0; j < size; j++)
{
mag[ j ] = sqrt( real[ j ] * real[ j ] + imag[ j ] * imag[ j ]);
void PhaseVocoder::reset()
{
for (int i = 0; i < m_n/2 + 1; ++i) {
// m_phase stores the "previous" phase, so set to one step
// behind so that a signal with initial phase at zero matches
// the expected values. This is completely unnecessary for any
// analytical purpose, it's just tidier.
double omega = (2 * M_PI * m_hop * i) / m_n;
m_phase[i] = -omega;
m_unwrapped[i] = -omega;
}
}
void PhaseVocoder::getPhase(unsigned int size, double *theta, double *real, double *imag)
{
unsigned int k;
// Phase Angle "matlab" style
//Watch out for quadrant mapping !!!
for( k = 0; k < size; k++)
{
theta[ k ] = atan2( -imag[ k ], real[ k ]);
void PhaseVocoder::getMagnitudes(double *mag)
{
for (int i = 0; i < m_n/2 + 1; i++) {
mag[i] = sqrt(m_real[i] * m_real[i] + m_imag[i] * m_imag[i]);
}
}
void PhaseVocoder::getPhases(double *theta)
{
for (int i = 0; i < m_n/2 + 1; i++) {
theta[i] = atan2(m_imag[i], m_real[i]);
}
}
void PhaseVocoder::unwrapPhases(double *theta, double *unwrapped)
{
for (int i = 0; i < m_n/2 + 1; ++i) {
double omega = (2 * M_PI * m_hop * i) / m_n;
double expected = m_phase[i] + omega;
double error = MathUtilities::princarg(theta[i] - expected);
unwrapped[i] = m_unwrapped[i] + omega + error;
m_phase[i] = theta[i];
m_unwrapped[i] = unwrapped[i];
}
}

View File

@ -4,7 +4,7 @@
QM DSP Library
Centre for Digital Music, Queen Mary, University of London.
This file 2005-2006 Christian Landone.
This file 2005-2006 Christian Landone, copyright 2013 QMUL.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@ -18,25 +18,63 @@
class FFTReal;
class PhaseVocoder
class PhaseVocoder
{
public:
PhaseVocoder( unsigned int size );
PhaseVocoder(int size, int hop);
virtual ~PhaseVocoder();
void process( double* src, double* mag, double* theta);
/**
* Given one frame of time-domain samples, FFT and return the
* magnitudes, instantaneous phases, and unwrapped phases.
*
* src must have size values (where size is the frame size value
* as passed to the PhaseVocoder constructor), and should have
* been windowed as necessary by the caller (but not fft-shifted).
*
* mag, phase, and unwrapped must each be non-NULL and point to
* enough space for size/2 + 1 values. The redundant conjugate
* half of the output is not returned.
*/
void processTimeDomain(const double *src,
double *mag, double *phase, double *unwrapped);
/**
* Given one frame of frequency-domain samples, return the
* magnitudes, instantaneous phases, and unwrapped phases.
*
* reals and imags must each contain size/2+1 values (where size
* is the frame size value as passed to the PhaseVocoder
* constructor).
*
* mag, phase, and unwrapped must each be non-NULL and point to
* enough space for size/2+1 values.
*/
void processFrequencyDomain(const double *reals, const double *imags,
double *mag, double *phase, double *unwrapped);
/**
* Reset the stored phases to zero. Note that this may be
* necessary occasionally (depending on the application) to avoid
* loss of floating-point precision in the accumulated unwrapped
* phase values as they grow.
*/
void reset();
protected:
void getPhase(unsigned int size, double *theta, double *real, double *imag);
// void coreFFT( unsigned int NumSamples, double *RealIn, double* ImagIn, double *RealOut, double *ImagOut);
void getMagnitude( unsigned int size, double* mag, double* real, double* imag);
void FFTShift( unsigned int size, double* src);
void FFTShift(double *src);
void getMagnitudes(double *mag);
void getPhases(double *theta);
void unwrapPhases(double *theta, double *unwrapped);
unsigned int m_n;
int m_n;
int m_hop;
FFTReal *m_fft;
double *m_imagOut;
double *m_realOut;
double *m_time;
double *m_imag;
double *m_real;
double *m_phase;
double *m_unwrapped;
};
#endif

View File

@ -199,10 +199,15 @@ void Decimator::doAntiAlias(const float *src, double *dst, unsigned int length)
void Decimator::process(const double *src, double *dst)
{
if( m_decFactor != 1 )
{
doAntiAlias( src, decBuffer, m_inputLength );
if (m_decFactor == 1) {
for( unsigned int i = 0; i < m_outputLength; i++ ) {
dst[i] = src[i];
}
return;
}
doAntiAlias( src, decBuffer, m_inputLength );
unsigned idx = 0;
for( unsigned int i = 0; i < m_outputLength; i++ )
@ -213,10 +218,15 @@ void Decimator::process(const double *src, double *dst)
void Decimator::process(const float *src, float *dst)
{
if( m_decFactor != 1 )
{
doAntiAlias( src, decBuffer, m_inputLength );
if (m_decFactor == 1) {
for( unsigned int i = 0; i < m_outputLength; i++ ) {
dst[i] = src[i];
}
return;
}
doAntiAlias( src, decBuffer, m_inputLength );
unsigned idx = 0;
for( unsigned int i = 0; i < m_outputLength; i++ )

View File

@ -15,12 +15,15 @@
#ifndef DECIMATOR_H
#define DECIMATOR_H
class Decimator
/**
* Decimator carries out a fast downsample by a power-of-two
* factor. Only a limited number of factors are supported, from two to
* whatever getHighestSupportedFactor() returns. This is much faster
* than Resampler but has a worse signal-noise ratio.
*/
class Decimator
{
public:
void process( const double* src, double* dst );
void process( const float* src, float* dst );
/**
* Construct a Decimator to operate on input blocks of length
* inLength, with decimation factor decFactor. inLength should be
@ -34,11 +37,28 @@ public:
Decimator( unsigned int inLength, unsigned int decFactor );
virtual ~Decimator();
/**
* Process inLength samples (as supplied to constructor) from src
* and write inLength / decFactor samples to dst. Note that src
* and dst may be the same or overlap (an intermediate buffer is
* used).
*/
void process( const double* src, double* dst );
/**
* Process inLength samples (as supplied to constructor) from src
* and write inLength / decFactor samples to dst. Note that src
* and dst may be the same or overlap (an intermediate buffer is
* used).
*/
void process( const float* src, float* dst );
int getFactor() const { return m_decFactor; }
static int getHighestSupportedFactor() { return 8; }
private:
void resetFilter();
private:
void deInitialise();
void initialise( unsigned int inLength, unsigned int decFactor );
void doAntiAlias( const double* src, double* dst, unsigned int length );
@ -55,8 +75,8 @@ private:
double a[ 9 ];
double b[ 9 ];
double* decBuffer;
};
#endif //
#endif //

View File

@ -0,0 +1,160 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP Library
Centre for Digital Music, Queen Mary, University of London.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "DecimatorB.h"
#include "maths/MathUtilities.h"
#include <iostream>
using std::vector;
DecimatorB::DecimatorB(int inLength, int decFactor)
{
m_inputLength = 0;
m_outputLength = 0;
m_decFactor = 1;
m_aaBuffer = 0;
m_tmpBuffer = 0;
initialise(inLength, decFactor);
}
DecimatorB::~DecimatorB()
{
deInitialise();
}
void DecimatorB::initialise(int inLength, int decFactor)
{
m_inputLength = inLength;
m_decFactor = decFactor;
m_outputLength = m_inputLength / m_decFactor;
if (m_decFactor < 2 || !MathUtilities::isPowerOfTwo(m_decFactor)) {
std::cerr << "ERROR: DecimatorB::initialise: Decimation factor must be a power of 2 and at least 2 (was: " << m_decFactor << ")" << std::endl;
m_decFactor = 0;
return;
}
if (m_inputLength % m_decFactor != 0) {
std::cerr << "ERROR: DecimatorB::initialise: inLength must be a multiple of decimation factor (was: " << m_inputLength << ", factor is " << m_decFactor << ")" << std::endl;
m_decFactor = 0;
return;
}
m_aaBuffer = new double[m_inputLength];
m_tmpBuffer = new double[m_inputLength];
// Order 6 Butterworth lowpass filter
// Calculated using e.g. MATLAB butter(6, 0.5, 'low')
m_b[0] = 0.029588223638661;
m_b[1] = 0.177529341831965;
m_b[2] = 0.443823354579912;
m_b[3] = 0.591764472773216;
m_b[4] = 0.443823354579912;
m_b[5] = 0.177529341831965;
m_b[6] = 0.029588223638661;
m_a[0] = 1.000000000000000;
m_a[1] = 0.000000000000000;
m_a[2] = 0.777695961855673;
m_a[3] = 0.000000000000000;
m_a[4] = 0.114199425062434;
m_a[5] = 0.000000000000000;
m_a[6] = 0.001750925956183;
for (int factor = m_decFactor; factor > 1; factor /= 2) {
m_o.push_back(vector<double>(6, 0.0));
}
}
void DecimatorB::deInitialise()
{
delete [] m_aaBuffer;
delete [] m_tmpBuffer;
}
void DecimatorB::doAntiAlias(const double *src, double *dst, int length,
int filteridx)
{
vector<double> &o = m_o[filteridx];
for (int i = 0; i < length; i++) {
double input = src[i];
double output = input * m_b[0] + o[0];
o[0] = input * m_b[1] - output * m_a[1] + o[1];
o[1] = input * m_b[2] - output * m_a[2] + o[2];
o[2] = input * m_b[3] - output * m_a[3] + o[3];
o[3] = input * m_b[4] - output * m_a[4] + o[4];
o[4] = input * m_b[5] - output * m_a[5] + o[5];
o[5] = input * m_b[6] - output * m_a[6];
dst[i] = output;
}
}
void DecimatorB::doProcess()
{
int filteridx = 0;
int factorDone = 1;
int factorRemaining = m_decFactor;
while (factorDone < m_decFactor) {
doAntiAlias(m_tmpBuffer, m_aaBuffer,
m_inputLength / factorDone,
filteridx);
filteridx ++;
factorDone *= 2;
for (int i = 0; i < m_inputLength / factorDone; ++i) {
m_tmpBuffer[i] = m_aaBuffer[i * 2];
}
}
}
void DecimatorB::process(const double *src, double *dst)
{
if (m_decFactor == 0) return;
for (int i = 0; i < m_inputLength; ++i) {
m_tmpBuffer[i] = src[i];
}
doProcess();
for (int i = 0; i < m_outputLength; ++i) {
dst[i] = m_tmpBuffer[i];
}
}
void DecimatorB::process(const float *src, float *dst)
{
if (m_decFactor == 0) return;
for (int i = 0; i < m_inputLength; ++i) {
m_tmpBuffer[i] = src[i];
}
doProcess();
for (int i = 0; i < m_outputLength; ++i) {
dst[i] = m_tmpBuffer[i];
}
}

View File

@ -0,0 +1,64 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP Library
Centre for Digital Music, Queen Mary, University of London.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef DECIMATORB_H
#define DECIMATORB_H
#include <vector>
/**
* DecimatorB carries out a fast downsample by a power-of-two
* factor. It only knows how to decimate by a factor of 2, and will
* use repeated decimation for higher factors. A Butterworth filter of
* order 6 is used for the lowpass filter.
*/
class DecimatorB
{
public:
void process( const double* src, double* dst );
void process( const float* src, float* dst );
/**
* Construct a DecimatorB to operate on input blocks of length
* inLength, with decimation factor decFactor. inLength should be
* a multiple of decFactor. Output blocks will be of length
* inLength / decFactor.
*
* decFactor must be a power of two.
*/
DecimatorB(int inLength, int decFactor);
virtual ~DecimatorB();
int getFactor() const { return m_decFactor; }
private:
void deInitialise();
void initialise(int inLength, int decFactor);
void doAntiAlias(const double* src, double* dst, int length, int filteridx);
void doProcess();
int m_inputLength;
int m_outputLength;
int m_decFactor;
std::vector<std::vector<double> > m_o;
double m_a[7];
double m_b[7];
double *m_aaBuffer;
double *m_tmpBuffer;
};
#endif

View File

@ -0,0 +1,416 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP Library
Centre for Digital Music, Queen Mary, University of London.
This file by Chris Cannam.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "Resampler.h"
#include "maths/MathUtilities.h"
#include "base/KaiserWindow.h"
#include "base/SincWindow.h"
#include "thread/Thread.h"
#include <iostream>
#include <vector>
#include <map>
#include <cassert>
using std::vector;
using std::map;
using std::cerr;
using std::endl;
//#define DEBUG_RESAMPLER 1
//#define DEBUG_RESAMPLER_VERBOSE 1
Resampler::Resampler(int sourceRate, int targetRate) :
m_sourceRate(sourceRate),
m_targetRate(targetRate)
{
initialise(100, 0.02);
}
Resampler::Resampler(int sourceRate, int targetRate,
double snr, double bandwidth) :
m_sourceRate(sourceRate),
m_targetRate(targetRate)
{
initialise(snr, bandwidth);
}
Resampler::~Resampler()
{
delete[] m_phaseData;
}
// peakToPole -> length -> beta -> window
static map<double, map<int, map<double, vector<double> > > >
knownFilters;
static Mutex
knownFilterMutex;
void
Resampler::initialise(double snr, double bandwidth)
{
int higher = std::max(m_sourceRate, m_targetRate);
int lower = std::min(m_sourceRate, m_targetRate);
m_gcd = MathUtilities::gcd(lower, higher);
m_peakToPole = higher / m_gcd;
if (m_targetRate < m_sourceRate) {
// antialiasing filter, should be slightly below nyquist
m_peakToPole = m_peakToPole / (1.0 - bandwidth/2.0);
}
KaiserWindow::Parameters params =
KaiserWindow::parametersForBandwidth(snr, bandwidth, higher / m_gcd);
params.length =
(params.length % 2 == 0 ? params.length + 1 : params.length);
params.length =
(params.length > 200001 ? 200001 : params.length);
m_filterLength = params.length;
vector<double> filter;
knownFilterMutex.lock();
if (knownFilters[m_peakToPole][m_filterLength].find(params.beta) ==
knownFilters[m_peakToPole][m_filterLength].end()) {
KaiserWindow kw(params);
SincWindow sw(m_filterLength, m_peakToPole * 2);
filter = vector<double>(m_filterLength, 0.0);
for (int i = 0; i < m_filterLength; ++i) filter[i] = 1.0;
sw.cut(filter.data());
kw.cut(filter.data());
knownFilters[m_peakToPole][m_filterLength][params.beta] = filter;
}
filter = knownFilters[m_peakToPole][m_filterLength][params.beta];
knownFilterMutex.unlock();
int inputSpacing = m_targetRate / m_gcd;
int outputSpacing = m_sourceRate / m_gcd;
#ifdef DEBUG_RESAMPLER
cerr << "resample " << m_sourceRate << " -> " << m_targetRate
<< ": inputSpacing " << inputSpacing << ", outputSpacing "
<< outputSpacing << ": filter length " << m_filterLength
<< endl;
#endif
// Now we have a filter of (odd) length flen in which the lower
// sample rate corresponds to every n'th point and the higher rate
// to every m'th where n and m are higher and lower rates divided
// by their gcd respectively. So if x coordinates are on the same
// scale as our filter resolution, then source sample i is at i *
// (targetRate / gcd) and target sample j is at j * (sourceRate /
// gcd).
// To reconstruct a single target sample, we want a buffer (real
// or virtual) of flen values formed of source samples spaced at
// intervals of (targetRate / gcd), in our example case 3. This
// is initially formed with the first sample at the filter peak.
//
// 0 0 0 0 a 0 0 b 0
//
// and of course we have our filter
//
// f1 f2 f3 f4 f5 f6 f7 f8 f9
//
// We take the sum of products of non-zero values from this buffer
// with corresponding values in the filter
//
// a * f5 + b * f8
//
// Then we drop (sourceRate / gcd) values, in our example case 4,
// from the start of the buffer and fill until it has flen values
// again
//
// a 0 0 b 0 0 c 0 0
//
// repeat to reconstruct the next target sample
//
// a * f1 + b * f4 + c * f7
//
// and so on.
//
// Above I said the buffer could be "real or virtual" -- ours is
// virtual. We don't actually store all the zero spacing values,
// except for padding at the start; normally we store only the
// values that actually came from the source stream, along with a
// phase value that tells us how many virtual zeroes there are at
// the start of the virtual buffer. So the two examples above are
//
// 0 a b [ with phase 1 ]
// a b c [ with phase 0 ]
//
// Having thus broken down the buffer so that only the elements we
// need to multiply are present, we can also unzip the filter into
// every-nth-element subsets at each phase, allowing us to do the
// filter multiplication as a simply vector multiply. That is, rather
// than store
//
// f1 f2 f3 f4 f5 f6 f7 f8 f9
//
// we store separately
//
// f1 f4 f7
// f2 f5 f8
// f3 f6 f9
//
// Each time we complete a multiply-and-sum, we need to work out
// how many (real) samples to drop from the start of our buffer,
// and how many to add at the end of it for the next multiply. We
// know we want to drop enough real samples to move along by one
// computed output sample, which is our outputSpacing number of
// virtual buffer samples. Depending on the relationship between
// input and output spacings, this may mean dropping several real
// samples, one real sample, or none at all (and simply moving to
// a different "phase").
m_phaseData = new Phase[inputSpacing];
for (int phase = 0; phase < inputSpacing; ++phase) {
Phase p;
p.nextPhase = phase - outputSpacing;
while (p.nextPhase < 0) p.nextPhase += inputSpacing;
p.nextPhase %= inputSpacing;
p.drop = int(ceil(std::max(0.0, double(outputSpacing - phase))
/ inputSpacing));
int filtZipLength = int(ceil(double(m_filterLength - phase)
/ inputSpacing));
for (int i = 0; i < filtZipLength; ++i) {
p.filter.push_back(filter[i * inputSpacing + phase]);
}
m_phaseData[phase] = p;
}
#ifdef DEBUG_RESAMPLER
int cp = 0;
int totDrop = 0;
for (int i = 0; i < inputSpacing; ++i) {
cerr << "phase = " << cp << ", drop = " << m_phaseData[cp].drop
<< ", filter length = " << m_phaseData[cp].filter.size()
<< ", next phase = " << m_phaseData[cp].nextPhase << endl;
totDrop += m_phaseData[cp].drop;
cp = m_phaseData[cp].nextPhase;
}
cerr << "total drop = " << totDrop << endl;
#endif
// The May implementation of this uses a pull model -- we ask the
// resampler for a certain number of output samples, and it asks
// its source stream for as many as it needs to calculate
// those. This means (among other things) that the source stream
// can be asked for enough samples up-front to fill the buffer
// before the first output sample is generated.
//
// In this implementation we're using a push model in which a
// certain number of source samples is provided and we're asked
// for as many output samples as that makes available. But we
// can't return any samples from the beginning until half the
// filter length has been provided as input. This means we must
// either return a very variable number of samples (none at all
// until the filter fills, then half the filter length at once) or
// else have a lengthy declared latency on the output. We do the
// latter. (What do other implementations do?)
//
// We want to make sure the first "real" sample will eventually be
// aligned with the centre sample in the filter (it's tidier, and
// easier to do diagnostic calculations that way). So we need to
// pick the initial phase and buffer fill accordingly.
//
// Example: if the inputSpacing is 2, outputSpacing is 3, and
// filter length is 7,
//
// x x x x a b c ... input samples
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
// i j k l ... output samples
// [--------|--------] <- filter with centre mark
//
// Let h be the index of the centre mark, here 3 (generally
// int(filterLength/2) for odd-length filters).
//
// The smallest n such that h + n * outputSpacing > filterLength
// is 2 (that is, ceil((filterLength - h) / outputSpacing)), and
// (h + 2 * outputSpacing) % inputSpacing == 1, so the initial
// phase is 1.
//
// To achieve our n, we need to pre-fill the "virtual" buffer with
// 4 zero samples: the x's above. This is int((h + n *
// outputSpacing) / inputSpacing). It's the phase that makes this
// buffer get dealt with in such a way as to give us an effective
// index for sample a of 9 rather than 8 or 10 or whatever.
//
// This gives us output latency of 2 (== n), i.e. output samples i
// and j will appear before the one in which input sample a is at
// the centre of the filter.
int h = int(m_filterLength / 2);
int n = ceil(double(m_filterLength - h) / outputSpacing);
m_phase = (h + n * outputSpacing) % inputSpacing;
int fill = (h + n * outputSpacing) / inputSpacing;
m_latency = n;
m_buffer = vector<double>(fill, 0);
m_bufferOrigin = 0;
#ifdef DEBUG_RESAMPLER
cerr << "initial phase " << m_phase << " (as " << (m_filterLength/2) << " % " << inputSpacing << ")"
<< ", latency " << m_latency << endl;
#endif
}
double
Resampler::reconstructOne()
{
Phase &pd = m_phaseData[m_phase];
double v = 0.0;
int n = pd.filter.size();
assert(n + m_bufferOrigin <= (int)m_buffer.size());
const double *const __restrict__ buf = m_buffer.data() + m_bufferOrigin;
const double *const __restrict__ filt = pd.filter.data();
for (int i = 0; i < n; ++i) {
// NB gcc can only vectorize this with -ffast-math
v += buf[i] * filt[i];
}
m_bufferOrigin += pd.drop;
m_phase = pd.nextPhase;
return v;
}
int
Resampler::process(const double *src, double *dst, int n)
{
for (int i = 0; i < n; ++i) {
m_buffer.push_back(src[i]);
}
int maxout = int(ceil(double(n) * m_targetRate / m_sourceRate));
int outidx = 0;
#ifdef DEBUG_RESAMPLER
cerr << "process: buf siz " << m_buffer.size() << " filt siz for phase " << m_phase << " " << m_phaseData[m_phase].filter.size() << endl;
#endif
double scaleFactor = (double(m_targetRate) / m_gcd) / m_peakToPole;
while (outidx < maxout &&
m_buffer.size() >= m_phaseData[m_phase].filter.size() + m_bufferOrigin) {
dst[outidx] = scaleFactor * reconstructOne();
outidx++;
}
m_buffer = vector<double>(m_buffer.begin() + m_bufferOrigin, m_buffer.end());
m_bufferOrigin = 0;
return outidx;
}
vector<double>
Resampler::process(const double *src, int n)
{
int maxout = int(ceil(double(n) * m_targetRate / m_sourceRate));
vector<double> out(maxout, 0.0);
int got = process(src, out.data(), n);
assert(got <= maxout);
if (got < maxout) out.resize(got);
return out;
}
vector<double>
Resampler::resample(int sourceRate, int targetRate, const double *data, int n)
{
Resampler r(sourceRate, targetRate);
int latency = r.getLatency();
// latency is the output latency. We need to provide enough
// padding input samples at the end of input to guarantee at
// *least* the latency's worth of output samples. that is,
int inputPad = int(ceil((double(latency) * sourceRate) / targetRate));
// that means we are providing this much input in total:
int n1 = n + inputPad;
// and obtaining this much output in total:
int m1 = int(ceil((double(n1) * targetRate) / sourceRate));
// in order to return this much output to the user:
int m = int(ceil((double(n) * targetRate) / sourceRate));
#ifdef DEBUG_RESAMPLER
cerr << "n = " << n << ", sourceRate = " << sourceRate << ", targetRate = " << targetRate << ", m = " << m << ", latency = " << latency << ", inputPad = " << inputPad << ", m1 = " << m1 << ", n1 = " << n1 << ", n1 - n = " << n1 - n << endl;
#endif
vector<double> pad(n1 - n, 0.0);
vector<double> out(m1 + 1, 0.0);
int gotData = r.process(data, out.data(), n);
int gotPad = r.process(pad.data(), out.data() + gotData, pad.size());
int got = gotData + gotPad;
#ifdef DEBUG_RESAMPLER
cerr << "resample: " << n << " in, " << pad.size() << " padding, " << got << " out (" << gotData << " data, " << gotPad << " padding, latency = " << latency << ")" << endl;
#endif
#ifdef DEBUG_RESAMPLER_VERBOSE
int printN = 50;
cerr << "first " << printN << " in:" << endl;
for (int i = 0; i < printN && i < n; ++i) {
if (i % 5 == 0) cerr << endl << i << "... ";
cerr << data[i] << " ";
}
cerr << endl;
#endif
int toReturn = got - latency;
if (toReturn > m) toReturn = m;
vector<double> sliced(out.begin() + latency,
out.begin() + latency + toReturn);
#ifdef DEBUG_RESAMPLER_VERBOSE
cerr << "first " << printN << " out (after latency compensation), length " << sliced.size() << ":";
for (int i = 0; i < printN && i < sliced.size(); ++i) {
if (i % 5 == 0) cerr << endl << i << "... ";
cerr << sliced[i] << " ";
}
cerr << endl;
#endif
return sliced;
}

View File

@ -0,0 +1,102 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP Library
Centre for Digital Music, Queen Mary, University of London.
This file by Chris Cannam.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef RESAMPLER_H
#define RESAMPLER_H
#include <vector>
/**
* Resampler resamples a stream from one integer sample rate to
* another (arbitrary) rate, using a kaiser-windowed sinc filter. The
* results and performance are pretty similar to libraries such as
* libsamplerate, though this implementation does not support
* time-varying ratios (the ratio is fixed on construction).
*
* See also Decimator, which is faster and rougher but supports only
* power-of-two downsampling factors.
*/
class Resampler
{
public:
/**
* Construct a Resampler to resample from sourceRate to
* targetRate.
*/
Resampler(int sourceRate, int targetRate);
/**
* Construct a Resampler to resample from sourceRate to
* targetRate, using the given filter parameters.
*/
Resampler(int sourceRate, int targetRate,
double snr, double bandwidth);
virtual ~Resampler();
/**
* Read n input samples from src and write resampled data to
* dst. The return value is the number of samples written, which
* will be no more than ceil((n * targetRate) / sourceRate). The
* caller must ensure the dst buffer has enough space for the
* samples returned.
*/
int process(const double *src, double *dst, int n);
/**
* Read n input samples from src and return resampled data by
* value.
*/
std::vector<double> process(const double *src, int n);
/**
* Return the number of samples of latency at the output due by
* the filter. (That is, the output will be delayed by this number
* of samples relative to the input.)
*/
int getLatency() const { return m_latency; }
/**
* Carry out a one-off resample of a single block of n
* samples. The output is latency-compensated.
*/
static std::vector<double> resample
(int sourceRate, int targetRate, const double *data, int n);
private:
int m_sourceRate;
int m_targetRate;
int m_gcd;
int m_filterLength;
int m_bufferLength;
int m_latency;
double m_peakToPole;
struct Phase {
int nextPhase;
std::vector<double> filter;
int drop;
};
Phase *m_phaseData;
int m_phase;
std::vector<double> m_buffer;
int m_bufferOrigin;
void initialise(double, double);
double reconstructOne();
};
#endif

View File

@ -51,7 +51,7 @@ void ClusterMeltSegmenter::initialise(int fs)
if (featureType == FEATURE_TYPE_CONSTQ ||
featureType == FEATURE_TYPE_CHROMA) {
// run internal processing at 11025 or thereabouts
int internalRate = 11025;
int decimationFactor = samplerate / internalRate;
@ -77,11 +77,11 @@ void ClusterMeltSegmenter::initialise(int fs)
constq = new ConstantQ(config);
constq->sparsekernel();
ncoeff = constq->getK();
fft = new FFTReal(constq->getfftlength());
} else if (featureType == FEATURE_TYPE_MFCC) {
// run internal processing at 22050 or thereabouts
@ -110,7 +110,7 @@ void ClusterMeltSegmenter::initialise(int fs)
}
}
ClusterMeltSegmenter::~ClusterMeltSegmenter()
ClusterMeltSegmenter::~ClusterMeltSegmenter()
{
delete window;
delete constq;
@ -164,7 +164,7 @@ void ClusterMeltSegmenter::extractFeaturesConstQ(const double* samples, int nsam
vector<double> cq(ncoeff);
for (int i = 0; i < ncoeff; ++i) cq[i] = 0.0;
const double *psource = samples;
int pcount = nsamples;
@ -174,9 +174,9 @@ void ClusterMeltSegmenter::extractFeaturesConstQ(const double* samples, int nsam
decimator->process(samples, decout);
psource = decout;
}
int origin = 0;
// std::cerr << "nsamples = " << nsamples << ", pcount = " << pcount << std::endl;
int frames = 0;
@ -208,11 +208,11 @@ void ClusterMeltSegmenter::extractFeaturesConstQ(const double* samples, int nsam
}
window->cut(frame);
fft->process(false, frame, real, imag);
fft->forward(frame, real, imag);
constq->process(real, imag, cqre, cqim);
for (int i = 0; i < ncoeff; ++i) {
cq[i] += sqrt(cqre[i] * cqre[i] + cqim[i] * cqim[i]);
}
@ -255,7 +255,7 @@ void ClusterMeltSegmenter::extractFeaturesMFCC(const double* samples, int nsampl
vector<double> cc(ncoeff);
for (int i = 0; i < ncoeff; ++i) cc[i] = 0.0;
const double *psource = samples;
int pcount = nsamples;
@ -287,7 +287,7 @@ void ClusterMeltSegmenter::extractFeaturesMFCC(const double* samples, int nsampl
}
mfcc->process(frame, ccout);
for (int i = 0; i < ncoeff; ++i) {
cc[i] += ccout[i];
}
@ -330,44 +330,44 @@ void ClusterMeltSegmenter::segment()
decimator = 0;
if (features.size() < histogramLength) return;
/*
/*
std::cerr << "ClusterMeltSegmenter::segment: have " << features.size()
<< " features with " << features[0].size() << " coefficients (ncoeff = " << ncoeff << ", ncomponents = " << ncomponents << ")" << std::endl;
*/
// copy the features to a native array and use the existing C segmenter...
double** arrFeatures = new double*[features.size()];
double** arrFeatures = new double*[features.size()];
for (int i = 0; i < features.size(); i++)
{
if (featureType == FEATURE_TYPE_UNKNOWN) {
arrFeatures[i] = new double[features[0].size()];
for (int j = 0; j < features[0].size(); j++)
arrFeatures[i][j] = features[i][j];
arrFeatures[i][j] = features[i][j];
} else {
arrFeatures[i] = new double[ncoeff+1]; // allow space for the normalised envelope
for (int j = 0; j < ncoeff; j++)
arrFeatures[i][j] = features[i][j];
arrFeatures[i][j] = features[i][j];
}
}
q = new int[features.size()];
if (featureType == FEATURE_TYPE_UNKNOWN ||
featureType == FEATURE_TYPE_MFCC)
cluster_segment(q, arrFeatures, features.size(), features[0].size(), nHMMStates, histogramLength,
cluster_segment(q, arrFeatures, features.size(), features[0].size(), nHMMStates, histogramLength,
nclusters, neighbourhoodLimit);
else
constq_segment(q, arrFeatures, features.size(), nbins, ncoeff, featureType,
constq_segment(q, arrFeatures, features.size(), nbins, ncoeff, featureType,
nHMMStates, histogramLength, nclusters, neighbourhoodLimit);
// convert the cluster assignment sequence to a segmentation
makeSegmentation(q, features.size());
makeSegmentation(q, features.size());
// de-allocate arrays
delete [] q;
for (int i = 0; i < features.size(); i++)
delete [] arrFeatures[i];
delete [] arrFeatures;
// clear the features
clear();
}
@ -377,11 +377,11 @@ void ClusterMeltSegmenter::makeSegmentation(int* q, int len)
segmentation.segments.clear();
segmentation.nsegtypes = nclusters;
segmentation.samplerate = samplerate;
Segment segment;
segment.start = 0;
segment.type = q[0];
for (int i = 1; i < len; i++)
{
if (q[i] != q[i-1])

View File

@ -31,12 +31,12 @@ class ClusterMeltSegmenterParams
// defaults are sensible for 11025Hz with 0.2 second hopsize
{
public:
ClusterMeltSegmenterParams() :
ClusterMeltSegmenterParams() :
featureType(FEATURE_TYPE_CONSTQ),
hopSize(0.2),
windowSize(0.6),
fmin(62),
fmax(16000),
fmax(16000),
nbins(8),
ncomponents(20),
nHMMStates(40),
@ -72,34 +72,34 @@ public:
protected:
void makeSegmentation(int* q, int len);
void extractFeaturesConstQ(const double *, int);
void extractFeaturesMFCC(const double *, int);
Window<double> *window;
FFTReal *fft;
ConstantQ* constq;
ConstantQ* constq;
MFCC* mfcc;
model_t* model; // the HMM
int* q; // the decoded HMM state sequence
vector<vector<double> > histograms;
feature_types featureType;
vector<vector<double> > histograms;
feature_types featureType;
double hopSize; // in seconds
double windowSize; // in seconds
// constant-Q parameters
int fmin;
int fmax;
int nbins;
int ncoeff;
// PCA parameters
int ncomponents;
// HMM parameters
int nHMMStates;
// clustering parameters
int nclusters;
int histogramLength;

View File

@ -19,13 +19,13 @@
ostream& operator<<(ostream& os, const Segmentation& s)
{
os << "structure_name : begin_time end_time\n";
for (int i = 0; i < s.segments.size(); i++)
{
Segment seg = s.segments[i];
os << std::fixed << seg.type << ':' << '\t' << std::setprecision(6) << seg.start / static_cast<double>(s.samplerate)
os << std::fixed << seg.type << ':' << '\t' << std::setprecision(6) << seg.start / static_cast<double>(s.samplerate)
<< '\t' << std::setprecision(6) << seg.end / static_cast<double>(s.samplerate) << "\n";
}
return os;
}

View File

@ -35,7 +35,7 @@ class Segmentation
public:
int nsegtypes; // number of segment types, so possible types are {0,1,...,nsegtypes-1}
int samplerate;
vector<Segment> segments;
vector<Segment> segments;
};
ostream& operator<<(ostream& os, const Segmentation& s);
@ -52,7 +52,7 @@ public:
virtual void segment() = 0; // call once all the features have been extracted
virtual void segment(int m) = 0; // specify desired number of segment-types
virtual void clear() { features.clear(); }
const Segmentation& getSegmentation() const { return segmentation; }
const Segmentation& getSegmentation() const { return segmentation; }
protected:
vector<vector<double> > features;
Segmentation segmentation;

View File

@ -25,7 +25,7 @@ double kldist(double* a, double* b, int n) {
because a, b represent probability distributions */
double q, d;
int i;
d = 0;
for (i = 0; i < n; i++)
{
@ -38,8 +38,8 @@ double kldist(double* a, double* b, int n) {
d += b[i] * log(b[i] / q);
}
}
return d;
}
return d;
}
void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l, int *c) {
double lambda, sum, beta, logsumexp, maxlp;
@ -48,9 +48,9 @@ void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l,
int** nc; /* neighbour counts for each histogram */
double** lp; /* soft assignment probs for each histogram */
int* oldc; /* previous hard assignments (to check convergence) */
/* NB h is passed as a 1d row major array */
/* parameter values */
lambda = DEFAULT_LAMBDA;
if (l > 0)
@ -60,22 +60,22 @@ void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l,
B = 2 * limit + 1;
maxiter0 = 20; /* number of iterations at initial temperature */
maxiter1 = 5; /* number of iterations at subsequent temperatures */
/* allocate memory */
/* allocate memory */
cl = (double**) malloc(k*sizeof(double*));
for (i= 0; i < k; i++)
cl[i] = (double*) malloc(m*sizeof(double));
nc = (int**) malloc(n*sizeof(int*));
for (i= 0; i < n; i++)
nc[i] = (int*) malloc(k*sizeof(int));
lp = (double**) malloc(n*sizeof(double*));
for (i= 0; i < n; i++)
lp[i] = (double*) malloc(k*sizeof(double));
oldc = (int*) malloc(n * sizeof(int));
/* initialise */
for (i = 0; i < k; i++)
{
@ -90,40 +90,40 @@ void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l,
{
cl[i][j] /= sum; /* normalise */
}
}
}
//print_array(cl, k, m);
for (i = 0; i < n; i++)
c[i] = 1; /* initially assign all histograms to cluster 1 */
for (a = 0; a < t; a++)
{
beta = Bsched[a];
if (a == 0)
maxiter = maxiter0;
else
maxiter = maxiter1;
for (it = 0; it < maxiter; it++)
{
//if (it == maxiter - 1)
// mexPrintf("hasn't converged after %d iterations\n", maxiter);
for (i = 0; i < n; i++)
{
/* save current hard assignments */
oldc[i] = c[i];
/* calculate soft assignment logprobs for each cluster */
sum = 0;
for (j = 0; j < k; j++)
{
lp[i][ j] = -beta * kldist(cl[j], &h[i*m], m);
/* update matching neighbour counts for this histogram, based on current hard assignments */
/* old version:
nc[i][j] = 0;
nc[i][j] = 0;
if (i >= limit && i <= n - 1 - limit)
{
for (b = i - limit; b <= i + limit; b++)
@ -144,14 +144,14 @@ void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l,
for (b = b0; b <= b1; b++)
if (c[b] == j+1)
nc[i][j]--;
sum += exp(lp[i][j]);
}
/* normalise responsibilities and add duration logprior */
logsumexp = log(sum);
for (j = 0; j < k; j++)
lp[i][j] -= logsumexp + lambda * nc[i][j];
lp[i][j] -= logsumexp + lambda * nc[i][j];
}
//print_array(lp, n, k);
/*
@ -160,10 +160,10 @@ void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l,
for (j = 0; j < k; j++)
mexPrintf("%d ", nc[i][j]);
mexPrintf("\n");
}
}
*/
/* update the assignments now that we know the duration priors
based on the current assignments */
for (i = 0; i < n; i++)
@ -177,14 +177,14 @@ void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l,
c[i] = j+1;
}
}
/* break if assignments haven't changed */
i = 0;
while (i < n && oldc[i] == c[i])
i++;
if (i == n)
break;
/* update reference histograms now we know new responsibilities */
for (j = 0; j < k; j++)
{
@ -194,21 +194,21 @@ void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l,
for (i = 0; i < n; i++)
{
cl[j][b] += exp(lp[i][j]) * h[i*m+b];
}
}
}
sum = 0;
sum = 0;
for (i = 0; i < n; i++)
sum += exp(lp[i][j]);
for (b = 0; b < m; b++)
cl[j][b] /= sum; /* normalise */
}
}
//print_array(cl, k, m);
//mexPrintf("\n\n");
}
}
/* free memory */
for (i = 0; i < k; i++)
free(cl[i]);
@ -219,7 +219,7 @@ void cluster_melt(double *h, int m, int n, double *Bsched, int t, int k, int l,
for (i = 0; i < n; i++)
free(lp[i]);
free(lp);
free(oldc);
free(oldc);
}

View File

@ -25,7 +25,7 @@ void cq2chroma(double** cq, int nframes, int ncoeff, int bins, double** chroma)
int t, b, oct, ix;
//double maxchroma; /* max chroma value at each time, for normalisation */
//double sum; /* for normalisation */
for (t = 0; t < nframes; t++)
{
for (b = 0; b < bins; b++)
@ -50,7 +50,7 @@ void cq2chroma(double** cq, int nframes, int ncoeff, int bins, double** chroma)
maxchroma = chroma[t][b];
if (maxchroma > 0)
for (b = 0; b < bins; b++)
chroma[t][b] /= maxchroma;
chroma[t][b] /= maxchroma;
*/
}
}
@ -62,13 +62,13 @@ void mpeg7_constq(double** features, int nframes, int ncoeff)
double ss;
double env;
double maxenv = 0;
/* convert const-Q features to dB scale */
for (i = 0; i < nframes; i++)
for (j = 0; j < ncoeff; j++)
features[i][j] = 10.0 * log10(features[i][j]+DBL_EPSILON);
/* normalise each feature vector and add the norm as an extra feature dimension */
/* normalise each feature vector and add the norm as an extra feature dimension */
for (i = 0; i < nframes; i++)
{
ss = 0;
@ -80,10 +80,10 @@ void mpeg7_constq(double** features, int nframes, int ncoeff)
features[i][ncoeff] = env;
if (env > maxenv)
maxenv = env;
}
}
/* normalise the envelopes */
for (i = 0; i < nframes; i++)
features[i][ncoeff] /= maxenv;
features[i][ncoeff] /= maxenv;
}
/* return histograms h[nx*m] of data x[nx] into m bins using a sliding window of length h_len (MUST BE ODD) */
@ -94,7 +94,7 @@ void create_histograms(int* x, int nx, int m, int hlen, double* h)
int i, j, t;
double norm;
for (i = 0; i < nx*m; i++)
for (i = 0; i < nx*m; i++)
h[i] = 0;
for (i = hlen/2; i < nx-hlen/2; i++)
@ -109,7 +109,7 @@ void create_histograms(int* x, int nx, int m, int hlen, double* h)
for (j = 0; j < m; j++)
h[i*m+j] /= norm;
}
/* duplicate histograms at beginning and end to create one histogram for each data value supplied */
for (i = 0; i < hlen/2; i++)
for (j = 0; j < m; j++)
@ -120,11 +120,11 @@ void create_histograms(int* x, int nx, int m, int hlen, double* h)
}
/* segment using HMM and then histogram clustering */
void cluster_segment(int* q, double** features, int frames_read, int feature_length, int nHMM_states,
void cluster_segment(int* q, double** features, int frames_read, int feature_length, int nHMM_states,
int histogram_length, int nclusters, int neighbour_limit)
{
int i, j;
/*****************************/
if (0) {
/* try just using the predominant bin number as a 'decoded state' */
@ -137,60 +137,60 @@ void cluster_segment(int* q, double** features, int frames_read, int feature_len
maxval = 0;
for (j = 0; j < feature_length; j++)
{
if (features[i][j] > maxval)
if (features[i][j] > maxval)
{
maxval = features[i][j];
maxbin = j;
}
}
}
if (maxval > chroma_thresh)
q[i] = maxbin;
else
q[i] = feature_length;
}
}
if (1) {
/*****************************/
/* scale all the features to 'balance covariances' during HMM training */
double scale = 10;
for (i = 0; i < frames_read; i++)
for (j = 0; j < feature_length; j++)
features[i][j] *= scale;
/* train an HMM on the features */
/* create a model */
model_t* model = hmm_init(features, frames_read, feature_length, nHMM_states);
/* train the model */
hmm_train(features, frames_read, model);
/*
/*
printf("\n\nafter training:\n");
hmm_print(model);
*/
*/
/* decode the hidden state sequence */
viterbi_decode(features, frames_read, model, q);
viterbi_decode(features, frames_read, model, q);
hmm_close(model);
/*****************************/
}
/*****************************/
/*
fprintf(stderr, "HMM state sequence:\n");
for (i = 0; i < frames_read; i++)
fprintf(stderr, "%d ", q[i]);
fprintf(stderr, "\n\n");
*/
/* create histograms of states */
double* h = (double*) malloc(frames_read*nHMM_states*sizeof(double)); /* vector in row major order */
create_histograms(q, frames_read, nHMM_states, histogram_length, h);
/* cluster the histograms */
int nbsched = 20; /* length of inverse temperature schedule */
double* bsched = (double*) malloc(nbsched*sizeof(double)); /* inverse temperature schedule */
@ -200,39 +200,39 @@ void cluster_segment(int* q, double** features, int frames_read, int feature_len
for (i = 1; i < nbsched; i++)
bsched[i] = alpha * bsched[i-1];
cluster_melt(h, nHMM_states, frames_read, bsched, nbsched, nclusters, neighbour_limit, q);
/* now q holds a sequence of cluster assignments */
free(h);
free(h);
free(bsched);
}
/* segment constant-Q or chroma features */
void constq_segment(int* q, double** features, int frames_read, int bins, int ncoeff, int feature_type,
void constq_segment(int* q, double** features, int frames_read, int bins, int ncoeff, int feature_type,
int nHMM_states, int histogram_length, int nclusters, int neighbour_limit)
{
int feature_length;
double** chroma;
int i;
if (feature_type == FEATURE_TYPE_CONSTQ)
{
/* fprintf(stderr, "Converting to dB and normalising...\n");
*/
*/
mpeg7_constq(features, frames_read, ncoeff);
/*
/*
fprintf(stderr, "Running PCA...\n");
*/
*/
/* do PCA on the features (but not the envelope) */
int ncomponents = 20;
pca_project(features, frames_read, ncoeff, ncomponents);
/* copy the envelope so that it immediatly follows the chosen components */
for (i = 0; i < frames_read; i++)
features[i][ncomponents] = features[i][ncoeff];
features[i][ncomponents] = features[i][ncoeff];
feature_length = ncomponents + 1;
/**************************************
//TEST
// feature file name
@ -241,7 +241,7 @@ void constq_segment(int* q, double** features, int frames_read, int bins, int nc
strcpy(file_name, dir);
strcat(file_name, trackname);
strcat(file_name, "_features_c20r8h0.2f0.6.mat");
// get the features from Matlab from mat-file
int frames_in_file;
readmatarray_size(file_name, 2, &frames_in_file, &feature_length);
@ -254,27 +254,27 @@ void constq_segment(int* q, double** features, int frames_read, int bins, int nc
features[frames_read-missing_frames][i] = features[frames_read-missing_frames-1][i];
--missing_frames;
}
free(file_name);
******************************************/
cluster_segment(q, features, frames_read, feature_length, nHMM_states, histogram_length, nclusters, neighbour_limit);
}
if (feature_type == FEATURE_TYPE_CHROMA)
{
/*
fprintf(stderr, "Converting to chroma features...\n");
*/
*/
/* convert constant-Q to normalised chroma features */
chroma = (double**) malloc(frames_read*sizeof(double*));
for (i = 0; i < frames_read; i++)
chroma[i] = (double*) malloc(bins*sizeof(double));
cq2chroma(features, frames_read, ncoeff, bins, chroma);
feature_length = bins;
cluster_segment(q, chroma, frames_read, feature_length, nHMM_states, histogram_length, nclusters, neighbour_limit);
for (i = 0; i < frames_read; i++)
free(chroma[i]);
free(chroma);

View File

@ -38,10 +38,10 @@ void cq2chroma(double** cq, int nframes, int ncoeff, int bins, double** chroma);
void create_histograms(int* x, int nx, int m, int hlen, double* h);
void cluster_segment(int* q, double** features, int frames_read, int feature_length, int nHMM_states,
void cluster_segment(int* q, double** features, int frames_read, int feature_length, int nHMM_states,
int histogram_length, int nclusters, int neighbour_limit);
void constq_segment(int* q, double** features, int frames_read, int bins, int ncoeff, int feature_type,
void constq_segment(int* q, double** features, int frames_read, int bins, int ncoeff, int feature_type,
int nHMM_states, int histogram_length, int nclusters, int neighbour_limit);
#ifdef __cplusplus

View File

@ -34,10 +34,10 @@ typedef struct segmentation_t
segment_t* segments;
} segmentation_t;
typedef enum
{
FEATURE_TYPE_UNKNOWN = 0,
FEATURE_TYPE_CONSTQ = 1,
typedef enum
{
FEATURE_TYPE_UNKNOWN = 0,
FEATURE_TYPE_CONSTQ = 1,
FEATURE_TYPE_CHROMA = 2,
FEATURE_TYPE_MFCC = 3
} feature_types;

View File

@ -6,6 +6,14 @@
Centre for Digital Music, Queen Mary, University of London.
This file 2005-2006 Christian Landone.
Modifications:
- delta threshold
Description: add delta threshold used as offset in the smoothed
detection function
Author: Mathieu Barthet
Date: June 2010
This program 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 2 of the
@ -25,7 +33,7 @@
DFProcess::DFProcess( DFProcConfig Config )
{
filtSrc = NULL;
filtDst = NULL;
filtDst = NULL;
m_filtScratchIn = NULL;
m_filtScratchOut = NULL;
@ -51,13 +59,16 @@ void DFProcess::initialise( DFProcConfig Config )
filtSrc = new double[ m_length ];
filtDst = new double[ m_length ];
//Low Pass Smoothing Filter Config
m_FilterConfigParams.ord = Config.LPOrd;
m_FilterConfigParams.ACoeffs = Config.LPACoeffs;
m_FilterConfigParams.BCoeffs = Config.LPBCoeffs;
m_FiltFilt = new FiltFilt( m_FilterConfigParams );
//add delta threshold
m_delta = Config.delta;
}
void DFProcess::deInitialise()
@ -115,7 +126,7 @@ void DFProcess::medianFilter(double *src, double *dst)
{
if (index >= m_length) break;
l = 0;
for( j = i; j < ( i + m_winPost + m_winPre + 1); j++)
{
@ -139,15 +150,17 @@ void DFProcess::medianFilter(double *src, double *dst)
l++;
}
scratch[ index++ ] = MathUtilities::median( y, l);
scratch[ index++ ] = MathUtilities::median( y, l);
}
for( i = 0; i < m_length; i++ )
{
val = src[ i ] - scratch[ i ];// - 0.033;
//add a delta threshold used as an offset when computing the smoothed detection function
//(helps to discard noise when detecting peaks)
val = src[ i ] - scratch[ i ] - m_delta;
if( m_isMedianPositive )
{
if( val > 0 )
@ -164,7 +177,7 @@ void DFProcess::medianFilter(double *src, double *dst)
dst[ i ] = val;
}
}
delete [] y;
delete [] scratch;
}
@ -180,8 +193,8 @@ void DFProcess::removeDCNormalize( double *src, double*dst )
MathUtilities::getAlphaNorm( src, m_length, m_alphaNormParam, &DFAlphaNorm );
for(int i = 0; i< m_length; i++)
for( unsigned int i = 0; i< m_length; i++)
{
dst[ i ] = ( src[ i ] - DFMin ) / DFAlphaNorm;
dst[ i ] = ( src[ i ] - DFMin ) / DFAlphaNorm;
}
}

View File

@ -6,6 +6,14 @@
Centre for Digital Music, Queen Mary, University of London.
This file 2005-2006 Christian Landone.
Modifications:
- delta threshold
Description: add delta threshold used as offset in the smoothed
detection function
Author: Mathieu Barthet
Date: June 2010
This program 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 2 of the
@ -20,17 +28,31 @@
#include "FiltFilt.h"
struct DFProcConfig{
unsigned int length;
unsigned int LPOrd;
double *LPACoeffs;
double *LPBCoeffs;
unsigned int length;
unsigned int LPOrd;
double *LPACoeffs;
double *LPBCoeffs;
unsigned int winPre;
unsigned int winPost;
unsigned int winPost;
double AlphaNormParam;
bool isMedianPositive;
float delta; //delta threshold used as an offset when computing the smoothed detection function
DFProcConfig() :
length(0),
LPOrd(0),
LPACoeffs(NULL),
LPBCoeffs(NULL),
winPre(0),
winPost(0),
AlphaNormParam(0),
isMedianPositive(false),
delta(0)
{
}
};
class DFProcess
class DFProcess
{
public:
DFProcess( DFProcConfig Config );
@ -38,7 +60,7 @@ public:
void process( double* src, double* dst );
private:
void initialise( DFProcConfig Config );
void deInitialise();
@ -59,11 +81,12 @@ private:
double* m_filtScratchIn;
double* m_filtScratchOut;
FiltFiltConfig m_FilterConfigParams;
FilterConfig m_FilterConfigParams;
FiltFilt* m_FiltFilt;
bool m_isMedianPositive;
float m_delta; //add delta threshold
};
#endif

View File

@ -19,12 +19,12 @@
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
FiltFilt::FiltFilt( FiltFiltConfig Config )
FiltFilt::FiltFilt( FilterConfig Config )
{
m_filtScratchIn = NULL;
m_filtScratchOut = NULL;
m_ord = 0;
initialise( Config );
}
@ -33,13 +33,13 @@ FiltFilt::~FiltFilt()
deInitialise();
}
void FiltFilt::initialise( FiltFiltConfig Config )
void FiltFilt::initialise( FilterConfig Config )
{
m_ord = Config.ord;
m_filterConfig.ord = Config.ord;
m_filterConfig.ACoeffs = Config.ACoeffs;
m_filterConfig.BCoeffs = Config.BCoeffs;
m_filter = new Filter( m_filterConfig );
}
@ -50,7 +50,7 @@ void FiltFilt::deInitialise()
void FiltFilt::process(double *src, double *dst, unsigned int length)
{
{
unsigned int i;
if (length == 0) return;
@ -62,8 +62,8 @@ void FiltFilt::process(double *src, double *dst, unsigned int length)
m_filtScratchIn = new double[ nExt ];
m_filtScratchOut = new double[ nExt ];
for( i = 0; i< nExt; i++ )
for( i = 0; i< nExt; i++ )
{
m_filtScratchIn[ i ] = 0.0;
m_filtScratchOut[ i ] = 0.0;
@ -89,21 +89,21 @@ void FiltFilt::process(double *src, double *dst, unsigned int length)
{
m_filtScratchIn[ i + nFact ] = src[ i ];
}
////////////////////////////////
// Do 0Ph filtering
m_filter->process( m_filtScratchIn, m_filtScratchOut, nExt);
// reverse the series for FILTFILT
// reverse the series for FILTFILT
for ( i = 0; i < nExt; i++)
{
{
m_filtScratchIn[ i ] = m_filtScratchOut[ nExt - i - 1];
}
// do FILTER again
// do FILTER again
m_filter->process( m_filtScratchIn, m_filtScratchOut, nExt);
// reverse the series back
// reverse the series back
for ( i = 0; i < nExt; i++)
{
m_filtScratchIn[ i ] = m_filtScratchOut[ nExt - i - 1 ];
@ -117,7 +117,7 @@ void FiltFilt::process(double *src, double *dst, unsigned int length)
for( i = 0; i < length; i++ )
{
dst[ index++ ] = m_filtScratchOut[ i + nFact ];
}
}
delete [] m_filtScratchIn;
delete [] m_filtScratchOut;

View File

@ -18,23 +18,22 @@
#include "Filter.h"
struct FiltFiltConfig{
unsigned int ord;
double* ACoeffs;
double* BCoeffs;
};
class FiltFilt
/**
* Zero-phase digital filter, implemented by processing the data
* through a filter specified by the given FilterConfig structure (see
* Filter) and then processing it again in reverse.
*/
class FiltFilt
{
public:
FiltFilt( FiltFiltConfig Config );
FiltFilt( FilterConfig Config );
virtual ~FiltFilt();
void reset();
void process( double* src, double* dst, unsigned int length );
private:
void initialise( FiltFiltConfig Config );
void initialise( FilterConfig Config );
void deInitialise();
unsigned int m_ord;

View File

@ -20,13 +20,22 @@
#define NULL 0
#endif
/**
* Filter specification. For a filter of order ord, the ACoeffs and
* BCoeffs arrays must point to ord+1 values each. ACoeffs provides
* the denominator and BCoeffs the numerator coefficients of the
* filter.
*/
struct FilterConfig{
unsigned int ord;
double* ACoeffs;
double* BCoeffs;
};
class Filter
/**
* Digital filter specified through FilterConfig structure.
*/
class Filter
{
public:
Filter( FilterConfig Config );
@ -36,7 +45,6 @@ public:
void process( double *src, double *dst, unsigned int length );
private:
void initialise( FilterConfig Config );
void deInitialise();

View File

@ -44,14 +44,14 @@ void Framer::configure( unsigned int frameLength, unsigned int hop )
if( m_dataFrame != NULL )
{
delete [] m_dataFrame;
delete [] m_dataFrame;
m_dataFrame = NULL;
}
m_dataFrame = new double[ m_frameLength ];
if( m_strideFrame != NULL )
{
delete [] m_strideFrame;
delete [] m_strideFrame;
m_strideFrame = NULL;
}
m_strideFrame = new double[ m_stepSize ];
@ -64,8 +64,8 @@ void Framer::getFrame(double *dst)
{
for( unsigned int u = 0; u < m_frameLength; u++)
{
dst[ u ] = m_srcBuffer[ m_ulSrcIndex++ ];
}
dst[ u ] = m_srcBuffer[ m_ulSrcIndex++ ];
}
m_ulSrcIndex -= ( m_frameLength - m_stepSize );
}
else
@ -77,7 +77,7 @@ void Framer::getFrame(double *dst)
{
dst[ u ] = m_srcBuffer[ m_ulSrcIndex++ ];
}
for( unsigned int u = 0; u < zero; u++ )
{
dst[ rem + u ] = 0;

View File

@ -21,7 +21,7 @@
#include <stdio.h>
class Framer
class Framer
{
public:
void setSource( double* src, unsigned int length );

View File

@ -44,7 +44,10 @@ DownBeat::DownBeat(float originalSampleRate,
// 16x decimation, which is our expected normal situation)
m_beatframesize = MathUtilities::nextPowerOfTwo
(int((m_rate / decimationFactor) * 1.3));
// std::cerr << "rate = " << m_rate << ", bfs = " << m_beatframesize << std::endl;
if (m_beatframesize < 2) {
m_beatframesize = 2;
}
// std::cerr << "rate = " << m_rate << ", dec = " << decimationFactor << ", bfs = " << m_beatframesize << std::endl;
m_beatframe = new double[m_beatframesize];
m_fftRealOut = new double[m_beatframesize];
m_fftImagOut = new double[m_beatframesize];
@ -122,7 +125,7 @@ DownBeat::pushAudioBlock(const float *audio)
// std::cerr << "pushAudioBlock: rms in " << sqrt(rmsin) << ", out " << sqrt(rmsout) << std::endl;
m_buffill += m_increment / m_factor;
}
const float *
DownBeat::getBufferedAudio(size_t &length) const
{
@ -192,9 +195,9 @@ DownBeat::findDownBeats(const float *audio,
}
// Now FFT beat frame
m_fft->process(false, m_beatframe, m_fftRealOut, m_fftImagOut);
m_fft->forward(m_beatframe, m_fftRealOut, m_fftImagOut);
// Calculate magnitudes
for (size_t j = 0; j < m_beatframesize/2; ++j) {
@ -257,7 +260,7 @@ DownBeat::measureSpecDiff(d_vec_t oldspec, d_vec_t newspec)
{
// JENSEN-SHANNON DIVERGENCE BETWEEN SPECTRAL FRAMES
unsigned int SPECSIZE = 512; // ONLY LOOK AT FIRST 512 SAMPLES OF SPECTRUM.
unsigned int SPECSIZE = 512; // ONLY LOOK AT FIRST 512 SAMPLES OF SPECTRUM.
if (SPECSIZE > oldspec.size()/4) {
SPECSIZE = oldspec.size()/4;
}
@ -266,37 +269,37 @@ DownBeat::measureSpecDiff(d_vec_t oldspec, d_vec_t newspec)
double sumnew = 0.;
double sumold = 0.;
for (unsigned int i = 0;i < SPECSIZE;i++)
{
newspec[i] +=EPS;
oldspec[i] +=EPS;
sumnew+=newspec[i];
sumold+=oldspec[i];
}
}
for (unsigned int i = 0;i < SPECSIZE;i++)
{
newspec[i] /= (sumnew);
oldspec[i] /= (sumold);
// IF ANY SPECTRAL VALUES ARE 0 (SHOULDN'T BE ANY!) SET THEM TO 1
if (newspec[i] == 0)
{
newspec[i] = 1.;
}
if (oldspec[i] == 0)
{
oldspec[i] = 1.;
}
// JENSEN-SHANNON CALCULATION
sd1 = 0.5*oldspec[i] + 0.5*newspec[i];
sd1 = 0.5*oldspec[i] + 0.5*newspec[i];
SD = SD + (-sd1*log(sd1)) + (0.5*(oldspec[i]*log(oldspec[i]))) + (0.5*(newspec[i]*log(newspec[i])));
}
return SD;
}

View File

@ -17,6 +17,7 @@
#define DOWNBEAT_H
#include <vector>
#include <cstddef>
#include "dsp/rateconversion/Decimator.h"
@ -28,7 +29,7 @@ class FFTReal;
* This class takes an input audio signal and a sequence of beat
* locations (calculated e.g. by TempoTrackV2) and estimates which of
* the beat locations are downbeats (first beat of the bar).
*
*
* The input audio signal is expected to have been downsampled to a
* very low sampling rate (e.g. 2700Hz). A utility function for
* downsampling and buffering incoming block-by-block audio is
@ -56,7 +57,7 @@ public:
/**
* Estimate which beats are down-beats.
*
*
* audio contains the input audio stream after downsampling, and
* audioLength contains the number of samples in this downsampled
* stream.
@ -83,18 +84,18 @@ public:
* and the region following it.
*/
void getBeatSD(vector<double> &beatsd) const;
/**
* For your downsampling convenience: call this function
* repeatedly with input audio blocks containing dfIncrement
* samples at the original sample rate, to decimate them to the
* downsampled rate and buffer them within the DownBeat class.
*
*
* Call getBufferedAudio() to retrieve the results after all
* blocks have been processed.
*/
void pushAudioBlock(const float *audio);
/**
* Retrieve the accumulated audio produced by pushAudioBlock calls.
*/

File diff suppressed because it is too large Load Diff

View File

@ -5,11 +5,11 @@
Centre for Digital Music, Queen Mary, University of London.
This file 2005-2006 Christian Landone.
This program 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 2 of the
License, or (at your option) any later version. See the file
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
@ -31,7 +31,7 @@ using std::vector;
struct WinThresh
{
unsigned int pre;
unsigned int post;
unsigned int post;
};
struct TTParams

View File

@ -91,10 +91,17 @@ TempoTrackV2::filter_df(d_vec_t &df)
}
// MEPD 28/11/12
// This function now allows for a user to specify an inputtempo (in BPM)
// and a flag "constraintempo" which replaces the general rayleigh weighting for periodicities
// with a gaussian which is centered around the input tempo
// Note, if inputtempo = 120 and constraintempo = false, then functionality is
// as it was before
void
TempoTrackV2::calculateBeatPeriod(const vector<double> &df,
vector<double> &beat_period,
vector<double> &tempi)
vector<double> &tempi,
double inputtempo, bool constraintempo)
{
// to follow matlab.. split into 512 sample frames with a 128 hop size
// calculate the acf,
@ -103,13 +110,42 @@ TempoTrackV2::calculateBeatPeriod(const vector<double> &df,
// and get best path
unsigned int wv_len = 128;
double rayparam = 43.;
// MEPD 28/11/12
// the default value of inputtempo in the beat tracking plugin is 120
// so if the user specifies a different inputtempo, the rayparam will be updated
// accordingly.
// note: 60*44100/512 is a magic number
// this might (will?) break if a user specifies a different frame rate for the onset detection function
double rayparam = (60*44100/512)/inputtempo;
// these debug statements can be removed.
// std::cerr << "inputtempo" << inputtempo << std::endl;
// std::cerr << "rayparam" << rayparam << std::endl;
// std::cerr << "constraintempo" << constraintempo << std::endl;
// make rayleigh weighting curve
d_vec_t wv(wv_len);
for (unsigned int i=0; i<wv.size(); i++)
// check whether or not to use rayleigh weighting (if constraintempo is false)
// or use gaussian weighting it (constraintempo is true)
if (constraintempo)
{
wv[i] = (static_cast<double> (i) / pow(rayparam,2.)) * exp((-1.*pow(-static_cast<double> (i),2.)) / (2.*pow(rayparam,2.)));
for (unsigned int i=0; i<wv.size(); i++)
{
// MEPD 28/11/12
// do a gaussian weighting instead of rayleigh
wv[i] = exp( (-1.*pow((static_cast<double> (i)-rayparam),2.)) / (2.*pow(rayparam/4.,2.)) );
}
}
else
{
for (unsigned int i=0; i<wv.size(); i++)
{
// MEPD 28/11/12
// standard rayleigh weighting over periodicities
wv[i] = (static_cast<double> (i) / pow(rayparam,2.)) * exp((-1.*pow(-static_cast<double> (i),2.)) / (2.*pow(rayparam,2.)));
}
}
// beat tracking frame size (roughly 6 seconds) and hop (1.5 seconds)
@ -397,10 +433,14 @@ TempoTrackV2::normalise_vec(d_vec_t &df)
}
}
// MEPD 28/11/12
// this function has been updated to allow the "alpha" and "tightness" parameters
// of the dynamic program to be set by the user
// the default value of alpha = 0.9 and tightness = 4
void
TempoTrackV2::calculateBeats(const vector<double> &df,
const vector<double> &beat_period,
vector<double> &beats)
vector<double> &beats, double alpha, double tightness)
{
if (df.empty() || beat_period.empty()) return;
@ -414,8 +454,12 @@ TempoTrackV2::calculateBeats(const vector<double> &df,
backlink[i] = -1;
}
double tightness = 4.;
double alpha = 0.9;
//double tightness = 4.;
//double alpha = 0.9;
// MEPD 28/11/12
// debug statements that can be removed.
// std::cerr << "alpha" << alpha << std::endl;
// std::cerr << "tightness" << tightness << std::endl;
// main loop
for (unsigned int i=0; i<localscore.size(); i++)
@ -462,7 +506,7 @@ TempoTrackV2::calculateBeats(const vector<double> &df,
int startpoint = get_max_ind(tmp_vec) + cumscore.size() - beat_period[beat_period.size()-1] ;
// can happen if no results obtained earlier (e.g. input too short)
if (startpoint >= backlink.size()) startpoint = backlink.size()-1;
if (startpoint >= (int)backlink.size()) startpoint = backlink.size()-1;
// USE BACKLINK TO GET EACH NEW BEAT (TOWARDS THE BEGINNING OF THE FILE)
// BACKTRACKING FROM THE END TO THE BEGINNING.. MAKING SURE NOT TO GO BEFORE SAMPLE 0

View File

@ -18,8 +18,7 @@
#define TEMPOTRACKV2_H
#include <vector>
using std::vector;
using namespace std;
//!!! Question: how far is this actually sample rate dependent? I
// think it does produce plausible results for e.g. 48000 as well as
@ -40,15 +39,35 @@ public:
TempoTrackV2(float sampleRate, size_t dfIncrement);
~TempoTrackV2();
// Returned beat periods are given in df increment units; tempi in bpm
// Returned beat periods are given in df increment units; inputtempo and tempi in bpm
void calculateBeatPeriod(const vector<double> &df,
vector<double> &beatPeriod,
vector<double> &tempi);
vector<double> &tempi) {
calculateBeatPeriod(df, beatPeriod, tempi, 120.0, false);
}
// Returned beat periods are given in df increment units; inputtempo and tempi in bpm
// MEPD 28/11/12 Expose inputtempo and constraintempo parameters
// Note, if inputtempo = 120 and constraintempo = false, then functionality is as it was before
void calculateBeatPeriod(const vector<double> &df,
vector<double> &beatPeriod,
vector<double> &tempi,
double inputtempo, bool constraintempo);
// Returned beat positions are given in df increment units
void calculateBeats(const vector<double> &df,
const vector<double> &beatPeriod,
vector<double> &beats);
vector<double> &beats) {
calculateBeats(df, beatPeriod, beats, 0.9, 4.0);
}
// Returned beat positions are given in df increment units
// MEPD 28/11/12 Expose alpha and tightness parameters
// Note, if alpha = 0.9 and tightness = 4, then functionality is as it was before
void calculateBeats(const vector<double> &df,
const vector<double> &beatPeriod,
vector<double> &beats,
double alpha, double tightness);
private:
typedef vector<int> i_vec_t;

View File

@ -16,7 +16,7 @@
#include "ChangeDetectionFunction.h"
#ifndef PI
#define PI (3.14159265358979323846)
#define PI (3.14159265358979232846)
#endif
@ -34,20 +34,20 @@ ChangeDetectionFunction::~ChangeDetectionFunction()
void ChangeDetectionFunction::setFilterWidth(const int iWidth)
{
m_iFilterWidth = iWidth*2+1;
// it is assumed that the gaussian is 0 outside of +/- FWHM
// => filter width = 2*FWHM = 2*2.3548*sigma
m_dFilterSigma = double(m_iFilterWidth) / double(2*2.3548);
m_vaGaussian.resize(m_iFilterWidth);
double dScale = 1.0 / (m_dFilterSigma*sqrt(2*PI));
for (int x = -(m_iFilterWidth-1)/2; x <= (m_iFilterWidth-1)/2; x++)
{
double w = dScale * std::exp ( -(x*x)/(2*m_dFilterSigma*m_dFilterSigma) );
m_vaGaussian[x + (m_iFilterWidth-1)/2] = w;
}
#ifdef DEBUG_CHANGE_DETECTION_FUNCTION
std::cerr << "Filter sigma: " << m_dFilterSigma << std::endl;
std::cerr << "Filter width: " << m_iFilterWidth << std::endl;
@ -59,37 +59,37 @@ ChangeDistance ChangeDetectionFunction::process(const TCSGram& rTCSGram)
{
ChangeDistance retVal;
retVal.resize(rTCSGram.getSize(), 0.0);
TCSGram smoothedTCSGram;
for (int iPosition = 0; iPosition < rTCSGram.getSize(); iPosition++)
{
int iSkipLower = 0;
int iLowerPos = iPosition - (m_iFilterWidth-1)/2;
int iUpperPos = iPosition + (m_iFilterWidth-1)/2;
if (iLowerPos < 0)
{
iSkipLower = -iLowerPos;
iLowerPos = 0;
}
if (iUpperPos >= rTCSGram.getSize())
{
int iMaxIndex = rTCSGram.getSize() - 1;
iUpperPos = iMaxIndex;
}
TCSVector smoothedVector;
// for every bin of the vector, calculate the smoothed value
for (int iPC = 0; iPC < 6; iPC++)
{
{
size_t j = 0;
double dSmoothedValue = 0.0;
TCSVector rCV;
for (int i = iLowerPos; i <= iUpperPos; i++)
{
rTCSGram.getTCSVector(i, rCV);
@ -98,7 +98,7 @@ ChangeDistance ChangeDetectionFunction::process(const TCSGram& rTCSGram)
smoothedVector[iPC] = dSmoothedValue;
}
smoothedTCSGram.addTCSVector(smoothedVector);
}
@ -109,10 +109,10 @@ ChangeDistance ChangeDetectionFunction::process(const TCSGram& rTCSGram)
if the current estimate is not confident enough, look further into the future/the past
e.g., High frequency content, zero crossing rate, spectral flatness
*/
TCSVector nextTCS;
TCSVector previousTCS;
int iWindow = 1;
// while (previousTCS.magnitude() < 0.1 && (iPosition-iWindow) > 0)
@ -121,9 +121,9 @@ ChangeDistance ChangeDetectionFunction::process(const TCSGram& rTCSGram)
// std::cout << previousTCS.magnitude() << std::endl;
iWindow++;
}
iWindow = 1;
// while (nextTCS.magnitude() < 0.1 && (iPosition+iWindow) < (rTCSGram.getSize()-1) )
{
smoothedTCSGram.getTCSVector(iPosition+iWindow, nextTCS);
@ -136,7 +136,7 @@ ChangeDistance ChangeDetectionFunction::process(const TCSGram& rTCSGram)
{
distance += std::pow(nextTCS[j] - previousTCS[j], 2.0);
}
retVal[iPosition] = std::pow(distance, 0.5);
}

View File

@ -38,7 +38,7 @@ public:
ChangeDistance process(const TCSGram& rTCSGram);
private:
void setFilterWidth(const int iWidth);
private:
valarray<double> m_vaGaussian;
double m_dFilterSigma;

View File

@ -34,7 +34,7 @@ TCSGram::~TCSGram()
void TCSGram::getTCSVector(int iPosition, TCSVector& rTCSVector) const
{
if (iPosition < 0)
if (iPosition < 0)
rTCSVector = TCSVector();
else if (iPosition >= m_VectorList.size())
rTCSVector = TCSVector();
@ -52,10 +52,10 @@ void TCSGram::addTCSVector(const TCSVector& rTCSVector)
{
size_t uSize = m_VectorList.size();
long lMilliSeconds = static_cast<long>(uSize*m_dFrameDurationMS);
std::pair<long, TCSVector> p;
std::pair<long, TCSVector> p;
p.first = lMilliSeconds;
p.second = rTCSVector;
m_VectorList.push_back(p);
}
@ -68,7 +68,7 @@ long TCSGram::getDuration() const
void TCSGram::printDebug()
{
vectorlist_t::iterator vectorIterator = m_VectorList.begin();
while (vectorIterator != m_VectorList.end())
{
vectorIterator->second.printDebug();

View File

@ -26,7 +26,7 @@ typedef std::vector<std::pair<long, TCSVector> > vectorlist_t;
class TCSGram
{
public:
public:
TCSGram();
~TCSGram();
void getTCSVector(int, TCSVector&) const;

View File

@ -19,7 +19,7 @@
#include <iostream>
#ifndef PI
#define PI (3.14159265358979323846)
#define PI (3.14159265358979232846)
#endif
TonalEstimator::TonalEstimator()
@ -27,15 +27,15 @@ TonalEstimator::TonalEstimator()
m_Basis.resize(6);
int i = 0;
// circle of fifths
m_Basis[i].resize(12);
for (int iP = 0; iP < 12; iP++)
{
m_Basis[i][iP] = std::sin( (7.0 / 6.0) * iP * PI);
}
i++;
m_Basis[i].resize(12);
@ -43,17 +43,17 @@ TonalEstimator::TonalEstimator()
{
m_Basis[i][iP] = std::cos( (7.0 / 6.0) * iP * PI);
}
i++;
// circle of major thirds
m_Basis[i].resize(12);
for (int iP = 0; iP < 12; iP++)
{
m_Basis[i][iP] = 0.6 * std::sin( (2.0 / 3.0) * iP * PI);
}
i++;
m_Basis[i].resize(12);
@ -71,7 +71,7 @@ TonalEstimator::TonalEstimator()
{
m_Basis[i][iP] = 1.1 * std::sin( (3.0 / 2.0) * iP * PI);
}
i++;
m_Basis[i].resize(12);
@ -90,7 +90,7 @@ TCSVector TonalEstimator::transform2TCS(const ChromaVector& rVector)
{
TCSVector vaRetVal;
vaRetVal.resize(6, 0.0);
for (int i = 0; i < 6; i++)
{
for (int iP = 0; iP < 12; iP++)
@ -98,6 +98,6 @@ TCSVector TonalEstimator::transform2TCS(const ChromaVector& rVector)
vaRetVal[i] += m_Basis[i][iP] * rVector[iP];
}
}
return vaRetVal;
}

View File

@ -27,24 +27,24 @@ class ChromaVector : public std::valarray<double>
public:
ChromaVector(size_t uSize = 12) : std::valarray<double>()
{ resize(uSize, 0.0f); }
virtual ~ChromaVector() {};
void printDebug()
{
for (int i = 0; i < size(); i++)
{
std::cout << (*this)[i] << ";";
}
std::cout << std::endl;
}
void normalizeL1()
{
// normalize the chroma vector (L1 norm)
double dSum = 0.0;
for (size_t i = 0; i < 12; (dSum += std::abs((*this)[i++]))) ;
for (size_t i = 0; i < 12; dSum > 0.0000001?((*this)[i] /= dSum):(*this)[i]=0.0, i++) ;
@ -54,8 +54,8 @@ public:
{
for (size_t i = 0; i < 12; ++i) (*this)[i] = 0.0;
}
};
class TCSVector : public std::valarray<double>
@ -63,7 +63,7 @@ class TCSVector : public std::valarray<double>
public:
TCSVector() : std::valarray<double>()
{ resize(6, 0.0f); }
virtual ~TCSVector() {};
void printDebug()
@ -72,19 +72,19 @@ public:
{
std::cout << (*this)[i] << ";";
}
std::cout << std::endl;
}
double magnitude() const
{
double dMag = 0.0;
for (size_t i = 0; i < 6; i++)
{
dMag += std::pow((*this)[i], 2.0);
}
return std::sqrt(dMag);
}

View File

@ -4,178 +4,199 @@
QM DSP Library
Centre for Digital Music, Queen Mary, University of London.
This file is based on Don Cross's public domain FFT implementation.
*/
#include "FFT.h"
#include "maths/MathUtilities.h"
#include "kiss_fft.h"
#include "kiss_fftr.h"
#include <cmath>
#include <iostream>
FFT::FFT(unsigned int n) :
m_n(n),
m_private(0)
#include <stdexcept>
class FFT::D
{
if( !MathUtilities::isPowerOfTwo(m_n) )
{
std::cerr << "ERROR: FFT: Non-power-of-two FFT size "
<< m_n << " not supported in this implementation"
<< std::endl;
return;
public:
D(int n) : m_n(n) {
m_planf = kiss_fft_alloc(m_n, 0, NULL, NULL);
m_plani = kiss_fft_alloc(m_n, 1, NULL, NULL);
m_kin = new kiss_fft_cpx[m_n];
m_kout = new kiss_fft_cpx[m_n];
}
~D() {
kiss_fft_free(m_planf);
kiss_fft_free(m_plani);
delete[] m_kin;
delete[] m_kout;
}
void process(bool inverse,
const double *ri,
const double *ii,
double *ro,
double *io) {
for (int i = 0; i < m_n; ++i) {
m_kin[i].r = ri[i];
m_kin[i].i = (ii ? ii[i] : 0.0);
}
if (!inverse) {
kiss_fft(m_planf, m_kin, m_kout);
for (int i = 0; i < m_n; ++i) {
ro[i] = m_kout[i].r;
io[i] = m_kout[i].i;
}
} else {
kiss_fft(m_plani, m_kin, m_kout);
double scale = 1.0 / m_n;
for (int i = 0; i < m_n; ++i) {
ro[i] = m_kout[i].r * scale;
io[i] = m_kout[i].i * scale;
}
}
}
private:
int m_n;
kiss_fft_cfg m_planf;
kiss_fft_cfg m_plani;
kiss_fft_cpx *m_kin;
kiss_fft_cpx *m_kout;
};
FFT::FFT(int n) :
m_d(new D(n))
{
}
FFT::~FFT()
{
delete m_d;
}
FFTReal::FFTReal(unsigned int n) :
m_n(n),
m_private_real(0)
void
FFT::process(bool inverse,
const double *p_lpRealIn, const double *p_lpImagIn,
double *p_lpRealOut, double *p_lpImagOut)
{
m_d->process(inverse,
p_lpRealIn, p_lpImagIn,
p_lpRealOut, p_lpImagOut);
}
class FFTReal::D
{
public:
D(int n) : m_n(n) {
if (n % 2) {
throw std::invalid_argument
("nsamples must be even in FFTReal constructor");
}
m_planf = kiss_fftr_alloc(m_n, 0, NULL, NULL);
m_plani = kiss_fftr_alloc(m_n, 1, NULL, NULL);
m_c = new kiss_fft_cpx[m_n];
}
~D() {
kiss_fftr_free(m_planf);
kiss_fftr_free(m_plani);
delete[] m_c;
}
void forward(const double *ri, double *ro, double *io) {
kiss_fftr(m_planf, ri, m_c);
for (int i = 0; i <= m_n/2; ++i) {
ro[i] = m_c[i].r;
io[i] = m_c[i].i;
}
for (int i = 0; i + 1 < m_n/2; ++i) {
ro[m_n - i - 1] = ro[i + 1];
io[m_n - i - 1] = -io[i + 1];
}
}
void forwardMagnitude(const double *ri, double *mo) {
double *io = new double[m_n];
forward(ri, mo, io);
for (int i = 0; i < m_n; ++i) {
mo[i] = sqrt(mo[i] * mo[i] + io[i] * io[i]);
}
delete[] io;
}
void inverse(const double *ri, const double *ii, double *ro) {
// kiss_fftr.h says
// "input freqdata has nfft/2+1 complex points"
for (int i = 0; i < m_n/2 + 1; ++i) {
m_c[i].r = ri[i];
m_c[i].i = ii[i];
}
kiss_fftri(m_plani, m_c, ro);
double scale = 1.0 / m_n;
for (int i = 0; i < m_n; ++i) {
ro[i] *= scale;
}
}
private:
int m_n;
kiss_fftr_cfg m_planf;
kiss_fftr_cfg m_plani;
kiss_fft_cpx *m_c;
};
FFTReal::FFTReal(int n) :
m_d(new D(n))
{
m_private_real = new FFT(m_n);
}
FFTReal::~FFTReal()
{
delete (FFT *)m_private_real;
delete m_d;
}
void
FFTReal::process(bool inverse,
const double *realIn,
double *realOut, double *imagOut)
FFTReal::forward(const double *ri, double *ro, double *io)
{
((FFT *)m_private_real)->process(inverse, realIn, 0, realOut, imagOut);
}
static unsigned int numberOfBitsNeeded(unsigned int p_nSamples)
{
int i;
if( p_nSamples < 2 )
{
return 0;
}
for ( i=0; ; i++ )
{
if( p_nSamples & (1 << i) ) return i;
}
}
static unsigned int reverseBits(unsigned int p_nIndex, unsigned int p_nBits)
{
unsigned int i, rev;
for(i=rev=0; i < p_nBits; i++)
{
rev = (rev << 1) | (p_nIndex & 1);
p_nIndex >>= 1;
}
return rev;
m_d->forward(ri, ro, io);
}
void
FFT::process(bool p_bInverseTransform,
const double *p_lpRealIn, const double *p_lpImagIn,
double *p_lpRealOut, double *p_lpImagOut)
FFTReal::forwardMagnitude(const double *ri, double *mo)
{
if (!p_lpRealIn || !p_lpRealOut || !p_lpImagOut) return;
// std::cerr << "FFT::process(" << m_n << "," << p_bInverseTransform << ")" << std::endl;
unsigned int NumBits;
unsigned int i, j, k, n;
unsigned int BlockSize, BlockEnd;
double angle_numerator = 2.0 * M_PI;
double tr, ti;
if( !MathUtilities::isPowerOfTwo(m_n) )
{
std::cerr << "ERROR: FFT::process: Non-power-of-two FFT size "
<< m_n << " not supported in this implementation"
<< std::endl;
return;
}
if( p_bInverseTransform ) angle_numerator = -angle_numerator;
NumBits = numberOfBitsNeeded ( m_n );
for( i=0; i < m_n; i++ )
{
j = reverseBits ( i, NumBits );
p_lpRealOut[j] = p_lpRealIn[i];
p_lpImagOut[j] = (p_lpImagIn == 0) ? 0.0 : p_lpImagIn[i];
}
BlockEnd = 1;
for( BlockSize = 2; BlockSize <= m_n; BlockSize <<= 1 )
{
double delta_angle = angle_numerator / (double)BlockSize;
double sm2 = -sin ( -2 * delta_angle );
double sm1 = -sin ( -delta_angle );
double cm2 = cos ( -2 * delta_angle );
double cm1 = cos ( -delta_angle );
double w = 2 * cm1;
double ar[3], ai[3];
for( i=0; i < m_n; i += BlockSize )
{
ar[2] = cm2;
ar[1] = cm1;
ai[2] = sm2;
ai[1] = sm1;
for ( j=i, n=0; n < BlockEnd; j++, n++ )
{
ar[0] = w*ar[1] - ar[2];
ar[2] = ar[1];
ar[1] = ar[0];
ai[0] = w*ai[1] - ai[2];
ai[2] = ai[1];
ai[1] = ai[0];
k = j + BlockEnd;
tr = ar[0]*p_lpRealOut[k] - ai[0]*p_lpImagOut[k];
ti = ar[0]*p_lpImagOut[k] + ai[0]*p_lpRealOut[k];
p_lpRealOut[k] = p_lpRealOut[j] - tr;
p_lpImagOut[k] = p_lpImagOut[j] - ti;
p_lpRealOut[j] += tr;
p_lpImagOut[j] += ti;
}
}
BlockEnd = BlockSize;
}
if( p_bInverseTransform )
{
double denom = (double)m_n;
for ( i=0; i < m_n; i++ )
{
p_lpRealOut[i] /= denom;
p_lpImagOut[i] /= denom;
}
}
m_d->forwardMagnitude(ri, mo);
}
void
FFTReal::inverse(const double *ri, const double *ii, double *ro)
{
m_d->inverse(ri, ii, ro);
}

View File

@ -9,34 +9,97 @@
#ifndef FFT_H
#define FFT_H
class FFT
class FFT
{
public:
FFT(unsigned int nsamples);
virtual ~FFT();
/**
* Construct an FFT object to carry out complex-to-complex
* transforms of size nsamples. nsamples does not have to be a
* power of two.
*/
FFT(int nsamples);
~FFT();
/**
* Carry out a forward or inverse transform (depending on the
* value of inverse) of size nsamples, where nsamples is the value
* provided to the constructor above.
*
* realIn and (where present) imagIn should contain nsamples each,
* and realOut and imagOut should point to enough space to receive
* nsamples each.
*
* imagIn may be NULL if the signal is real, but the other
* pointers must be valid.
*
* The inverse transform is scaled by 1/nsamples.
*/
void process(bool inverse,
const double *realIn, const double *imagIn,
double *realOut, double *imagOut);
private:
unsigned int m_n;
void *m_private;
class D;
D *m_d;
};
class FFTReal
{
public:
FFTReal(unsigned int nsamples);
/**
* Construct an FFT object to carry out real-to-complex transforms
* of size nsamples. nsamples does not have to be a power of two,
* but it does have to be even. (Use the complex-complex FFT above
* if you need an odd FFT size. This constructor will throw
* std::invalid_argument if nsamples is odd.)
*/
FFTReal(int nsamples);
~FFTReal();
void process(bool inverse,
const double *realIn,
/**
* Carry out a forward real-to-complex transform of size nsamples,
* where nsamples is the value provided to the constructor above.
*
* realIn, realOut, and imagOut must point to (enough space for)
* nsamples values. For consistency with the FFT class above, and
* compatibility with existing code, the conjugate half of the
* output is returned even though it is redundant.
*/
void forward(const double *realIn,
double *realOut, double *imagOut);
/**
* Carry out a forward real-to-complex transform of size nsamples,
* where nsamples is the value provided to the constructor
* above. Return only the magnitudes of the complex output values.
*
* realIn and magOut must point to (enough space for) nsamples
* values. For consistency with the FFT class above, and
* compatibility with existing code, the conjugate half of the
* output is returned even though it is redundant.
*/
void forwardMagnitude(const double *realIn, double *magOut);
/**
* Carry out an inverse real transform (i.e. complex-to-real) of
* size nsamples, where nsamples is the value provided to the
* constructor above.
*
* realIn and imagIn should point to at least nsamples/2+1 values;
* if more are provided, only the first nsamples/2+1 values of
* each will be used (the conjugate half will always be deduced
* from the first nsamples/2+1 rather than being read from the
* input data). realOut should point to enough space to receive
* nsamples values.
*
* The inverse transform is scaled by 1/nsamples.
*/
void inverse(const double *realIn, const double *imagIn,
double *realOut);
private:
unsigned int m_n;
void *m_private_real;
};
class D;
D *m_d;
};
#endif

View File

@ -13,10 +13,6 @@
COPYING included with this distribution for more information.
*/
#ifdef COMPILER_MSVC
#pragma warning(disable:4305)
#endif
#include "Wavelet.h"
#include <cassert>
@ -81,11 +77,11 @@ Wavelet::createDecompositionFilters(Type wavelet,
lpd.clear();
hpd.clear();
unsigned int flength = 0;
int flength = 0;
switch (wavelet) {
case Haar:
case Haar:
lpd.push_back(0.70710678118655);
lpd.push_back(0.70710678118655);
hpd.push_back(-0.70710678118655);
@ -103,7 +99,7 @@ Wavelet::createDecompositionFilters(Type wavelet,
hpd.push_back(-0.22414386804186);
hpd.push_back(-0.12940952255092);
flength = 4;
break;
break;
case Daubechies_3:
lpd.push_back(0.03522629188210);
@ -592,7 +588,7 @@ Wavelet::createDecompositionFilters(Type wavelet,
hpd.push_back(-0.00000000000000);
flength = 80;
break;
case Symlet_2:
lpd.push_back(-0.12940952255092);
lpd.push_back(0.22414386804186);
@ -692,7 +688,7 @@ Wavelet::createDecompositionFilters(Type wavelet,
hpd.push_back(0.01540410932703);
flength = 12;
break;
case Symlet_7:
lpd.push_back(0.00268181456826);
lpd.push_back(-0.00104738488868);

View File

@ -0,0 +1,11 @@
Copyright (c) 2003-2010 Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,134 @@
KISS FFT - A mixed-radix Fast Fourier Transform based up on the principle,
"Keep It Simple, Stupid."
There are many great fft libraries already around. Kiss FFT is not trying
to be better than any of them. It only attempts to be a reasonably efficient,
moderately useful FFT that can use fixed or floating data types and can be
incorporated into someone's C program in a few minutes with trivial licensing.
USAGE:
The basic usage for 1-d complex FFT is:
#include "kiss_fft.h"
kiss_fft_cfg cfg = kiss_fft_alloc( nfft ,is_inverse_fft ,0,0 );
while ...
... // put kth sample in cx_in[k].r and cx_in[k].i
kiss_fft( cfg , cx_in , cx_out );
... // transformed. DC is in cx_out[0].r and cx_out[0].i
free(cfg);
Note: frequency-domain data is stored from dc up to 2pi.
so cx_out[0] is the dc bin of the FFT
and cx_out[nfft/2] is the Nyquist bin (if exists)
Declarations are in "kiss_fft.h", along with a brief description of the
functions you'll need to use.
Code definitions for 1d complex FFTs are in kiss_fft.c.
You can do other cool stuff with the extras you'll find in tools/
* multi-dimensional FFTs
* real-optimized FFTs (returns the positive half-spectrum: (nfft/2+1) complex frequency bins)
* fast convolution FIR filtering (not available for fixed point)
* spectrum image creation
The core fft and most tools/ code can be compiled to use float, double,
Q15 short or Q31 samples. The default is float.
BACKGROUND:
I started coding this because I couldn't find a fixed point FFT that didn't
use assembly code. I started with floating point numbers so I could get the
theory straight before working on fixed point issues. In the end, I had a
little bit of code that could be recompiled easily to do ffts with short, float
or double (other types should be easy too).
Once I got my FFT working, I was curious about the speed compared to
a well respected and highly optimized fft library. I don't want to criticize
this great library, so let's call it FFT_BRANDX.
During this process, I learned:
1. FFT_BRANDX has more than 100K lines of code. The core of kiss_fft is about 500 lines (cpx 1-d).
2. It took me an embarrassingly long time to get FFT_BRANDX working.
3. A simple program using FFT_BRANDX is 522KB. A similar program using kiss_fft is 18KB (without optimizing for size).
4. FFT_BRANDX is roughly twice as fast as KISS FFT in default mode.
It is wonderful that free, highly optimized libraries like FFT_BRANDX exist.
But such libraries carry a huge burden of complexity necessary to extract every
last bit of performance.
Sometimes simpler is better, even if it's not better.
FREQUENTLY ASKED QUESTIONS:
Q: Can I use kissfft in a project with a ___ license?
A: Yes. See LICENSE below.
Q: Why don't I get the output I expect?
A: The two most common causes of this are
1) scaling : is there a constant multiplier between what you got and what you want?
2) mixed build environment -- all code must be compiled with same preprocessor
definitions for FIXED_POINT and kiss_fft_scalar
Q: Will you write/debug my code for me?
A: Probably not unless you pay me. I am happy to answer pointed and topical questions, but
I may refer you to a book, a forum, or some other resource.
PERFORMANCE:
(on Athlon XP 2100+, with gcc 2.96, float data type)
Kiss performed 10000 1024-pt cpx ffts in .63 s of cpu time.
For comparison, it took md5sum twice as long to process the same amount of data.
Transforming 5 minutes of CD quality audio takes less than a second (nfft=1024).
DO NOT:
... use Kiss if you need the Fastest Fourier Transform in the World
... ask me to add features that will bloat the code
UNDER THE HOOD:
Kiss FFT uses a time decimation, mixed-radix, out-of-place FFT. If you give it an input buffer
and output buffer that are the same, a temporary buffer will be created to hold the data.
No static data is used. The core routines of kiss_fft are thread-safe (but not all of the tools directory).
No scaling is done for the floating point version (for speed).
Scaling is done both ways for the fixed-point version (for overflow prevention).
Optimized butterflies are used for factors 2,3,4, and 5.
The real (i.e. not complex) optimization code only works for even length ffts. It does two half-length
FFTs in parallel (packed into real&imag), and then combines them via twiddling. The result is
nfft/2+1 complex frequency bins from DC to Nyquist. If you don't know what this means, search the web.
The fast convolution filtering uses the overlap-scrap method, slightly
modified to put the scrap at the tail.
LICENSE:
Revised BSD License, see COPYING for verbiage.
Basically, "free to use&change, give credit where due, no guarantees"
Note this license is compatible with GPL at one end of the spectrum and closed, commercial software at
the other end. See http://www.fsf.org/licensing/licenses
A commercial license is available which removes the requirement for attribution. Contact me for details.
TODO:
*) Add real optimization for odd length FFTs
*) Document/revisit the input/output fft scaling
*) Make doc describing the overlap (tail) scrap fast convolution filtering in kiss_fastfir.c
*) Test all the ./tools/ code with fixed point (kiss_fastfir.c doesn't work, maybe others)
AUTHOR:
Mark Borgerding
Mark@Borgerding.net

View File

@ -0,0 +1,164 @@
/*
Copyright (c) 2003-2010, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* kiss_fft.h
defines kiss_fft_scalar as either short or a float type
and defines
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
#include "kiss_fft.h"
#include <limits.h>
#define MAXFACTORS 32
/* e.g. an fft of length 128 has 4 factors
as far as kissfft is concerned
4*4*4*2
*/
struct kiss_fft_state{
int nfft;
int inverse;
int factors[2*MAXFACTORS];
kiss_fft_cpx twiddles[1];
};
/*
Explanation of macros dealing with complex math:
C_MUL(m,a,b) : m = a*b
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
C_SUB( res, a,b) : res = a - b
C_SUBFROM( res , a) : res -= a
C_ADDTO( res , a) : res += a
* */
#ifdef FIXED_POINT
#if (FIXED_POINT==32)
# define FRACBITS 31
# define SAMPPROD int64_t
#define SAMP_MAX 2147483647
#else
# define FRACBITS 15
# define SAMPPROD int32_t
#define SAMP_MAX 32767
#endif
#define SAMP_MIN -SAMP_MAX
#if defined(CHECK_OVERFLOW)
# define CHECK_OVERFLOW_OP(a,op,b) \
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
#endif
# define smul(a,b) ( (SAMPPROD)(a)*(b) )
# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
# define S_MUL(a,b) sround( smul(a,b) )
# define C_MUL(m,a,b) \
do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
(m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
# define DIVSCALAR(x,k) \
(x) = sround( smul( x, SAMP_MAX/k ) )
# define C_FIXDIV(c,div) \
do { DIVSCALAR( (c).r , div); \
DIVSCALAR( (c).i , div); }while (0)
# define C_MULBYSCALAR( c, s ) \
do{ (c).r = sround( smul( (c).r , s ) ) ;\
(c).i = sround( smul( (c).i , s ) ) ; }while(0)
#else /* not FIXED_POINT*/
# define S_MUL(a,b) ( (a)*(b) )
#define C_MUL(m,a,b) \
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
# define C_FIXDIV(c,div) /* NOOP */
# define C_MULBYSCALAR( c, s ) \
do{ (c).r *= (s);\
(c).i *= (s); }while(0)
#endif
#ifndef CHECK_OVERFLOW_OP
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
#endif
#define C_ADD( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
}while(0)
#define C_SUB( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
}while(0)
#define C_ADDTO( res , a)\
do { \
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
(res).r += (a).r; (res).i += (a).i;\
}while(0)
#define C_SUBFROM( res , a)\
do {\
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
(res).r -= (a).r; (res).i -= (a).i; \
}while(0)
#ifdef FIXED_POINT
# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase))
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
# define HALF_OF(x) ((x)>>1)
#elif defined(USE_SIMD)
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
#else
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
# define HALF_OF(x) ((x)*.5)
#endif
#define kf_cexp(x,phase) \
do{ \
(x)->r = KISS_FFT_COS(phase);\
(x)->i = KISS_FFT_SIN(phase);\
}while(0)
/* a debugging function */
#define pcpx(c)\
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
#ifdef KISS_FFT_USE_ALLOCA
// define this to allow use of alloca instead of malloc for temporary buffers
// Temporary buffers are used in two case:
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform.
#include <alloca.h>
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
#define KISS_FFT_TMP_FREE(ptr)
#else
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
#endif

View File

@ -0,0 +1,408 @@
/*
Copyright (c) 2003-2010, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "_kiss_fft_guts.h"
/* The guts header contains all the multiplication and addition macros that are defined for
fixed or floating point complex numbers. It also delares the kf_ internal functions.
*/
static void kf_bfly2(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m
)
{
kiss_fft_cpx * Fout2;
kiss_fft_cpx * tw1 = st->twiddles;
kiss_fft_cpx t;
Fout2 = Fout + m;
do{
C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2);
C_MUL (t, *Fout2 , *tw1);
tw1 += fstride;
C_SUB( *Fout2 , *Fout , t );
C_ADDTO( *Fout , t );
++Fout2;
++Fout;
}while (--m);
}
static void kf_bfly4(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
const size_t m
)
{
kiss_fft_cpx *tw1,*tw2,*tw3;
kiss_fft_cpx scratch[6];
size_t k=m;
const size_t m2=2*m;
const size_t m3=3*m;
tw3 = tw2 = tw1 = st->twiddles;
do {
C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4);
C_MUL(scratch[0],Fout[m] , *tw1 );
C_MUL(scratch[1],Fout[m2] , *tw2 );
C_MUL(scratch[2],Fout[m3] , *tw3 );
C_SUB( scratch[5] , *Fout, scratch[1] );
C_ADDTO(*Fout, scratch[1]);
C_ADD( scratch[3] , scratch[0] , scratch[2] );
C_SUB( scratch[4] , scratch[0] , scratch[2] );
C_SUB( Fout[m2], *Fout, scratch[3] );
tw1 += fstride;
tw2 += fstride*2;
tw3 += fstride*3;
C_ADDTO( *Fout , scratch[3] );
if(st->inverse) {
Fout[m].r = scratch[5].r - scratch[4].i;
Fout[m].i = scratch[5].i + scratch[4].r;
Fout[m3].r = scratch[5].r + scratch[4].i;
Fout[m3].i = scratch[5].i - scratch[4].r;
}else{
Fout[m].r = scratch[5].r + scratch[4].i;
Fout[m].i = scratch[5].i - scratch[4].r;
Fout[m3].r = scratch[5].r - scratch[4].i;
Fout[m3].i = scratch[5].i + scratch[4].r;
}
++Fout;
}while(--k);
}
static void kf_bfly3(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
size_t m
)
{
size_t k=m;
const size_t m2 = 2*m;
kiss_fft_cpx *tw1,*tw2;
kiss_fft_cpx scratch[5];
kiss_fft_cpx epi3;
epi3 = st->twiddles[fstride*m];
tw1=tw2=st->twiddles;
do{
C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
C_MUL(scratch[1],Fout[m] , *tw1);
C_MUL(scratch[2],Fout[m2] , *tw2);
C_ADD(scratch[3],scratch[1],scratch[2]);
C_SUB(scratch[0],scratch[1],scratch[2]);
tw1 += fstride;
tw2 += fstride*2;
Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
C_MULBYSCALAR( scratch[0] , epi3.i );
C_ADDTO(*Fout,scratch[3]);
Fout[m2].r = Fout[m].r + scratch[0].i;
Fout[m2].i = Fout[m].i - scratch[0].r;
Fout[m].r -= scratch[0].i;
Fout[m].i += scratch[0].r;
++Fout;
}while(--k);
}
static void kf_bfly5(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m
)
{
kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
int u;
kiss_fft_cpx scratch[13];
kiss_fft_cpx * twiddles = st->twiddles;
kiss_fft_cpx *tw;
kiss_fft_cpx ya,yb;
ya = twiddles[fstride*m];
yb = twiddles[fstride*2*m];
Fout0=Fout;
Fout1=Fout0+m;
Fout2=Fout0+2*m;
Fout3=Fout0+3*m;
Fout4=Fout0+4*m;
tw=st->twiddles;
for ( u=0; u<m; ++u ) {
C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
scratch[0] = *Fout0;
C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
C_ADD( scratch[7],scratch[1],scratch[4]);
C_SUB( scratch[10],scratch[1],scratch[4]);
C_ADD( scratch[8],scratch[2],scratch[3]);
C_SUB( scratch[9],scratch[2],scratch[3]);
Fout0->r += scratch[7].r + scratch[8].r;
Fout0->i += scratch[7].i + scratch[8].i;
scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
C_SUB(*Fout1,scratch[5],scratch[6]);
C_ADD(*Fout4,scratch[5],scratch[6]);
scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
C_ADD(*Fout2,scratch[11],scratch[12]);
C_SUB(*Fout3,scratch[11],scratch[12]);
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
}
}
/* perform the butterfly for one stage of a mixed radix FFT */
static void kf_bfly_generic(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m,
int p
)
{
int u,k,q1,q;
kiss_fft_cpx * twiddles = st->twiddles;
kiss_fft_cpx t;
int Norig = st->nfft;
kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p);
for ( u=0; u<m; ++u ) {
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
scratch[q1] = Fout[ k ];
C_FIXDIV(scratch[q1],p);
k += m;
}
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
int twidx=0;
Fout[ k ] = scratch[0];
for (q=1;q<p;++q ) {
twidx += fstride * k;
if (twidx>=Norig) twidx-=Norig;
C_MUL(t,scratch[q] , twiddles[twidx] );
C_ADDTO( Fout[ k ] ,t);
}
k += m;
}
}
KISS_FFT_TMP_FREE(scratch);
}
static
void kf_work(
kiss_fft_cpx * Fout,
const kiss_fft_cpx * f,
const size_t fstride,
int in_stride,
int * factors,
const kiss_fft_cfg st
)
{
kiss_fft_cpx * Fout_beg=Fout;
const int p=*factors++; /* the radix */
const int m=*factors++; /* stage's fft length/p */
const kiss_fft_cpx * Fout_end = Fout + p*m;
#ifdef _OPENMP
// use openmp extensions at the
// top-level (not recursive)
if (fstride==1 && p<=5)
{
int k;
// execute the p different work units in different threads
# pragma omp parallel for
for (k=0;k<p;++k)
kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st);
// all threads have joined by this point
switch (p) {
case 2: kf_bfly2(Fout,fstride,st,m); break;
case 3: kf_bfly3(Fout,fstride,st,m); break;
case 4: kf_bfly4(Fout,fstride,st,m); break;
case 5: kf_bfly5(Fout,fstride,st,m); break;
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
}
return;
}
#endif
if (m==1) {
do{
*Fout = *f;
f += fstride*in_stride;
}while(++Fout != Fout_end );
}else{
do{
// recursive call:
// DFT of size m*p performed by doing
// p instances of smaller DFTs of size m,
// each one takes a decimated version of the input
kf_work( Fout , f, fstride*p, in_stride, factors,st);
f += fstride*in_stride;
}while( (Fout += m) != Fout_end );
}
Fout=Fout_beg;
// recombine the p smaller DFTs
switch (p) {
case 2: kf_bfly2(Fout,fstride,st,m); break;
case 3: kf_bfly3(Fout,fstride,st,m); break;
case 4: kf_bfly4(Fout,fstride,st,m); break;
case 5: kf_bfly5(Fout,fstride,st,m); break;
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
}
}
/* facbuf is populated by p1,m1,p2,m2, ...
where
p[i] * m[i] = m[i-1]
m0 = n */
static
void kf_factor(int n,int * facbuf)
{
int p=4;
double floor_sqrt;
floor_sqrt = floor( sqrt((double)n) );
/*factor out powers of 4, powers of 2, then any remaining primes */
do {
while (n % p) {
switch (p) {
case 4: p = 2; break;
case 2: p = 3; break;
default: p += 2; break;
}
if (p > floor_sqrt)
p = n; /* no more factors, skip to end */
}
n /= p;
*facbuf++ = p;
*facbuf++ = n;
} while (n > 1);
}
/*
*
* User-callable function to allocate all necessary storage space for the fft.
*
* The return value is a contiguous block of memory, allocated with malloc. As such,
* It can be freed with free(), rather than a kiss_fft-specific function.
* */
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
{
kiss_fft_cfg st=NULL;
size_t memneeded = sizeof(struct kiss_fft_state)
+ sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/
if ( lenmem==NULL ) {
st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded );
}else{
if (mem != NULL && *lenmem >= memneeded)
st = (kiss_fft_cfg)mem;
*lenmem = memneeded;
}
if (st) {
int i;
st->nfft=nfft;
st->inverse = inverse_fft;
for (i=0;i<nfft;++i) {
const double pi=3.141592653589793238462643383279502884197169399375105820974944;
double phase = -2*pi*i / nfft;
if (st->inverse)
phase *= -1;
kf_cexp(st->twiddles+i, phase );
}
kf_factor(nfft,st->factors);
}
return st;
}
void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride)
{
if (fin == fout) {
//NOTE: this is not really an in-place FFT algorithm.
//It just performs an out-of-place FFT into a temp buffer
kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft);
kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft);
KISS_FFT_TMP_FREE(tmpbuf);
}else{
kf_work( fout, fin, 1,in_stride, st->factors,st );
}
}
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
{
kiss_fft_stride(cfg,fin,fout,1);
}
void kiss_fft_cleanup(void)
{
// nothing needed any more
}
int kiss_fft_next_fast_size(int n)
{
while(1) {
int m=n;
while ( (m%2) == 0 ) m/=2;
while ( (m%3) == 0 ) m/=3;
while ( (m%5) == 0 ) m/=5;
if (m<=1)
break; /* n is completely factorable by twos, threes, and fives */
n++;
}
return n;
}

View File

@ -0,0 +1,124 @@
#ifndef KISS_FFT_H
#define KISS_FFT_H
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
ATTENTION!
If you would like a :
-- a utility that will handle the caching of fft objects
-- real-only (no imaginary time component ) FFT
-- a multi-dimensional FFT
-- a command-line utility to perform ffts
-- a command-line utility to perform fast-convolution filtering
Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
in the tools/ directory.
*/
#ifdef USE_SIMD
# include <xmmintrin.h>
# define kiss_fft_scalar __m128
#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
#define KISS_FFT_FREE _mm_free
#else
#define KISS_FFT_MALLOC malloc
#define KISS_FFT_FREE free
#endif
#ifdef FIXED_POINT
#include <sys/types.h>
# if (FIXED_POINT == 32)
# define kiss_fft_scalar int32_t
# else
# define kiss_fft_scalar int16_t
# endif
#else
# ifndef kiss_fft_scalar
/* default is float */
# define kiss_fft_scalar float
# endif
#endif
typedef struct {
kiss_fft_scalar r;
kiss_fft_scalar i;
}kiss_fft_cpx;
typedef struct kiss_fft_state* kiss_fft_cfg;
/*
* kiss_fft_alloc
*
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
*
* typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
*
* The return value from fft_alloc is a cfg buffer used internally
* by the fft routine or NULL.
*
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
* The returned value should be free()d when done to avoid memory leaks.
*
* The state can be placed in a user supplied buffer 'mem':
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
* then the function places the cfg in mem and the size used in *lenmem
* and returns mem.
*
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
* then the function returns NULL and places the minimum cfg
* buffer size in *lenmem.
* */
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
/*
* kiss_fft(cfg,in_out_buf)
*
* Perform an FFT on a complex input buffer.
* for a forward FFT,
* fin should be f[0] , f[1] , ... ,f[nfft-1]
* fout will be F[0] , F[1] , ... ,F[nfft-1]
* Note that each element is complex and can be accessed like
f[k].r and f[k].i
* */
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
/*
A more generic version of the above function. It reads its input from every Nth sample.
* */
void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
/* If kiss_fft_alloc allocated a buffer, it is one contiguous
buffer and can be simply free()d when no longer needed*/
#define kiss_fft_free free
/*
Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
your compiler output to call this before you exit.
*/
void kiss_fft_cleanup(void);
/*
* Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
*/
int kiss_fft_next_fast_size(int n);
/* for real ffts, we need an even size */
#define kiss_fftr_next_fast_size_real(n) \
(kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,300 @@
#ifndef KISSFFT_CLASS_HH
#define KISSFFT_CLASS_HH
#include <complex>
#include <vector>
namespace kissfft_utils {
template <typename T_scalar>
struct traits
{
typedef T_scalar scalar_type;
typedef std::complex<scalar_type> cpx_type;
void fill_twiddles( std::complex<T_scalar> * dst ,int nfft,bool inverse)
{
T_scalar phinc = (inverse?2:-2)* acos( (T_scalar) -1) / nfft;
for (int i=0;i<nfft;++i)
dst[i] = exp( std::complex<T_scalar>(0,i*phinc) );
}
void prepare(
std::vector< std::complex<T_scalar> > & dst,
int nfft,bool inverse,
std::vector<int> & stageRadix,
std::vector<int> & stageRemainder )
{
_twiddles.resize(nfft);
fill_twiddles( &_twiddles[0],nfft,inverse);
dst = _twiddles;
//factorize
//start factoring out 4's, then 2's, then 3,5,7,9,...
int n= nfft;
int p=4;
do {
while (n % p) {
switch (p) {
case 4: p = 2; break;
case 2: p = 3; break;
default: p += 2; break;
}
if (p*p>n)
p=n;// no more factors
}
n /= p;
stageRadix.push_back(p);
stageRemainder.push_back(n);
}while(n>1);
}
std::vector<cpx_type> _twiddles;
const cpx_type twiddle(int i) { return _twiddles[i]; }
};
}
template <typename T_Scalar,
typename T_traits=kissfft_utils::traits<T_Scalar>
>
class kissfft
{
public:
typedef T_traits traits_type;
typedef typename traits_type::scalar_type scalar_type;
typedef typename traits_type::cpx_type cpx_type;
kissfft(int nfft,bool inverse,const traits_type & traits=traits_type() )
:_nfft(nfft),_inverse(inverse),_traits(traits)
{
_traits.prepare(_twiddles, _nfft,_inverse ,_stageRadix, _stageRemainder);
}
void transform(const cpx_type * src , cpx_type * dst)
{
kf_work(0, dst, src, 1,1);
}
private:
void kf_work( int stage,cpx_type * Fout, const cpx_type * f, size_t fstride,size_t in_stride)
{
int p = _stageRadix[stage];
int m = _stageRemainder[stage];
cpx_type * Fout_beg = Fout;
cpx_type * Fout_end = Fout + p*m;
if (m==1) {
do{
*Fout = *f;
f += fstride*in_stride;
}while(++Fout != Fout_end );
}else{
do{
// recursive call:
// DFT of size m*p performed by doing
// p instances of smaller DFTs of size m,
// each one takes a decimated version of the input
kf_work(stage+1, Fout , f, fstride*p,in_stride);
f += fstride*in_stride;
}while( (Fout += m) != Fout_end );
}
Fout=Fout_beg;
// recombine the p smaller DFTs
switch (p) {
case 2: kf_bfly2(Fout,fstride,m); break;
case 3: kf_bfly3(Fout,fstride,m); break;
case 4: kf_bfly4(Fout,fstride,m); break;
case 5: kf_bfly5(Fout,fstride,m); break;
default: kf_bfly_generic(Fout,fstride,m,p); break;
}
}
// these were #define macros in the original kiss_fft
void C_ADD( cpx_type & c,const cpx_type & a,const cpx_type & b) { c=a+b;}
void C_MUL( cpx_type & c,const cpx_type & a,const cpx_type & b) { c=a*b;}
void C_SUB( cpx_type & c,const cpx_type & a,const cpx_type & b) { c=a-b;}
void C_ADDTO( cpx_type & c,const cpx_type & a) { c+=a;}
void C_FIXDIV( cpx_type & ,int ) {} // NO-OP for float types
scalar_type S_MUL( const scalar_type & a,const scalar_type & b) { return a*b;}
scalar_type HALF_OF( const scalar_type & a) { return a*.5;}
void C_MULBYSCALAR(cpx_type & c,const scalar_type & a) {c*=a;}
void kf_bfly2( cpx_type * Fout, const size_t fstride, int m)
{
for (int k=0;k<m;++k) {
cpx_type t = Fout[m+k] * _traits.twiddle(k*fstride);
Fout[m+k] = Fout[k] - t;
Fout[k] += t;
}
}
void kf_bfly4( cpx_type * Fout, const size_t fstride, const size_t m)
{
cpx_type scratch[7];
int negative_if_inverse = _inverse * -2 +1;
for (size_t k=0;k<m;++k) {
scratch[0] = Fout[k+m] * _traits.twiddle(k*fstride);
scratch[1] = Fout[k+2*m] * _traits.twiddle(k*fstride*2);
scratch[2] = Fout[k+3*m] * _traits.twiddle(k*fstride*3);
scratch[5] = Fout[k] - scratch[1];
Fout[k] += scratch[1];
scratch[3] = scratch[0] + scratch[2];
scratch[4] = scratch[0] - scratch[2];
scratch[4] = cpx_type( scratch[4].imag()*negative_if_inverse , -scratch[4].real()* negative_if_inverse );
Fout[k+2*m] = Fout[k] - scratch[3];
Fout[k] += scratch[3];
Fout[k+m] = scratch[5] + scratch[4];
Fout[k+3*m] = scratch[5] - scratch[4];
}
}
void kf_bfly3( cpx_type * Fout, const size_t fstride, const size_t m)
{
size_t k=m;
const size_t m2 = 2*m;
cpx_type *tw1,*tw2;
cpx_type scratch[5];
cpx_type epi3;
epi3 = _twiddles[fstride*m];
tw1=tw2=&_twiddles[0];
do{
C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
C_MUL(scratch[1],Fout[m] , *tw1);
C_MUL(scratch[2],Fout[m2] , *tw2);
C_ADD(scratch[3],scratch[1],scratch[2]);
C_SUB(scratch[0],scratch[1],scratch[2]);
tw1 += fstride;
tw2 += fstride*2;
Fout[m] = cpx_type( Fout->real() - HALF_OF(scratch[3].real() ) , Fout->imag() - HALF_OF(scratch[3].imag() ) );
C_MULBYSCALAR( scratch[0] , epi3.imag() );
C_ADDTO(*Fout,scratch[3]);
Fout[m2] = cpx_type( Fout[m].real() + scratch[0].imag() , Fout[m].imag() - scratch[0].real() );
C_ADDTO( Fout[m] , cpx_type( -scratch[0].imag(),scratch[0].real() ) );
++Fout;
}while(--k);
}
void kf_bfly5( cpx_type * Fout, const size_t fstride, const size_t m)
{
cpx_type *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
size_t u;
cpx_type scratch[13];
cpx_type * twiddles = &_twiddles[0];
cpx_type *tw;
cpx_type ya,yb;
ya = twiddles[fstride*m];
yb = twiddles[fstride*2*m];
Fout0=Fout;
Fout1=Fout0+m;
Fout2=Fout0+2*m;
Fout3=Fout0+3*m;
Fout4=Fout0+4*m;
tw=twiddles;
for ( u=0; u<m; ++u ) {
C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
scratch[0] = *Fout0;
C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
C_ADD( scratch[7],scratch[1],scratch[4]);
C_SUB( scratch[10],scratch[1],scratch[4]);
C_ADD( scratch[8],scratch[2],scratch[3]);
C_SUB( scratch[9],scratch[2],scratch[3]);
C_ADDTO( *Fout0, scratch[7]);
C_ADDTO( *Fout0, scratch[8]);
scratch[5] = scratch[0] + cpx_type(
S_MUL(scratch[7].real(),ya.real() ) + S_MUL(scratch[8].real() ,yb.real() ),
S_MUL(scratch[7].imag(),ya.real()) + S_MUL(scratch[8].imag(),yb.real())
);
scratch[6] = cpx_type(
S_MUL(scratch[10].imag(),ya.imag()) + S_MUL(scratch[9].imag(),yb.imag()),
-S_MUL(scratch[10].real(),ya.imag()) - S_MUL(scratch[9].real(),yb.imag())
);
C_SUB(*Fout1,scratch[5],scratch[6]);
C_ADD(*Fout4,scratch[5],scratch[6]);
scratch[11] = scratch[0] +
cpx_type(
S_MUL(scratch[7].real(),yb.real()) + S_MUL(scratch[8].real(),ya.real()),
S_MUL(scratch[7].imag(),yb.real()) + S_MUL(scratch[8].imag(),ya.real())
);
scratch[12] = cpx_type(
-S_MUL(scratch[10].imag(),yb.imag()) + S_MUL(scratch[9].imag(),ya.imag()),
S_MUL(scratch[10].real(),yb.imag()) - S_MUL(scratch[9].real(),ya.imag())
);
C_ADD(*Fout2,scratch[11],scratch[12]);
C_SUB(*Fout3,scratch[11],scratch[12]);
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
}
}
/* perform the butterfly for one stage of a mixed radix FFT */
void kf_bfly_generic(
cpx_type * Fout,
const size_t fstride,
int m,
int p
)
{
int u,k,q1,q;
cpx_type * twiddles = &_twiddles[0];
cpx_type t;
int Norig = _nfft;
cpx_type scratchbuf[p];
for ( u=0; u<m; ++u ) {
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
scratchbuf[q1] = Fout[ k ];
C_FIXDIV(scratchbuf[q1],p);
k += m;
}
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
int twidx=0;
Fout[ k ] = scratchbuf[0];
for (q=1;q<p;++q ) {
twidx += fstride * k;
if (twidx>=Norig) twidx-=Norig;
C_MUL(t,scratchbuf[q] , twiddles[twidx] );
C_ADDTO( Fout[ k ] ,t);
}
k += m;
}
}
}
int _nfft;
bool _inverse;
std::vector<cpx_type> _twiddles;
std::vector<int> _stageRadix;
std::vector<int> _stageRemainder;
traits_type _traits;
};
#endif

View File

@ -0,0 +1,208 @@
/*
Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "kiss_fft.h"
#include "kiss_fftndr.h"
static
void fft_file(FILE * fin,FILE * fout,int nfft,int isinverse)
{
kiss_fft_cfg st;
kiss_fft_cpx * buf;
kiss_fft_cpx * bufout;
buf = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * nfft );
bufout = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * nfft );
st = kiss_fft_alloc( nfft ,isinverse ,0,0);
while ( fread( buf , sizeof(kiss_fft_cpx) * nfft ,1, fin ) > 0 ) {
kiss_fft( st , buf ,bufout);
fwrite( bufout , sizeof(kiss_fft_cpx) , nfft , fout );
}
free(st);
free(buf);
free(bufout);
}
static
void fft_filend(FILE * fin,FILE * fout,int *dims,int ndims,int isinverse)
{
kiss_fftnd_cfg st;
kiss_fft_cpx *buf;
int dimprod=1,i;
for (i=0;i<ndims;++i)
dimprod *= dims[i];
buf = (kiss_fft_cpx *) malloc (sizeof (kiss_fft_cpx) * dimprod);
st = kiss_fftnd_alloc (dims, ndims, isinverse, 0, 0);
while (fread (buf, sizeof (kiss_fft_cpx) * dimprod, 1, fin) > 0) {
kiss_fftnd (st, buf, buf);
fwrite (buf, sizeof (kiss_fft_cpx), dimprod, fout);
}
free (st);
free (buf);
}
static
void fft_filend_real(FILE * fin,FILE * fout,int *dims,int ndims,int isinverse)
{
int dimprod=1,i;
kiss_fftndr_cfg st;
void *ibuf;
void *obuf;
int insize,outsize; // size in bytes
for (i=0;i<ndims;++i)
dimprod *= dims[i];
insize = outsize = dimprod;
int rdim = dims[ndims-1];
if (isinverse)
insize = insize*2*(rdim/2+1)/rdim;
else
outsize = outsize*2*(rdim/2+1)/rdim;
ibuf = malloc(insize*sizeof(kiss_fft_scalar));
obuf = malloc(outsize*sizeof(kiss_fft_scalar));
st = kiss_fftndr_alloc(dims, ndims, isinverse, 0, 0);
while ( fread (ibuf, sizeof(kiss_fft_scalar), insize, fin) > 0) {
if (isinverse) {
kiss_fftndri(st,
(kiss_fft_cpx*)ibuf,
(kiss_fft_scalar*)obuf);
}else{
kiss_fftndr(st,
(kiss_fft_scalar*)ibuf,
(kiss_fft_cpx*)obuf);
}
fwrite (obuf, sizeof(kiss_fft_scalar), outsize,fout);
}
free(st);
free(ibuf);
free(obuf);
}
static
void fft_file_real(FILE * fin,FILE * fout,int nfft,int isinverse)
{
kiss_fftr_cfg st;
kiss_fft_scalar * rbuf;
kiss_fft_cpx * cbuf;
rbuf = (kiss_fft_scalar*)malloc(sizeof(kiss_fft_scalar) * nfft );
cbuf = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * (nfft/2+1) );
st = kiss_fftr_alloc( nfft ,isinverse ,0,0);
if (isinverse==0) {
while ( fread( rbuf , sizeof(kiss_fft_scalar) * nfft ,1, fin ) > 0 ) {
kiss_fftr( st , rbuf ,cbuf);
fwrite( cbuf , sizeof(kiss_fft_cpx) , (nfft/2 + 1) , fout );
}
}else{
while ( fread( cbuf , sizeof(kiss_fft_cpx) * (nfft/2+1) ,1, fin ) > 0 ) {
kiss_fftri( st , cbuf ,rbuf);
fwrite( rbuf , sizeof(kiss_fft_scalar) , nfft , fout );
}
}
free(st);
free(rbuf);
free(cbuf);
}
static
int get_dims(char * arg,int * dims)
{
char *p0;
int ndims=0;
do{
p0 = strchr(arg,',');
if (p0)
*p0++ = '\0';
dims[ndims++] = atoi(arg);
// fprintf(stderr,"dims[%d] = %d\n",ndims-1,dims[ndims-1]);
arg = p0;
}while (p0);
return ndims;
}
int main(int argc,char ** argv)
{
int isinverse=0;
int isreal=0;
FILE *fin=stdin;
FILE *fout=stdout;
int ndims=1;
int dims[32];
dims[0] = 1024; /*default fft size*/
while (1) {
int c=getopt(argc,argv,"n:iR");
if (c==-1) break;
switch (c) {
case 'n':
ndims = get_dims(optarg,dims);
break;
case 'i':isinverse=1;break;
case 'R':isreal=1;break;
case '?':
fprintf(stderr,"usage options:\n"
"\t-n d1[,d2,d3...]: fft dimension(s)\n"
"\t-i : inverse\n"
"\t-R : real input samples, not complex\n");
exit (1);
default:fprintf(stderr,"bad %c\n",c);break;
}
}
if ( optind < argc ) {
if (strcmp("-",argv[optind]) !=0)
fin = fopen(argv[optind],"rb");
++optind;
}
if ( optind < argc ) {
if ( strcmp("-",argv[optind]) !=0 )
fout = fopen(argv[optind],"wb");
++optind;
}
if (ndims==1) {
if (isreal)
fft_file_real(fin,fout,dims[0],isinverse);
else
fft_file(fin,fout,dims[0],isinverse);
}else{
if (isreal)
fft_filend_real(fin,fout,dims,ndims,isinverse);
else
fft_filend(fin,fout,dims,ndims,isinverse);
}
if (fout!=stdout) fclose(fout);
if (fin!=stdin) fclose(fin);
return 0;
}

View File

@ -0,0 +1,116 @@
#include "kfc.h"
/*
Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
typedef struct cached_fft *kfc_cfg;
struct cached_fft
{
int nfft;
int inverse;
kiss_fft_cfg cfg;
kfc_cfg next;
};
static kfc_cfg cache_root=NULL;
static int ncached=0;
static kiss_fft_cfg find_cached_fft(int nfft,int inverse)
{
size_t len;
kfc_cfg cur=cache_root;
kfc_cfg prev=NULL;
while ( cur ) {
if ( cur->nfft == nfft && inverse == cur->inverse )
break;/*found the right node*/
prev = cur;
cur = prev->next;
}
if (cur== NULL) {
/* no cached node found, need to create a new one*/
kiss_fft_alloc(nfft,inverse,0,&len);
#ifdef USE_SIMD
int padding = (16-sizeof(struct cached_fft)) & 15;
// make sure the cfg aligns on a 16 byte boundary
len += padding;
#endif
cur = (kfc_cfg)KISS_FFT_MALLOC((sizeof(struct cached_fft) + len ));
if (cur == NULL)
return NULL;
cur->cfg = (kiss_fft_cfg)(cur+1);
#ifdef USE_SIMD
cur->cfg = (kiss_fft_cfg) ((char*)(cur+1)+padding);
#endif
kiss_fft_alloc(nfft,inverse,cur->cfg,&len);
cur->nfft=nfft;
cur->inverse=inverse;
cur->next = NULL;
if ( prev )
prev->next = cur;
else
cache_root = cur;
++ncached;
}
return cur->cfg;
}
void kfc_cleanup(void)
{
kfc_cfg cur=cache_root;
kfc_cfg next=NULL;
while (cur){
next = cur->next;
free(cur);
cur=next;
}
ncached=0;
cache_root = NULL;
}
void kfc_fft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout)
{
kiss_fft( find_cached_fft(nfft,0),fin,fout );
}
void kfc_ifft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout)
{
kiss_fft( find_cached_fft(nfft,1),fin,fout );
}
#ifdef KFC_TEST
static void check(int nc)
{
if (ncached != nc) {
fprintf(stderr,"ncached should be %d,but it is %d\n",nc,ncached);
exit(1);
}
}
int main(void)
{
kiss_fft_cpx buf1[1024],buf2[1024];
memset(buf1,0,sizeof(buf1));
check(0);
kfc_fft(512,buf1,buf2);
check(1);
kfc_fft(512,buf1,buf2);
check(1);
kfc_ifft(512,buf1,buf2);
check(2);
kfc_cleanup();
check(0);
return 0;
}
#endif

View File

@ -0,0 +1,46 @@
#ifndef KFC_H
#define KFC_H
#include "kiss_fft.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
KFC -- Kiss FFT Cache
Not needing to deal with kiss_fft_alloc and a config
object may be handy for a lot of programs.
KFC uses the underlying KISS FFT functions, but caches the config object.
The first time kfc_fft or kfc_ifft for a given FFT size, the cfg
object is created for it. All subsequent calls use the cached
configuration object.
NOTE:
You should probably not use this if your program will be using a lot
of various sizes of FFTs. There is a linear search through the
cached objects. If you are only using one or two FFT sizes, this
will be negligible. Otherwise, you may want to use another method
of managing the cfg objects.
There is no automated cleanup of the cached objects. This could lead
to large memory usage in a program that uses a lot of *DIFFERENT*
sized FFTs. If you want to force all cached cfg objects to be freed,
call kfc_cleanup.
*/
/*forward complex FFT */
void kfc_fft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout);
/*reverse complex FFT */
void kfc_ifft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout);
/*free all cached objects*/
void kfc_cleanup(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,470 @@
/*
Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "_kiss_fft_guts.h"
/*
Some definitions that allow real or complex filtering
*/
#ifdef REAL_FASTFIR
#define MIN_FFT_LEN 2048
#include "kiss_fftr.h"
typedef kiss_fft_scalar kffsamp_t;
typedef kiss_fftr_cfg kfcfg_t;
#define FFT_ALLOC kiss_fftr_alloc
#define FFTFWD kiss_fftr
#define FFTINV kiss_fftri
#else
#define MIN_FFT_LEN 1024
typedef kiss_fft_cpx kffsamp_t;
typedef kiss_fft_cfg kfcfg_t;
#define FFT_ALLOC kiss_fft_alloc
#define FFTFWD kiss_fft
#define FFTINV kiss_fft
#endif
typedef struct kiss_fastfir_state *kiss_fastfir_cfg;
kiss_fastfir_cfg kiss_fastfir_alloc(const kffsamp_t * imp_resp,size_t n_imp_resp,
size_t * nfft,void * mem,size_t*lenmem);
/* see do_file_filter for usage */
size_t kiss_fastfir( kiss_fastfir_cfg cfg, kffsamp_t * inbuf, kffsamp_t * outbuf, size_t n, size_t *offset);
static int verbose=0;
struct kiss_fastfir_state{
size_t nfft;
size_t ngood;
kfcfg_t fftcfg;
kfcfg_t ifftcfg;
kiss_fft_cpx * fir_freq_resp;
kiss_fft_cpx * freqbuf;
size_t n_freq_bins;
kffsamp_t * tmpbuf;
};
kiss_fastfir_cfg kiss_fastfir_alloc(
const kffsamp_t * imp_resp,size_t n_imp_resp,
size_t *pnfft, /* if <= 0, an appropriate size will be chosen */
void * mem,size_t*lenmem)
{
kiss_fastfir_cfg st = NULL;
size_t len_fftcfg,len_ifftcfg;
size_t memneeded = sizeof(struct kiss_fastfir_state);
char * ptr;
size_t i;
size_t nfft=0;
float scale;
int n_freq_bins;
if (pnfft)
nfft=*pnfft;
if (nfft<=0) {
/* determine fft size as next power of two at least 2x
the impulse response length*/
i=n_imp_resp-1;
nfft=2;
do{
nfft<<=1;
}while (i>>=1);
#ifdef MIN_FFT_LEN
if ( nfft < MIN_FFT_LEN )
nfft=MIN_FFT_LEN;
#endif
}
if (pnfft)
*pnfft = nfft;
#ifdef REAL_FASTFIR
n_freq_bins = nfft/2 + 1;
#else
n_freq_bins = nfft;
#endif
/*fftcfg*/
FFT_ALLOC (nfft, 0, NULL, &len_fftcfg);
memneeded += len_fftcfg;
/*ifftcfg*/
FFT_ALLOC (nfft, 1, NULL, &len_ifftcfg);
memneeded += len_ifftcfg;
/* tmpbuf */
memneeded += sizeof(kffsamp_t) * nfft;
/* fir_freq_resp */
memneeded += sizeof(kiss_fft_cpx) * n_freq_bins;
/* freqbuf */
memneeded += sizeof(kiss_fft_cpx) * n_freq_bins;
if (lenmem == NULL) {
st = (kiss_fastfir_cfg) malloc (memneeded);
} else {
if (*lenmem >= memneeded)
st = (kiss_fastfir_cfg) mem;
*lenmem = memneeded;
}
if (!st)
return NULL;
st->nfft = nfft;
st->ngood = nfft - n_imp_resp + 1;
st->n_freq_bins = n_freq_bins;
ptr=(char*)(st+1);
st->fftcfg = (kfcfg_t)ptr;
ptr += len_fftcfg;
st->ifftcfg = (kfcfg_t)ptr;
ptr += len_ifftcfg;
st->tmpbuf = (kffsamp_t*)ptr;
ptr += sizeof(kffsamp_t) * nfft;
st->freqbuf = (kiss_fft_cpx*)ptr;
ptr += sizeof(kiss_fft_cpx) * n_freq_bins;
st->fir_freq_resp = (kiss_fft_cpx*)ptr;
ptr += sizeof(kiss_fft_cpx) * n_freq_bins;
FFT_ALLOC (nfft,0,st->fftcfg , &len_fftcfg);
FFT_ALLOC (nfft,1,st->ifftcfg , &len_ifftcfg);
memset(st->tmpbuf,0,sizeof(kffsamp_t)*nfft);
/*zero pad in the middle to left-rotate the impulse response
This puts the scrap samples at the end of the inverse fft'd buffer */
st->tmpbuf[0] = imp_resp[ n_imp_resp - 1 ];
for (i=0;i<n_imp_resp - 1; ++i) {
st->tmpbuf[ nfft - n_imp_resp + 1 + i ] = imp_resp[ i ];
}
FFTFWD(st->fftcfg,st->tmpbuf,st->fir_freq_resp);
/* TODO: this won't work for fixed point */
scale = 1.0 / st->nfft;
for ( i=0; i < st->n_freq_bins; ++i ) {
#ifdef USE_SIMD
st->fir_freq_resp[i].r *= _mm_set1_ps(scale);
st->fir_freq_resp[i].i *= _mm_set1_ps(scale);
#else
st->fir_freq_resp[i].r *= scale;
st->fir_freq_resp[i].i *= scale;
#endif
}
return st;
}
static void fastconv1buf(const kiss_fastfir_cfg st,const kffsamp_t * in,kffsamp_t * out)
{
size_t i;
/* multiply the frequency response of the input signal by
that of the fir filter*/
FFTFWD( st->fftcfg, in , st->freqbuf );
for ( i=0; i<st->n_freq_bins; ++i ) {
kiss_fft_cpx tmpsamp;
C_MUL(tmpsamp,st->freqbuf[i],st->fir_freq_resp[i]);
st->freqbuf[i] = tmpsamp;
}
/* perform the inverse fft*/
FFTINV(st->ifftcfg,st->freqbuf,out);
}
/* n : the size of inbuf and outbuf in samples
return value: the number of samples completely processed
n-retval samples should be copied to the front of the next input buffer */
static size_t kff_nocopy(
kiss_fastfir_cfg st,
const kffsamp_t * inbuf,
kffsamp_t * outbuf,
size_t n)
{
size_t norig=n;
while (n >= st->nfft ) {
fastconv1buf(st,inbuf,outbuf);
inbuf += st->ngood;
outbuf += st->ngood;
n -= st->ngood;
}
return norig - n;
}
static
size_t kff_flush(kiss_fastfir_cfg st,const kffsamp_t * inbuf,kffsamp_t * outbuf,size_t n)
{
size_t zpad=0,ntmp;
ntmp = kff_nocopy(st,inbuf,outbuf,n);
n -= ntmp;
inbuf += ntmp;
outbuf += ntmp;
zpad = st->nfft - n;
memset(st->tmpbuf,0,sizeof(kffsamp_t)*st->nfft );
memcpy(st->tmpbuf,inbuf,sizeof(kffsamp_t)*n );
fastconv1buf(st,st->tmpbuf,st->tmpbuf);
memcpy(outbuf,st->tmpbuf,sizeof(kffsamp_t)*( st->ngood - zpad ));
return ntmp + st->ngood - zpad;
}
size_t kiss_fastfir(
kiss_fastfir_cfg vst,
kffsamp_t * inbuf,
kffsamp_t * outbuf,
size_t n_new,
size_t *offset)
{
size_t ntot = n_new + *offset;
if (n_new==0) {
return kff_flush(vst,inbuf,outbuf,ntot);
}else{
size_t nwritten = kff_nocopy(vst,inbuf,outbuf,ntot);
*offset = ntot - nwritten;
/*save the unused or underused samples at the front of the input buffer */
memcpy( inbuf , inbuf+nwritten , *offset * sizeof(kffsamp_t) );
return nwritten;
}
}
#ifdef FAST_FILT_UTIL
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <assert.h>
static
void direct_file_filter(
FILE * fin,
FILE * fout,
const kffsamp_t * imp_resp,
size_t n_imp_resp)
{
size_t nlag = n_imp_resp - 1;
const kffsamp_t *tmph;
kffsamp_t *buf, *circbuf;
kffsamp_t outval;
size_t nread;
size_t nbuf;
size_t oldestlag = 0;
size_t k, tap;
#ifndef REAL_FASTFIR
kffsamp_t tmp;
#endif
nbuf = 4096;
buf = (kffsamp_t *) malloc ( sizeof (kffsamp_t) * nbuf);
circbuf = (kffsamp_t *) malloc (sizeof (kffsamp_t) * nlag);
if (!circbuf || !buf) {
perror("circbuf allocation");
exit(1);
}
if ( fread (circbuf, sizeof (kffsamp_t), nlag, fin) != nlag ) {
perror ("insufficient data to overcome transient");
exit (1);
}
do {
nread = fread (buf, sizeof (kffsamp_t), nbuf, fin);
if (nread <= 0)
break;
for (k = 0; k < nread; ++k) {
tmph = imp_resp+nlag;
#ifdef REAL_FASTFIR
# ifdef USE_SIMD
outval = _mm_set1_ps(0);
#else
outval = 0;
#endif
for (tap = oldestlag; tap < nlag; ++tap)
outval += circbuf[tap] * *tmph--;
for (tap = 0; tap < oldestlag; ++tap)
outval += circbuf[tap] * *tmph--;
outval += buf[k] * *tmph;
#else
# ifdef USE_SIMD
outval.r = outval.i = _mm_set1_ps(0);
#else
outval.r = outval.i = 0;
#endif
for (tap = oldestlag; tap < nlag; ++tap){
C_MUL(tmp,circbuf[tap],*tmph);
--tmph;
C_ADDTO(outval,tmp);
}
for (tap = 0; tap < oldestlag; ++tap) {
C_MUL(tmp,circbuf[tap],*tmph);
--tmph;
C_ADDTO(outval,tmp);
}
C_MUL(tmp,buf[k],*tmph);
C_ADDTO(outval,tmp);
#endif
circbuf[oldestlag++] = buf[k];
buf[k] = outval;
if (oldestlag == nlag)
oldestlag = 0;
}
if (fwrite (buf, sizeof (buf[0]), nread, fout) != nread) {
perror ("short write");
exit (1);
}
} while (nread);
free (buf);
free (circbuf);
}
static
void do_file_filter(
FILE * fin,
FILE * fout,
const kffsamp_t * imp_resp,
size_t n_imp_resp,
size_t nfft )
{
int fdout;
size_t n_samps_buf;
kiss_fastfir_cfg cfg;
kffsamp_t *inbuf,*outbuf;
int nread,nwrite;
size_t idx_inbuf;
fdout = fileno(fout);
cfg=kiss_fastfir_alloc(imp_resp,n_imp_resp,&nfft,0,0);
/* use length to minimize buffer shift*/
n_samps_buf = 8*4096/sizeof(kffsamp_t);
n_samps_buf = nfft + 4*(nfft-n_imp_resp+1);
if (verbose) fprintf(stderr,"bufsize=%d\n",(int)(sizeof(kffsamp_t)*n_samps_buf) );
/*allocate space and initialize pointers */
inbuf = (kffsamp_t*)malloc(sizeof(kffsamp_t)*n_samps_buf);
outbuf = (kffsamp_t*)malloc(sizeof(kffsamp_t)*n_samps_buf);
idx_inbuf=0;
do{
/* start reading at inbuf[idx_inbuf] */
nread = fread( inbuf + idx_inbuf, sizeof(kffsamp_t), n_samps_buf - idx_inbuf,fin );
/* If nread==0, then this is a flush.
The total number of samples in input is idx_inbuf + nread . */
nwrite = kiss_fastfir(cfg, inbuf, outbuf,nread,&idx_inbuf) * sizeof(kffsamp_t);
/* kiss_fastfir moved any unused samples to the front of inbuf and updated idx_inbuf */
if ( write(fdout, outbuf, nwrite) != nwrite ) {
perror("short write");
exit(1);
}
}while ( nread );
free(cfg);
free(inbuf);
free(outbuf);
}
int main(int argc,char**argv)
{
kffsamp_t * h;
int use_direct=0;
size_t nh,nfft=0;
FILE *fin=stdin;
FILE *fout=stdout;
FILE *filtfile=NULL;
while (1) {
int c=getopt(argc,argv,"n:h:i:o:vd");
if (c==-1) break;
switch (c) {
case 'v':
verbose=1;
break;
case 'n':
nfft=atoi(optarg);
break;
case 'i':
fin = fopen(optarg,"rb");
if (fin==NULL) {
perror(optarg);
exit(1);
}
break;
case 'o':
fout = fopen(optarg,"w+b");
if (fout==NULL) {
perror(optarg);
exit(1);
}
break;
case 'h':
filtfile = fopen(optarg,"rb");
if (filtfile==NULL) {
perror(optarg);
exit(1);
}
break;
case 'd':
use_direct=1;
break;
case '?':
fprintf(stderr,"usage options:\n"
"\t-n nfft: fft size to use\n"
"\t-d : use direct FIR filtering, not fast convolution\n"
"\t-i filename: input file\n"
"\t-o filename: output(filtered) file\n"
"\t-n nfft: fft size to use\n"
"\t-h filename: impulse response\n");
exit (1);
default:fprintf(stderr,"bad %c\n",c);break;
}
}
if (filtfile==NULL) {
fprintf(stderr,"You must supply the FIR coeffs via -h\n");
exit(1);
}
fseek(filtfile,0,SEEK_END);
nh = ftell(filtfile) / sizeof(kffsamp_t);
if (verbose) fprintf(stderr,"%d samples in FIR filter\n",(int)nh);
h = (kffsamp_t*)malloc(sizeof(kffsamp_t)*nh);
fseek(filtfile,0,SEEK_SET);
if (fread(h,sizeof(kffsamp_t),nh,filtfile) != nh)
fprintf(stderr,"short read on filter file\n");
fclose(filtfile);
if (use_direct)
direct_file_filter( fin, fout, h,nh);
else
do_file_filter( fin, fout, h,nh,nfft);
if (fout!=stdout) fclose(fout);
if (fin!=stdin) fclose(fin);
return 0;
}
#endif

View File

@ -0,0 +1,193 @@
/*
Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "kiss_fftnd.h"
#include "_kiss_fft_guts.h"
struct kiss_fftnd_state{
int dimprod; /* dimsum would be mighty tasty right now */
int ndims;
int *dims;
kiss_fft_cfg *states; /* cfg states for each dimension */
kiss_fft_cpx * tmpbuf; /*buffer capable of hold the entire input */
};
kiss_fftnd_cfg kiss_fftnd_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem)
{
kiss_fftnd_cfg st = NULL;
int i;
int dimprod=1;
size_t memneeded = sizeof(struct kiss_fftnd_state);
char * ptr;
for (i=0;i<ndims;++i) {
size_t sublen=0;
kiss_fft_alloc (dims[i], inverse_fft, NULL, &sublen);
memneeded += sublen; /* st->states[i] */
dimprod *= dims[i];
}
memneeded += sizeof(int) * ndims;/* st->dims */
memneeded += sizeof(void*) * ndims;/* st->states */
memneeded += sizeof(kiss_fft_cpx) * dimprod; /* st->tmpbuf */
if (lenmem == NULL) {/* allocate for the caller*/
st = (kiss_fftnd_cfg) malloc (memneeded);
} else { /* initialize supplied buffer if big enough */
if (*lenmem >= memneeded)
st = (kiss_fftnd_cfg) mem;
*lenmem = memneeded; /*tell caller how big struct is (or would be) */
}
if (!st)
return NULL; /*malloc failed or buffer too small */
st->dimprod = dimprod;
st->ndims = ndims;
ptr=(char*)(st+1);
st->states = (kiss_fft_cfg *)ptr;
ptr += sizeof(void*) * ndims;
st->dims = (int*)ptr;
ptr += sizeof(int) * ndims;
st->tmpbuf = (kiss_fft_cpx*)ptr;
ptr += sizeof(kiss_fft_cpx) * dimprod;
for (i=0;i<ndims;++i) {
size_t len;
st->dims[i] = dims[i];
kiss_fft_alloc (st->dims[i], inverse_fft, NULL, &len);
st->states[i] = kiss_fft_alloc (st->dims[i], inverse_fft, ptr,&len);
ptr += len;
}
/*
Hi there!
If you're looking at this particular code, it probably means you've got a brain-dead bounds checker
that thinks the above code overwrites the end of the array.
It doesn't.
-- Mark
P.S.
The below code might give you some warm fuzzies and help convince you.
*/
if ( ptr - (char*)st != (int)memneeded ) {
fprintf(stderr,
"################################################################################\n"
"Internal error! Memory allocation miscalculation\n"
"################################################################################\n"
);
}
return st;
}
/*
This works by tackling one dimension at a time.
In effect,
Each stage starts out by reshaping the matrix into a DixSi 2d matrix.
A Di-sized fft is taken of each column, transposing the matrix as it goes.
Here's a 3-d example:
Take a 2x3x4 matrix, laid out in memory as a contiguous buffer
[ [ [ a b c d ] [ e f g h ] [ i j k l ] ]
[ [ m n o p ] [ q r s t ] [ u v w x ] ] ]
Stage 0 ( D=2): treat the buffer as a 2x12 matrix
[ [a b ... k l]
[m n ... w x] ]
FFT each column with size 2.
Transpose the matrix at the same time using kiss_fft_stride.
[ [ a+m a-m ]
[ b+n b-n]
...
[ k+w k-w ]
[ l+x l-x ] ]
Note fft([x y]) == [x+y x-y]
Stage 1 ( D=3) treats the buffer (the output of stage D=2) as an 3x8 matrix,
[ [ a+m a-m b+n b-n c+o c-o d+p d-p ]
[ e+q e-q f+r f-r g+s g-s h+t h-t ]
[ i+u i-u j+v j-v k+w k-w l+x l-x ] ]
And perform FFTs (size=3) on each of the columns as above, transposing
the matrix as it goes. The output of stage 1 is
(Legend: ap = [ a+m e+q i+u ]
am = [ a-m e-q i-u ] )
[ [ sum(ap) fft(ap)[0] fft(ap)[1] ]
[ sum(am) fft(am)[0] fft(am)[1] ]
[ sum(bp) fft(bp)[0] fft(bp)[1] ]
[ sum(bm) fft(bm)[0] fft(bm)[1] ]
[ sum(cp) fft(cp)[0] fft(cp)[1] ]
[ sum(cm) fft(cm)[0] fft(cm)[1] ]
[ sum(dp) fft(dp)[0] fft(dp)[1] ]
[ sum(dm) fft(dm)[0] fft(dm)[1] ] ]
Stage 2 ( D=4) treats this buffer as a 4*6 matrix,
[ [ sum(ap) fft(ap)[0] fft(ap)[1] sum(am) fft(am)[0] fft(am)[1] ]
[ sum(bp) fft(bp)[0] fft(bp)[1] sum(bm) fft(bm)[0] fft(bm)[1] ]
[ sum(cp) fft(cp)[0] fft(cp)[1] sum(cm) fft(cm)[0] fft(cm)[1] ]
[ sum(dp) fft(dp)[0] fft(dp)[1] sum(dm) fft(dm)[0] fft(dm)[1] ] ]
Then FFTs each column, transposing as it goes.
The resulting matrix is the 3d FFT of the 2x3x4 input matrix.
Note as a sanity check that the first element of the final
stage's output (DC term) is
sum( [ sum(ap) sum(bp) sum(cp) sum(dp) ] )
, i.e. the summation of all 24 input elements.
*/
void kiss_fftnd(kiss_fftnd_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
{
int i,k;
const kiss_fft_cpx * bufin=fin;
kiss_fft_cpx * bufout;
/*arrange it so the last bufout == fout*/
if ( st->ndims & 1 ) {
bufout = fout;
if (fin==fout) {
memcpy( st->tmpbuf, fin, sizeof(kiss_fft_cpx) * st->dimprod );
bufin = st->tmpbuf;
}
}else
bufout = st->tmpbuf;
for ( k=0; k < st->ndims; ++k) {
int curdim = st->dims[k];
int stride = st->dimprod / curdim;
for ( i=0 ; i<stride ; ++i )
kiss_fft_stride( st->states[k], bufin+i , bufout+i*curdim, stride );
/*toggle back and forth between the two buffers*/
if (bufout == st->tmpbuf){
bufout = fout;
bufin = st->tmpbuf;
}else{
bufout = st->tmpbuf;
bufin = fout;
}
}
}

View File

@ -0,0 +1,18 @@
#ifndef KISS_FFTND_H
#define KISS_FFTND_H
#include "kiss_fft.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kiss_fftnd_state * kiss_fftnd_cfg;
kiss_fftnd_cfg kiss_fftnd_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem);
void kiss_fftnd(kiss_fftnd_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,118 @@
/*
Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "kiss_fftndr.h"
#include "_kiss_fft_guts.h"
#define MAX(x,y) ( ( (x)<(y) )?(y):(x) )
struct kiss_fftndr_state
{
int dimReal;
int dimOther;
kiss_fftr_cfg cfg_r;
kiss_fftnd_cfg cfg_nd;
void * tmpbuf;
};
static int prod(const int *dims, int ndims)
{
int x=1;
while (ndims--)
x *= *dims++;
return x;
}
kiss_fftndr_cfg kiss_fftndr_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem)
{
kiss_fftndr_cfg st = NULL;
size_t nr=0 , nd=0,ntmp=0;
int dimReal = dims[ndims-1];
int dimOther = prod(dims,ndims-1);
size_t memneeded;
(void)kiss_fftr_alloc(dimReal,inverse_fft,NULL,&nr);
(void)kiss_fftnd_alloc(dims,ndims-1,inverse_fft,NULL,&nd);
ntmp =
MAX( 2*dimOther , dimReal+2) * sizeof(kiss_fft_scalar) // freq buffer for one pass
+ dimOther*(dimReal+2) * sizeof(kiss_fft_scalar); // large enough to hold entire input in case of in-place
memneeded = sizeof( struct kiss_fftndr_state ) + nr + nd + ntmp;
if (lenmem==NULL) {
st = (kiss_fftndr_cfg) malloc(memneeded);
}else{
if (*lenmem >= memneeded)
st = (kiss_fftndr_cfg)mem;
*lenmem = memneeded;
}
if (st==NULL)
return NULL;
memset( st , 0 , memneeded);
st->dimReal = dimReal;
st->dimOther = dimOther;
st->cfg_r = kiss_fftr_alloc( dimReal,inverse_fft,st+1,&nr);
st->cfg_nd = kiss_fftnd_alloc(dims,ndims-1,inverse_fft, ((char*) st->cfg_r)+nr,&nd);
st->tmpbuf = (char*)st->cfg_nd + nd;
return st;
}
void kiss_fftndr(kiss_fftndr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
{
int k1,k2;
int dimReal = st->dimReal;
int dimOther = st->dimOther;
int nrbins = dimReal/2+1;
kiss_fft_cpx * tmp1 = (kiss_fft_cpx*)st->tmpbuf;
kiss_fft_cpx * tmp2 = tmp1 + MAX(nrbins,dimOther);
// timedata is N0 x N1 x ... x Nk real
// take a real chunk of data, fft it and place the output at correct intervals
for (k1=0;k1<dimOther;++k1) {
kiss_fftr( st->cfg_r, timedata + k1*dimReal , tmp1 ); // tmp1 now holds nrbins complex points
for (k2=0;k2<nrbins;++k2)
tmp2[ k2*dimOther+k1 ] = tmp1[k2];
}
for (k2=0;k2<nrbins;++k2) {
kiss_fftnd(st->cfg_nd, tmp2+k2*dimOther, tmp1); // tmp1 now holds dimOther complex points
for (k1=0;k1<dimOther;++k1)
freqdata[ k1*(nrbins) + k2] = tmp1[k1];
}
}
void kiss_fftndri(kiss_fftndr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
{
int k1,k2;
int dimReal = st->dimReal;
int dimOther = st->dimOther;
int nrbins = dimReal/2+1;
kiss_fft_cpx * tmp1 = (kiss_fft_cpx*)st->tmpbuf;
kiss_fft_cpx * tmp2 = tmp1 + MAX(nrbins,dimOther);
for (k2=0;k2<nrbins;++k2) {
for (k1=0;k1<dimOther;++k1)
tmp1[k1] = freqdata[ k1*(nrbins) + k2 ];
kiss_fftnd(st->cfg_nd, tmp1, tmp2+k2*dimOther);
}
for (k1=0;k1<dimOther;++k1) {
for (k2=0;k2<nrbins;++k2)
tmp1[k2] = tmp2[ k2*dimOther+k1 ];
kiss_fftri( st->cfg_r,tmp1,timedata + k1*dimReal);
}
}

View File

@ -0,0 +1,47 @@
#ifndef KISS_NDR_H
#define KISS_NDR_H
#include "kiss_fft.h"
#include "kiss_fftr.h"
#include "kiss_fftnd.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct kiss_fftndr_state *kiss_fftndr_cfg;
kiss_fftndr_cfg kiss_fftndr_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem);
/*
dims[0] must be even
If you don't care to allocate space, use mem = lenmem = NULL
*/
void kiss_fftndr(
kiss_fftndr_cfg cfg,
const kiss_fft_scalar *timedata,
kiss_fft_cpx *freqdata);
/*
input timedata has dims[0] X dims[1] X ... X dims[ndims-1] scalar points
output freqdata has dims[0] X dims[1] X ... X dims[ndims-1]/2+1 complex points
*/
void kiss_fftndri(
kiss_fftndr_cfg cfg,
const kiss_fft_cpx *freqdata,
kiss_fft_scalar *timedata);
/*
input and output dimensions are the exact opposite of kiss_fftndr
*/
#define kiss_fftr_free free
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,159 @@
/*
Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "kiss_fftr.h"
#include "_kiss_fft_guts.h"
struct kiss_fftr_state{
kiss_fft_cfg substate;
kiss_fft_cpx * tmpbuf;
kiss_fft_cpx * super_twiddles;
#ifdef USE_SIMD
void * pad;
#endif
};
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
{
int i;
kiss_fftr_cfg st = NULL;
size_t subsize, memneeded;
if (nfft & 1) {
fprintf(stderr,"Real FFT optimization must be even.\n");
return NULL;
}
nfft >>= 1;
kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2);
if (lenmem == NULL) {
st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
} else {
if (*lenmem >= memneeded)
st = (kiss_fftr_cfg) mem;
*lenmem = memneeded;
}
if (!st)
return NULL;
st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
st->super_twiddles = st->tmpbuf + nfft;
kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
for (i = 0; i < nfft/2; ++i) {
double phase =
-3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
if (inverse_fft)
phase *= -1;
kf_cexp (st->super_twiddles+i,phase);
}
return st;
}
void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
{
/* input buffer timedata is stored row-wise */
int k,ncfft;
kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
if ( st->substate->inverse) {
fprintf(stderr,"kiss fft usage error: improper alloc\n");
exit(1);
}
ncfft = st->substate->nfft;
/*perform the parallel fft of two real signals packed in real,imag*/
kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
/* The real part of the DC element of the frequency spectrum in st->tmpbuf
* contains the sum of the even-numbered elements of the input time sequence
* The imag part is the sum of the odd-numbered elements
*
* The sum of tdc.r and tdc.i is the sum of the input time sequence.
* yielding DC of input time sequence
* The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
* yielding Nyquist bin of input time sequence
*/
tdc.r = st->tmpbuf[0].r;
tdc.i = st->tmpbuf[0].i;
C_FIXDIV(tdc,2);
CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
freqdata[0].r = tdc.r + tdc.i;
freqdata[ncfft].r = tdc.r - tdc.i;
#ifdef USE_SIMD
freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
#else
freqdata[ncfft].i = freqdata[0].i = 0;
#endif
for ( k=1;k <= ncfft/2 ; ++k ) {
fpk = st->tmpbuf[k];
fpnk.r = st->tmpbuf[ncfft-k].r;
fpnk.i = - st->tmpbuf[ncfft-k].i;
C_FIXDIV(fpk,2);
C_FIXDIV(fpnk,2);
C_ADD( f1k, fpk , fpnk );
C_SUB( f2k, fpk , fpnk );
C_MUL( tw , f2k , st->super_twiddles[k-1]);
freqdata[k].r = HALF_OF(f1k.r + tw.r);
freqdata[k].i = HALF_OF(f1k.i + tw.i);
freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
}
}
void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
{
/* input buffer timedata is stored row-wise */
int k, ncfft;
if (st->substate->inverse == 0) {
fprintf (stderr, "kiss fft usage error: improper alloc\n");
exit (1);
}
ncfft = st->substate->nfft;
st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
C_FIXDIV(st->tmpbuf[0],2);
for (k = 1; k <= ncfft / 2; ++k) {
kiss_fft_cpx fk, fnkc, fek, fok, tmp;
fk = freqdata[k];
fnkc.r = freqdata[ncfft - k].r;
fnkc.i = -freqdata[ncfft - k].i;
C_FIXDIV( fk , 2 );
C_FIXDIV( fnkc , 2 );
C_ADD (fek, fk, fnkc);
C_SUB (tmp, fk, fnkc);
C_MUL (fok, tmp, st->super_twiddles[k-1]);
C_ADD (st->tmpbuf[k], fek, fok);
C_SUB (st->tmpbuf[ncfft - k], fek, fok);
#ifdef USE_SIMD
st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
#else
st->tmpbuf[ncfft - k].i *= -1;
#endif
}
kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
}

View File

@ -0,0 +1,46 @@
#ifndef KISS_FTR_H
#define KISS_FTR_H
#include "kiss_fft.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
*/
typedef struct kiss_fftr_state *kiss_fftr_cfg;
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
/*
nfft must be even
If you don't care to allocate space, use mem = lenmem = NULL
*/
void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
/*
input timedata has nfft scalar points
output freqdata has nfft/2+1 complex points
*/
void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
/*
input freqdata has nfft/2+1 complex points
output timedata has nfft scalar points
*/
#define kiss_fftr_free free
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,235 @@
/*
Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <png.h>
#include "kiss_fft.h"
#include "kiss_fftr.h"
int nfft=1024;
FILE * fin=NULL;
FILE * fout=NULL;
int navg=20;
int remove_dc=0;
int nrows=0;
float * vals=NULL;
int stereo=0;
static
void config(int argc,char** argv)
{
while (1) {
int c = getopt (argc, argv, "n:r:as");
if (c == -1)
break;
switch (c) {
case 'n': nfft=(int)atoi(optarg);break;
case 'r': navg=(int)atoi(optarg);break;
case 'a': remove_dc=1;break;
case 's': stereo=1;break;
case '?':
fprintf (stderr, "usage options:\n"
"\t-n d: fft dimension(s) [1024]\n"
"\t-r d: number of rows to average [20]\n"
"\t-a : remove average from each fft buffer\n"
"\t-s : input is stereo, channels will be combined before fft\n"
"16 bit machine format real input is assumed\n"
);
default:
fprintf (stderr, "bad %c\n", c);
exit (1);
break;
}
}
if ( optind < argc ) {
if (strcmp("-",argv[optind]) !=0)
fin = fopen(argv[optind],"rb");
++optind;
}
if ( optind < argc ) {
if ( strcmp("-",argv[optind]) !=0 )
fout = fopen(argv[optind],"wb");
++optind;
}
if (fin==NULL)
fin=stdin;
if (fout==NULL)
fout=stdout;
}
#define CHECKNULL(p) if ( (p)==NULL ) do { fprintf(stderr,"CHECKNULL failed @ %s(%d): %s\n",__FILE__,__LINE__,#p );exit(1);} while(0)
typedef struct
{
png_byte r;
png_byte g;
png_byte b;
} rgb_t;
static
void val2rgb(float x,rgb_t *p)
{
const double pi = 3.14159265358979;
p->g = (int)(255*sin(x*pi));
p->r = (int)(255*abs(sin(x*pi*3/2)));
p->b = (int)(255*abs(sin(x*pi*5/2)));
//fprintf(stderr,"%.2f : %d,%d,%d\n",x,(int)p->r,(int)p->g,(int)p->b);
}
static
void cpx2pixels(rgb_t * res,const float * fbuf,size_t n)
{
size_t i;
float minval,maxval,valrange;
minval=maxval=fbuf[0];
for (i = 0; i < n; ++i) {
if (fbuf[i] > maxval) maxval = fbuf[i];
if (fbuf[i] < minval) minval = fbuf[i];
}
fprintf(stderr,"min ==%f,max=%f\n",minval,maxval);
valrange = maxval-minval;
if (valrange == 0) {
fprintf(stderr,"min == max == %f\n",minval);
exit (1);
}
for (i = 0; i < n; ++i)
val2rgb( (fbuf[i] - minval)/valrange , res+i );
}
static
void transform_signal(void)
{
short *inbuf;
kiss_fftr_cfg cfg=NULL;
kiss_fft_scalar *tbuf;
kiss_fft_cpx *fbuf;
float *mag2buf;
int i;
int n;
int avgctr=0;
int nfreqs=nfft/2+1;
CHECKNULL( cfg=kiss_fftr_alloc(nfft,0,0,0) );
CHECKNULL( inbuf=(short*)malloc(sizeof(short)*2*nfft ) );
CHECKNULL( tbuf=(kiss_fft_scalar*)malloc(sizeof(kiss_fft_scalar)*nfft ) );
CHECKNULL( fbuf=(kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx)*nfreqs ) );
CHECKNULL( mag2buf=(float*)malloc(sizeof(float)*nfreqs ) );
memset(mag2buf,0,sizeof(mag2buf)*nfreqs);
while (1) {
if (stereo) {
n = fread(inbuf,sizeof(short)*2,nfft,fin);
if (n != nfft )
break;
for (i=0;i<nfft;++i)
tbuf[i] = inbuf[2*i] + inbuf[2*i+1];
}else{
n = fread(inbuf,sizeof(short),nfft,fin);
if (n != nfft )
break;
for (i=0;i<nfft;++i)
tbuf[i] = inbuf[i];
}
if (remove_dc) {
float avg = 0;
for (i=0;i<nfft;++i) avg += tbuf[i];
avg /= nfft;
for (i=0;i<nfft;++i) tbuf[i] -= (kiss_fft_scalar)avg;
}
/* do FFT */
kiss_fftr(cfg,tbuf,fbuf);
for (i=0;i<nfreqs;++i)
mag2buf[i] += fbuf[i].r * fbuf[i].r + fbuf[i].i * fbuf[i].i;
if (++avgctr == navg) {
avgctr=0;
++nrows;
vals = (float*)realloc(vals,sizeof(float)*nrows*nfreqs);
float eps = 1;
for (i=0;i<nfreqs;++i)
vals[(nrows - 1) * nfreqs + i] = 10 * log10 ( mag2buf[i] / navg + eps );
memset(mag2buf,0,sizeof(mag2buf[0])*nfreqs);
}
}
free(cfg);
free(inbuf);
free(tbuf);
free(fbuf);
free(mag2buf);
}
static
void make_png(void)
{
png_bytepp row_pointers=NULL;
rgb_t * row_data=NULL;
int i;
int nfreqs = nfft/2+1;
png_structp png_ptr=NULL;
png_infop info_ptr=NULL;
CHECKNULL( png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,0,0,0) );
CHECKNULL( info_ptr = png_create_info_struct(png_ptr) );
png_init_io(png_ptr, fout );
png_set_IHDR(png_ptr, info_ptr ,nfreqs,nrows,8,PNG_COLOR_TYPE_RGB,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT );
row_data = (rgb_t*)malloc(sizeof(rgb_t) * nrows * nfreqs) ;
cpx2pixels(row_data, vals, nfreqs*nrows );
row_pointers = realloc(row_pointers, nrows*sizeof(png_bytep));
for (i=0;i<nrows;++i) {
row_pointers[i] = (png_bytep)(row_data + i*nfreqs);
}
png_set_rows(png_ptr, info_ptr, row_pointers);
fprintf(stderr,"creating %dx%d png\n",nfreqs,nrows);
fprintf(stderr,"bitdepth %d \n",png_get_bit_depth(png_ptr,info_ptr ) );
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY , NULL);
}
int main(int argc,char ** argv)
{
config(argc,argv);
transform_signal();
make_png();
if (fout!=stdout) fclose(fout);
if (fin!=stdin) fclose(fin);
return 0;
}

View File

@ -46,11 +46,11 @@ model_t* hmm_init(double** x, int T, int L, int N)
{
int i, j, d, e, t;
double s, ss;
model_t* model;
model = (model_t*) malloc(sizeof(model_t));
model->N = N;
model->L = L;
model->L = L;
model->p0 = (double*) malloc(N*sizeof(double));
model->a = (double**) malloc(N*sizeof(double*));
model->mu = (double**) malloc(N*sizeof(double*));
@ -62,10 +62,10 @@ model_t* hmm_init(double** x, int T, int L, int N)
model->cov = (double**) malloc(L*sizeof(double*));
for (i = 0; i < L; i++)
model->cov[i] = (double*) malloc(L*sizeof(double));
srand(time(0));
double* global_mean = (double*) malloc(L*sizeof(double));
/* find global mean */
for (d = 0; d < L; d++)
{
@ -74,7 +74,7 @@ model_t* hmm_init(double** x, int T, int L, int N)
global_mean[d] += x[t][d];
global_mean[d] /= T;
}
/* calculate global diagonal covariance */
for (d = 0; d < L; d++)
{
@ -84,7 +84,7 @@ model_t* hmm_init(double** x, int T, int L, int N)
model->cov[d][d] += (x[t][d] - global_mean[d]) * (x[t][d] - global_mean[d]);
model->cov[d][d] /= T-1;
}
/* set all means close to global mean */
for (i = 0; i < N; i++)
{
@ -94,8 +94,8 @@ model_t* hmm_init(double** x, int T, int L, int N)
/* ideally the random number would be Gaussian(0,1), as a hack we make it uniform on [-0.25,0.25] */
model->mu[i][d] = global_mean[d] + (0.5 * rand() / (double) RAND_MAX - 0.25) * sqrt(model->cov[d][d]);
}
}
}
/* random intial and transition probs */
s = 0;
for (i = 0; i < N; i++)
@ -115,16 +115,16 @@ model_t* hmm_init(double** x, int T, int L, int N)
}
for (i = 0; i < N; i++)
model->p0[i] /= s;
free(global_mean);
return model;
}
void hmm_close(model_t* model)
{
int i;
for (i = 0; i < model->N; i++)
{
free(model->a[i]);
@ -134,23 +134,23 @@ void hmm_close(model_t* model)
free(model->mu);
for (i = 0; i < model->L; i++)
free(model->cov[i]);
free(model->cov);
free(model);
free(model->cov);
free(model);
}
void hmm_train(double** x, int T, model_t* model)
{
int i, t;
double loglik; /* overall log-likelihood at each iteration */
int N = model->N;
int L = model->L;
double* p0 = model->p0;
double** a = model->a;
double** mu = model->mu;
double** cov = model->cov;
/* allocate memory */
/* allocate memory */
double** gamma = (double**) malloc(T*sizeof(double*));
double*** xi = (double***) malloc(T*sizeof(double**));
for (t = 0; t < T; t++)
@ -160,45 +160,45 @@ void hmm_train(double** x, int T, model_t* model)
for (i = 0; i < N; i++)
xi[t][i] = (double*) malloc(N*sizeof(double));
}
/* temporary memory */
double* gauss_y = (double*) malloc(L*sizeof(double));
double* gauss_z = (double*) malloc(L*sizeof(double));
double* gauss_z = (double*) malloc(L*sizeof(double));
/* obs probs P(j|{x}) */
double** b = (double**) malloc(T*sizeof(double*));
for (t = 0; t < T; t++)
b[t] = (double*) malloc(N*sizeof(double));
/* inverse covariance and its determinant */
double** icov = (double**) malloc(L*sizeof(double*));
for (i = 0; i < L; i++)
icov[i] = (double*) malloc(L*sizeof(double));
double detcov;
double thresh = 0.0001;
int niter = 50;
int niter = 50;
int iter = 0;
double loglik1, loglik2;
int foundnan = 0;
while (iter < niter && !foundnan && !(iter > 1 && (loglik - loglik1) < thresh * (loglik1 - loglik2)))
while (iter < niter && !foundnan && !(iter > 1 && (loglik - loglik1) < thresh * (loglik1 - loglik2)))
{
++iter;
/*
/*
fprintf(stderr, "calculating obsprobs...\n");
fflush(stderr);
*/
*/
/* precalculate obs probs */
invert(cov, L, icov, &detcov);
for (t = 0; t < T; t++)
{
//int allzero = 1;
for (i = 0; i < N; i++)
{
b[t][i] = exp(loggauss(x[t], L, mu[i], icov, detcov, gauss_y, gauss_z));
//if (b[t][i] != 0)
// allzero = 0;
}
@ -213,13 +213,13 @@ void hmm_train(double** x, int T, model_t* model)
}
*/
}
/*
/*
fprintf(stderr, "forwards-backwards...\n");
fflush(stderr);
*/
*/
forward_backwards(xi, gamma, &loglik, &loglik1, &loglik2, iter, N, T, p0, a, b);
/*
fprintf(stderr, "iteration %d: loglik = %f\n", iter, loglik);
/*
fprintf(stderr, "iteration %d: loglik = %f\n", iter, loglik);
fprintf(stderr, "re-estimation...\n");
fflush(stderr);
*/
@ -227,9 +227,9 @@ void hmm_train(double** x, int T, model_t* model)
foundnan = 1;
continue;
}
baum_welch(p0, a, mu, cov, N, T, L, x, xi, gamma);
/*
printf("a:\n");
for (i = 0; i < model->N; i++)
@ -242,7 +242,7 @@ void hmm_train(double** x, int T, model_t* model)
*/
//hmm_print(model);
}
/* deallocate memory */
for (t = 0; t < T; t++)
{
@ -254,12 +254,12 @@ void hmm_train(double** x, int T, model_t* model)
}
free(gamma);
free(xi);
free(b);
free(b);
for (i = 0; i < L; i++)
free(icov[i]);
free(icov);
free(gauss_y);
free(gauss_z);
}
@ -267,27 +267,27 @@ void hmm_train(double** x, int T, model_t* model)
void mlss_reestimate(double* p0, double** a, double** mu, double** cov, int N, int T, int L, int* q, double** x)
{
/* fit a single Gaussian to observations in each state */
/* calculate the mean observation in each state */
/* calculate the overall covariance */
/* count transitions */
/* estimate initial probs from transitions (???) */
}
void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T, int L, double** x, double*** xi, double** gamma)
{
int i, j, t;
double* sum_gamma = (double*) malloc(N*sizeof(double));
/* temporary memory */
double* u = (double*) malloc(L*L*sizeof(double));
double* yy = (double*) malloc(T*L*sizeof(double));
double* yy2 = (double*) malloc(T*L*sizeof(double));
double* yy2 = (double*) malloc(T*L*sizeof(double));
/* re-estimate transition probs */
for (i = 0; i < N; i++)
{
@ -295,7 +295,7 @@ void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T,
for (t = 0; t < T-1; t++)
sum_gamma[i] += gamma[t][i];
}
for (i = 0; i < N; i++)
{
if (sum_gamma[i] == 0)
@ -310,7 +310,7 @@ void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T,
for (t = 0; t < T-1; t++)
a[i][j] += xi[t][i][j];
//s += a[i][j];
a[i][j] /= sum_gamma[i];
a[i][j] /= sum_gamma[i];
}
/*
for (j = 0; j < N; j++)
@ -319,21 +319,21 @@ void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T,
}
*/
}
/* NB: now we need to sum gamma over all t */
for (i = 0; i < N; i++)
sum_gamma[i] += gamma[T-1][i];
/* re-estimate initial probs */
for (i = 0; i < N; i++)
p0[i] = gamma[0][i];
/* re-estimate covariance */
int d, e;
double sum_sum_gamma = 0;
for (i = 0; i < N; i++)
sum_sum_gamma += sum_gamma[i];
sum_sum_gamma += sum_gamma[i];
/*
for (d = 0; d < L; d++)
{
@ -343,9 +343,9 @@ void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T,
for (t = 0; t < T; t++)
for (j = 0; j < N; j++)
cov[d][e] += gamma[t][j] * (x[t][d] - mu[j][d]) * (x[t][e] - mu[j][e]);
cov[d][e] /= sum_sum_gamma;
if (ISNAN(cov[d][e]))
{
printf("cov[%d][%d] was nan\n", d, e);
@ -365,12 +365,12 @@ void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T,
for (e = 0; e < d; e++)
cov[d][e] = cov[e][d];
*/
/* using BLAS */
for (d = 0; d < L; d++)
for (e = 0; e < L; e++)
cov[d][e] = 0;
for (j = 0; j < N; j++)
{
for (d = 0; d < L; d++)
@ -379,20 +379,20 @@ void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T,
yy[d*T+t] = x[t][d] - mu[j][d];
yy2[d*T+t] = gamma[t][j] * (x[t][d] - mu[j][d]);
}
cblas_dgemm(CblasColMajor, CblasTrans, CblasNoTrans, L, L, T, 1.0, yy, T, yy2, T, 0, u, L);
for (e = 0; e < L; e++)
for (d = 0; d < L; d++)
cov[d][e] += u[e*L+d];
}
for (d = 0; d < L; d++)
for (e = 0; e < L; e++)
cov[d][e] /= T; /* sum_sum_gamma; */
cov[d][e] /= T; /* sum_sum_gamma; */
//printf("sum_sum_gamma = %f\n", sum_sum_gamma); /* fine, = T IS THIS ALWAYS TRUE with pooled cov?? */
/* re-estimate means */
for (j = 0; j < N; j++)
{
@ -404,7 +404,7 @@ void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T,
mu[j][d] /= sum_gamma[j];
}
}
/* deallocate memory */
free(sum_gamma);
free(yy);
@ -416,7 +416,7 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
{
/* forwards-backwards with scaling */
int i, j, t;
double** alpha = (double**) malloc(T*sizeof(double*));
double** beta = (double**) malloc(T*sizeof(double*));
for (t = 0; t < T; t++)
@ -424,34 +424,34 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
alpha[t] = (double*) malloc(N*sizeof(double));
beta[t] = (double*) malloc(N*sizeof(double));
}
/* scaling coefficients */
double* c = (double*) malloc(T*sizeof(double));
/* calculate forward probs and scale coefficients */
c[0] = 0;
for (i = 0; i < N; i++)
{
alpha[0][i] = p0[i] * b[0][i];
c[0] += alpha[0][i];
//printf("p0[%d] = %f, b[0][%d] = %f\n", i, p0[i], i, b[0][i]);
}
c[0] = 1 / c[0];
for (i = 0; i < N; i++)
{
alpha[0][i] *= c[0];
alpha[0][i] *= c[0];
//printf("alpha[0][%d] = %f\n", i, alpha[0][i]); /* OK agrees with Matlab */
}
*loglik1 = *loglik;
*loglik = -log(c[0]);
if (iter == 2)
*loglik2 = *loglik;
for (t = 1; t < T; t++)
{
{
c[t] = 0;
for (j = 0; j < N; j++)
{
@ -459,10 +459,10 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
for (i = 0; i < N; i++)
alpha[t][j] += alpha[t-1][i] * a[i][j];
alpha[t][j] *= b[t][j];
c[t] += alpha[t][j];
}
/*
if (c[t] == 0)
{
@ -477,16 +477,16 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
exit(-1);
}
*/
c[t] = 1 / c[t];
for (j = 0; j < N; j++)
alpha[t][j] *= c[t];
//printf("c[%d] = %e\n", t, c[t]);
*loglik -= log(c[t]);
}
/* calculate backwards probs using same coefficients */
for (i = 0; i < N; i++)
beta[T-1][i] = 1;
@ -495,20 +495,20 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
{
for (i = 0; i < N; i++)
beta[t][i] *= c[t];
if (t == 0)
break;
for (i = 0; i < N; i++)
{
beta[t-1][i] = 0;
for (j = 0; j < N; j++)
beta[t-1][i] += a[i][j] * b[t][j] * beta[t][j];
}
t--;
}
/*
printf("alpha:\n");
for (t = 0; t < T; t++)
@ -526,7 +526,7 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
}
printf("\n\n");
*/
/* calculate posterior probs */
double tot;
for (t = 0; t < T; t++)
@ -539,12 +539,12 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
}
for (i = 0; i < N; i++)
{
gamma[t][i] /= tot;
//printf("gamma[%d][%d] = %f\n", t, i, gamma[t][i]);
gamma[t][i] /= tot;
//printf("gamma[%d][%d] = %f\n", t, i, gamma[t][i]);
}
}
}
for (t = 0; t < T-1; t++)
{
tot = 0;
@ -560,7 +560,7 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
for (j = 0; j < N; j++)
xi[t][i][j] /= tot;
}
/*
// CHECK - fine
// gamma[t][i] = \sum_j{xi[t][i][j]}
@ -568,8 +568,8 @@ void forward_backwards(double*** xi, double** gamma, double* loglik, double* log
for (j = 0; j < N; j++)
tot += xi[3][1][j];
printf("gamma[3][1] = %f, sum_j(xi[3][1][j]) = %f\n", gamma[3][1], tot);
*/
*/
for (t = 0; t < T; t++)
{
free(alpha[t]);
@ -585,20 +585,20 @@ void viterbi_decode(double** x, int T, model_t* model, int* q)
int i, j, t;
double max;
int havemax;
int N = model->N;
int L = model->L;
double* p0 = model->p0;
double** a = model->a;
double** mu = model->mu;
double** cov = model->cov;
/* inverse covariance and its determinant */
double** icov = (double**) malloc(L*sizeof(double*));
for (i = 0; i < L; i++)
icov[i] = (double*) malloc(L*sizeof(double));
double detcov;
double** logb = (double**) malloc(T*sizeof(double*));
double** phi = (double**) malloc(T*sizeof(double*));
int** psi = (int**) malloc(T*sizeof(int*));
@ -608,24 +608,24 @@ void viterbi_decode(double** x, int T, model_t* model, int* q)
phi[t] = (double*) malloc(N*sizeof(double));
psi[t] = (int*) malloc(N*sizeof(int));
}
/* temporary memory */
double* gauss_y = (double*) malloc(L*sizeof(double));
double* gauss_z = (double*) malloc(L*sizeof(double));
double* gauss_z = (double*) malloc(L*sizeof(double));
/* calculate observation logprobs */
invert(cov, L, icov, &detcov);
for (t = 0; t < T; t++)
for (i = 0; i < N; i++)
logb[t][i] = loggauss(x[t], L, mu[i], icov, detcov, gauss_y, gauss_z);
/* initialise */
for (i = 0; i < N; i++)
{
phi[0][i] = log(p0[i]) + logb[0][i];
psi[0][i] = 0;
}
for (t = 1; t < T; t++)
{
for (j = 0; j < N; j++)
@ -646,7 +646,7 @@ void viterbi_decode(double** x, int T, model_t* model, int* q)
}
}
}
/* find maximising state at time T-1 */
max = phi[T-1][0];
q[T-1] = 0;
@ -659,7 +659,7 @@ void viterbi_decode(double** x, int T, model_t* model, int* q)
}
}
/* track back */
t = T - 2;
while (t >= 0)
@ -667,7 +667,7 @@ void viterbi_decode(double** x, int T, model_t* model, int* q)
q[t] = psi[t+1][q[t+1]];
t--;
}
/* de-allocate memory */
for (i = 0; i < L; i++)
free(icov[i]);
@ -681,7 +681,7 @@ void viterbi_decode(double** x, int T, model_t* model, int* q)
free(logb);
free(phi);
free(psi);
free(gauss_y);
free(gauss_z);
}
@ -693,13 +693,13 @@ void invert(double** cov, int L, double** icov, double* detcov)
double* a = (double*) malloc(L*L*sizeof(double));
int i, j;
for(j=0; j < L; j++)
for (i=0; i < L; i++)
for (i=0; i < L; i++)
a[j*L+i] = cov[i][j];
int M = (int) L;
int M = (int) L;
int* ipiv = (int *) malloc(L*L*sizeof(int));
int ret;
/* LU decomposition */
ret = dgetrf_(&M, &M, a, &M, ipiv, &ret); /* ret should be zero, negative if cov is singular */
if (ret < 0)
@ -707,7 +707,7 @@ void invert(double** cov, int L, double** icov, double* detcov)
fprintf(stderr, "Covariance matrix was singular, couldn't invert\n");
exit(-1);
}
/* find determinant */
double det = 1;
for(i = 0; i < L; i++)
@ -723,27 +723,27 @@ void invert(double** cov, int L, double** icov, double* detcov)
if (det < 0)
det = -det;
*detcov = det;
/* allocate required working storage */
#ifndef HAVE_ATLAS
int lwork = -1;
double lwbest = 0.0;
dgetri_(&M, a, &M, ipiv, &lwbest, &lwork, &ret);
lwork = (int) lwbest;
lwork = (int) lwbest;
double* work = (double*) malloc(lwork*sizeof(double));
#endif
/* find inverse */
dgetri_(&M, a, &M, ipiv, work, &lwork, &ret);
for(j=0; j < L; j++)
for (i=0; i < L; i++)
icov[i][j] = a[j*L+i];
#ifndef HAVE_ATLAS
for (i=0; i < L; i++)
icov[i][j] = a[j*L+i];
#ifndef HAVE_ATLAS
free(work);
#endif
free(a);
free(a);
}
/* probability of multivariate Gaussian given mean, inverse and determinant of covariance */
@ -762,8 +762,8 @@ double gauss(double* x, int L, double* mu, double** icov, double detcov, double*
}
s = cblas_ddot(L, z, 1, y, 1);
//for (i = 0; i < L; i++)
// s += z[i] * y[i];
// s += z[i] * y[i];
return exp(-s/2.0) / (pow(2*PI, L/2.0) * sqrt(detcov));
}
@ -784,10 +784,10 @@ double loggauss(double* x, int L, double* mu, double** icov, double detcov, doub
}
s = cblas_ddot(L, z, 1, y, 1);
//for (i = 0; i < L; i++)
// s += z[i] * y[i];
// s += z[i] * y[i];
ret = -0.5 * (s + L * log(2*PI) + log(detcov));
/*
// TEST
if (ISINF(ret) > 0)
@ -795,9 +795,9 @@ double loggauss(double* x, int L, double* mu, double** icov, double detcov, doub
if (ISINF(ret) < 0)
printf("loggauss returning -infinity\n");
if (ISNAN(ret))
printf("loggauss returning nan\n");
printf("loggauss returning nan\n");
*/
return ret;
}

View File

@ -19,9 +19,9 @@ extern "C" {
*
*/
#ifndef PI
#ifndef PI
#define PI 3.14159265358979323846264338327950288
#endif
#endif
typedef struct _model_t {
int N; /* number of states */
@ -33,7 +33,7 @@ typedef struct _model_t {
} model_t;
void hmm_train(double** x, int T, model_t* model); /* with scaling */
void forward_backwards(double*** xi, double** gamma, double* loglik, double* loglik1, double* loglik2, int iter,
void forward_backwards(double*** xi, double** gamma, double* loglik, double* loglik1, double* loglik2, int iter,
int N, int T, double* p0, double** a, double** b);
void baum_welch(double* p0, double** a, double** mu, double** cov, int N, int T, int L, double** x, double*** xi, double** gamma);
void viterbi_decode(double** x, int T, model_t* model, int* q); /* using logs */

View File

@ -4,7 +4,7 @@
/* Allow the use in C++ code. */
#ifdef __cplusplus
extern "C"
extern "C"
{
#endif
@ -77,39 +77,39 @@ CBLAS_INDEX cblas_izamax(const int N, const void *X, const int incX);
* ===========================================================================
*/
/*
/*
* Routines with standard 4 prefixes (s, d, c, z)
*/
void cblas_sswap(const int N, float *X, const int incX,
void cblas_sswap(const int N, float *X, const int incX,
float *Y, const int incY);
void cblas_scopy(const int N, const float *X, const int incX,
void cblas_scopy(const int N, const float *X, const int incX,
float *Y, const int incY);
void cblas_saxpy(const int N, const float alpha, const float *X,
const int incX, float *Y, const int incY);
void cblas_dswap(const int N, double *X, const int incX,
void cblas_dswap(const int N, double *X, const int incX,
double *Y, const int incY);
void cblas_dcopy(const int N, const double *X, const int incX,
void cblas_dcopy(const int N, const double *X, const int incX,
double *Y, const int incY);
void cblas_daxpy(const int N, const double alpha, const double *X,
const int incX, double *Y, const int incY);
void cblas_cswap(const int N, void *X, const int incX,
void cblas_cswap(const int N, void *X, const int incX,
void *Y, const int incY);
void cblas_ccopy(const int N, const void *X, const int incX,
void cblas_ccopy(const int N, const void *X, const int incX,
void *Y, const int incY);
void cblas_caxpy(const int N, const void *alpha, const void *X,
const int incX, void *Y, const int incY);
void cblas_zswap(const int N, void *X, const int incX,
void cblas_zswap(const int N, void *X, const int incX,
void *Y, const int incY);
void cblas_zcopy(const int N, const void *X, const int incX,
void cblas_zcopy(const int N, const void *X, const int incX,
void *Y, const int incY);
void cblas_zaxpy(const int N, const void *alpha, const void *X,
const int incX, void *Y, const int incY);
/*
/*
* Routines with S and D prefix only
*/
void cblas_srotg(float *a, float *b, float *c, float *s);
@ -127,7 +127,7 @@ void cblas_drotm(const int N, double *X, const int incX,
double *Y, const int incY, const double *P);
/*
/*
* Routines with S D C Z CS and ZD prefixes
*/
void cblas_sscal(const int N, const float alpha, float *X, const int incX);
@ -143,7 +143,7 @@ void cblas_zdscal(const int N, const double alpha, void *X, const int incX);
* ===========================================================================
*/
/*
/*
* Routines with standard 4 prefixes (S, D, C, Z)
*/
void cblas_sgemv(const enum CBLAS_ORDER order,
@ -158,11 +158,11 @@ void cblas_sgbmv(const enum CBLAS_ORDER order,
const int incX, const float beta, float *Y, const int incY);
void cblas_strmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
const int N, const float *A, const int lda,
const int N, const float *A, const int lda,
float *X, const int incX);
void cblas_stbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
const int N, const int K, const float *A, const int lda,
const int N, const int K, const float *A, const int lda,
float *X, const int incX);
void cblas_stpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
@ -191,11 +191,11 @@ void cblas_dgbmv(const enum CBLAS_ORDER order,
const int incX, const double beta, double *Y, const int incY);
void cblas_dtrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
const int N, const double *A, const int lda,
const int N, const double *A, const int lda,
double *X, const int incX);
void cblas_dtbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
const int N, const int K, const double *A, const int lda,
const int N, const int K, const double *A, const int lda,
double *X, const int incX);
void cblas_dtpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
@ -224,11 +224,11 @@ void cblas_cgbmv(const enum CBLAS_ORDER order,
const int incX, const void *beta, void *Y, const int incY);
void cblas_ctrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
const int N, const void *A, const int lda,
const int N, const void *A, const int lda,
void *X, const int incX);
void cblas_ctbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
const int N, const int K, const void *A, const int lda,
const int N, const int K, const void *A, const int lda,
void *X, const int incX);
void cblas_ctpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
@ -257,11 +257,11 @@ void cblas_zgbmv(const enum CBLAS_ORDER order,
const int incX, const void *beta, void *Y, const int incY);
void cblas_ztrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
const int N, const void *A, const int lda,
const int N, const void *A, const int lda,
void *X, const int incX);
void cblas_ztbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
const int N, const int K, const void *A, const int lda,
const int N, const int K, const void *A, const int lda,
void *X, const int incX);
void cblas_ztpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag,
@ -279,7 +279,7 @@ void cblas_ztpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const int N, const void *Ap, void *X, const int incX);
/*
/*
* Routines with S and D prefixes only
*/
void cblas_ssymv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
@ -341,7 +341,7 @@ void cblas_dspr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
const int incX, const double *Y, const int incY, double *A);
/*
/*
* Routines with C and Z prefixes only
*/
void cblas_chemv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
@ -412,7 +412,7 @@ void cblas_zhpr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const
* ===========================================================================
*/
/*
/*
* Routines with standard 4 prefixes (S, D, C, Z)
*/
void cblas_sgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
@ -536,7 +536,7 @@ void cblas_ztrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
void *B, const int ldb);
/*
/*
* Routines with prefixes C and Z only
*/
void cblas_chemm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
@ -573,6 +573,6 @@ void cblas_xerbla(int p, const char *rout, const char *form, ...);
#ifdef __cplusplus
}
#endif
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ void Correlation::doAutoUnBiased(double *src, double *dst, unsigned int length)
{
for( j = i; j < length; j++)
{
tmp += src[ j-i ] * src[ j ];
tmp += src[ j-i ] * src[ j ];
}

View File

@ -18,7 +18,7 @@
#define EPS 2.2204e-016
class Correlation
class Correlation
{
public:
void doAutoUnBiased( double* src, double* dst, unsigned int length );
@ -27,4 +27,4 @@ public:
};
#endif //
#endif //

View File

@ -34,7 +34,7 @@ double CosineDistance::distance(const vector<double> &v1,
}
else
{
for(unsigned int i=0; i<v1.size(); i++)
for(int i=0; i<v1.size(); i++)
{
dSum1 += v1[i]*v2[i];
dDen1 += v1[i]*v1[i];

View File

@ -50,7 +50,7 @@ double KLDivergence::distanceDistribution(const vector<double> &d1,
double d = 0;
double small = 1e-20;
for (int i = 0; i < sz; ++i) {
d += d1[i] * log10((d1[i] + small) / (d2[i] + small));
}

View File

@ -24,7 +24,7 @@ typedef complex<double> ComplexData;
#ifndef PI
#define PI (3.14159265358979323846)
#define PI (3.14159265358979232846)
#endif
#define TWO_PI (2. * PI)

View File

@ -16,6 +16,8 @@
#include "MathUtilities.h"
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
@ -41,11 +43,11 @@ void MathUtilities::getAlphaNorm(const double *data, unsigned int len, unsigned
unsigned int i;
double temp = 0.0;
double a=0.0;
for( i = 0; i < len; i++)
{
temp = data[ i ];
a += ::pow( fabs(temp), double(alpha) );
}
a /= ( double )len;
@ -60,11 +62,11 @@ double MathUtilities::getAlphaNorm( const std::vector <double> &data, unsigned i
unsigned int len = data.size();
double temp = 0.0;
double a=0.0;
for( i = 0; i < len; i++)
{
temp = data[ i ];
a += ::pow( fabs(temp), double(alpha) );
}
a /= ( double )len;
@ -75,54 +77,27 @@ double MathUtilities::getAlphaNorm( const std::vector <double> &data, unsigned i
double MathUtilities::round(double x)
{
double val = (double)floor(x + 0.5);
return val;
if (x < 0) {
return -floor(-x + 0.5);
} else {
return floor(x + 0.5);
}
}
double MathUtilities::median(const double *src, unsigned int len)
{
unsigned int i, j;
double tmp = 0.0;
double tempMedian;
double medianVal;
if (len == 0) return 0;
std::vector<double> scratch;
for (int i = 0; i < len; ++i) scratch.push_back(src[i]);
std::sort(scratch.begin(), scratch.end());
double* scratch = new double[ len ];//Vector < double > sortedX = Vector < double > ( size );
for ( i = 0; i < len; i++ )
{
scratch[i] = src[i];
int middle = len/2;
if (len % 2 == 0) {
return (scratch[middle] + scratch[middle - 1]) / 2;
} else {
return scratch[middle];
}
for ( i = 0; i < len - 1; i++ )
{
for ( j = 0; j < len - 1 - i; j++ )
{
if ( scratch[j + 1] < scratch[j] )
{
// compare the two neighbors
tmp = scratch[j]; // swap a[j] and a[j+1]
scratch[j] = scratch[j + 1];
scratch[j + 1] = tmp;
}
}
}
int middle;
if ( len % 2 == 0 )
{
middle = len / 2;
tempMedian = ( scratch[middle] + scratch[middle - 1] ) / 2;
}
else
{
middle = ( int )floor( len / 2.0 );
tempMedian = scratch[middle];
}
medianVal = tempMedian;
delete [] scratch;
return medianVal;
}
double MathUtilities::sum(const double *src, unsigned int len)
@ -142,8 +117,10 @@ double MathUtilities::mean(const double *src, unsigned int len)
{
double retVal =0.0;
double s = sum( src, len );
if (len == 0) return 0;
double s = sum( src, len );
retVal = s / (double)len;
return retVal;
@ -154,8 +131,10 @@ double MathUtilities::mean(const std::vector<double> &src,
unsigned int count)
{
double sum = 0.;
for (unsigned int i = 0; i < count; ++i)
if (count == 0) return 0;
for (int i = 0; i < (int)count; ++i)
{
sum += src[start + i];
}
@ -172,7 +151,7 @@ void MathUtilities::getFrameMinMax(const double *data, unsigned int len, double
*min = *max = 0;
return;
}
*min = data[0];
*max = data[0];
@ -188,7 +167,7 @@ void MathUtilities::getFrameMinMax(const double *data, unsigned int len, double
{
*max = temp ;
}
}
}
@ -197,7 +176,7 @@ int MathUtilities::getMax( double* pData, unsigned int Length, double* pMax )
unsigned int index = 0;
unsigned int i;
double temp = 0.0;
double max = pData[0];
for( i = 0; i < Length; i++)
@ -209,7 +188,7 @@ int MathUtilities::getMax( double* pData, unsigned int Length, double* pMax )
max = temp ;
index = i;
}
}
if (pMax) *pMax = max;
@ -223,7 +202,7 @@ int MathUtilities::getMax( const std::vector<double> & data, double* pMax )
unsigned int index = 0;
unsigned int i;
double temp = 0.0;
double max = data[0];
for( i = 0; i < data.size(); i++)
@ -235,7 +214,7 @@ int MathUtilities::getMax( const std::vector<double> & data, double* pMax )
max = temp ;
index = i;
}
}
if (pMax) *pMax = max;
@ -265,7 +244,7 @@ void MathUtilities::circShift( double* pData, int length, int shift)
int MathUtilities::compareInt (const void * a, const void * b)
{
return ( *(const int*)a - *(const int*)b );
return ( *(int*)a - *(int*)b );
}
void MathUtilities::normalise(double *data, int length, NormaliseType type)
@ -316,9 +295,9 @@ void MathUtilities::normalise(std::vector<double> &data, NormaliseType type)
case NormaliseUnitSum:
{
double sum = 0.0;
for (unsigned int i = 0; i < data.size(); ++i) sum += data[i];
for (int i = 0; i < (int)data.size(); ++i) sum += data[i];
if (sum != 0.0) {
for (unsigned int i = 0; i < data.size(); ++i) data[i] /= sum;
for (int i = 0; i < (int)data.size(); ++i) data[i] /= sum;
}
}
break;
@ -326,11 +305,11 @@ void MathUtilities::normalise(std::vector<double> &data, NormaliseType type)
case NormaliseUnitMax:
{
double max = 0.0;
for (unsigned int i = 0; i < data.size(); ++i) {
for (int i = 0; i < (int)data.size(); ++i) {
if (fabs(data[i]) > max) max = fabs(data[i]);
}
if (max != 0.0) {
for (unsigned int i = 0; i < data.size(); ++i) data[i] /= max;
for (int i = 0; i < (int)data.size(); ++i) data[i] /= max;
}
}
break;
@ -344,7 +323,7 @@ void MathUtilities::adaptiveThreshold(std::vector<double> &data)
if (sz == 0) return;
std::vector<double> smoothed(sz);
int p_pre = 8;
int p_post = 7;
@ -365,7 +344,7 @@ void MathUtilities::adaptiveThreshold(std::vector<double> &data)
bool
MathUtilities::isPowerOfTwo(int x)
{
if (x < 2) return false;
if (x < 1) return false;
if (x & (x-1)) return false;
return true;
}
@ -374,6 +353,7 @@ int
MathUtilities::nextPowerOfTwo(int x)
{
if (isPowerOfTwo(x)) return x;
if (x < 1) return 1;
int n = 1;
while (x) { x >>= 1; n <<= 1; }
return n;
@ -383,6 +363,7 @@ int
MathUtilities::previousPowerOfTwo(int x)
{
if (isPowerOfTwo(x)) return x;
if (x < 1) return 1;
int n = 1;
x >>= 1;
while (x) { x >>= 1; n <<= 1; }
@ -393,8 +374,30 @@ int
MathUtilities::nearestPowerOfTwo(int x)
{
if (isPowerOfTwo(x)) return x;
int n0 = previousPowerOfTwo(x), n1 = nearestPowerOfTwo(x);
int n0 = previousPowerOfTwo(x), n1 = nextPowerOfTwo(x);
if (x - n0 < n1 - x) return n0;
else return n1;
}
double
MathUtilities::factorial(int x)
{
if (x < 0) return 0;
double f = 1;
for (int i = 1; i <= x; ++i) {
f = f * i;
}
return f;
}
int
MathUtilities::gcd(int a, int b)
{
int c = a % b;
if (c == 0) {
return b;
} else {
return gcd(b, c);
}
}

View File

@ -20,20 +20,57 @@
#include "nan-inf.h"
class MathUtilities
/**
* Static helper functions for simple mathematical calculations.
*/
class MathUtilities
{
public:
public:
/**
* Round x to the nearest integer.
*/
static double round( double x );
/**
* Return through min and max pointers the highest and lowest
* values in the given array of the given length.
*/
static void getFrameMinMax( const double* data, unsigned int len, double* min, double* max );
/**
* Return the mean of the given array of the given length.
*/
static double mean( const double* src, unsigned int len );
/**
* Return the mean of the subset of the given vector identified by
* start and count.
*/
static double mean( const std::vector<double> &data,
unsigned int start, unsigned int count );
/**
* Return the sum of the values in the given array of the given
* length.
*/
static double sum( const double* src, unsigned int len );
/**
* Return the median of the values in the given array of the given
* length. If the array is even in length, the returned value will
* be half-way between the two values adjacent to median.
*/
static double median( const double* src, unsigned int len );
/**
* The principle argument function. Map the phase angle ang into
* the range [-pi,pi).
*/
static double princarg( double ang );
/**
* Floating-point division modulus: return x % y.
*/
static double mod( double x, double y);
static void getAlphaNorm(const double *data, unsigned int len, unsigned int alpha, double* ANorm);
@ -51,19 +88,50 @@ public:
NormaliseUnitMax
};
static void normalise(double *data, int length,
NormaliseType n = NormaliseUnitMax);
static void normalise(double *data, int length,
NormaliseType n = NormaliseUnitMax);
static void normalise(std::vector<double> &data,
NormaliseType n = NormaliseUnitMax);
static void normalise(std::vector<double> &data,
NormaliseType n = NormaliseUnitMax);
// moving mean threshholding:
/**
* Threshold the input/output vector data against a moving-mean
* average filter.
*/
static void adaptiveThreshold(std::vector<double> &data);
/**
* Return true if x is 2^n for some integer n >= 0.
*/
static bool isPowerOfTwo(int x);
static int nextPowerOfTwo(int x); // e.g. 1300 -> 2048, 2048 -> 2048
static int previousPowerOfTwo(int x); // e.g. 1300 -> 1024, 2048 -> 2048
static int nearestPowerOfTwo(int x); // e.g. 1300 -> 1024, 1700 -> 2048
/**
* Return the next higher integer power of two from x, e.g. 1300
* -> 2048, 2048 -> 2048.
*/
static int nextPowerOfTwo(int x);
/**
* Return the next lower integer power of two from x, e.g. 1300 ->
* 1024, 2048 -> 2048.
*/
static int previousPowerOfTwo(int x);
/**
* Return the nearest integer power of two to x, e.g. 1300 -> 1024,
* 12 -> 16 (not 8; if two are equidistant, the higher is returned).
*/
static int nearestPowerOfTwo(int x);
/**
* Return x!
*/
static double factorial(int x); // returns double in case it is large
/**
* Return the greatest common divisor of natural numbers a and b.
*/
static int gcd(int a, int b);
};
#endif

View File

@ -0,0 +1,131 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
QM DSP Library
Centre for Digital Music, Queen Mary, University of London.
This file Copyright 2010 Chris Cannam.
This program 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 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef MEDIAN_FILTER_H
#define MEDIAN_FILTER_H
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <vector>
template <typename T>
class MedianFilter
{
public:
MedianFilter(int size, float percentile = 50.f) :
m_size(size),
m_frame(new T[size]),
m_sorted(new T[size]),
m_sortend(m_sorted + size - 1) {
setPercentile(percentile);
reset();
}
~MedianFilter() {
delete[] m_frame;
delete[] m_sorted;
}
void setPercentile(float p) {
m_index = int((m_size * p) / 100.f);
if (m_index >= m_size) m_index = m_size-1;
if (m_index < 0) m_index = 0;
}
void push(T value) {
if (value != value) {
std::cerr << "WARNING: MedianFilter::push: attempt to push NaN, pushing zero instead" << std::endl;
// we do need to push something, to maintain the filter length
value = T();
}
drop(m_frame[0]);
const int sz1 = m_size-1;
for (int i = 0; i < sz1; ++i) m_frame[i] = m_frame[i+1];
m_frame[m_size-1] = value;
put(value);
}
T get() const {
return m_sorted[m_index];
}
int getSize() const {
return m_size;
}
T getAt(float percentile) {
int ix = int((m_size * percentile) / 100.f);
if (ix >= m_size) ix = m_size-1;
if (ix < 0) ix = 0;
return m_sorted[ix];
}
void reset() {
for (int i = 0; i < m_size; ++i) m_frame[i] = 0;
for (int i = 0; i < m_size; ++i) m_sorted[i] = 0;
}
static std::vector<T> filter(int size, const std::vector<T> &in) {
std::vector<T> out;
MedianFilter<T> f(size);
for (int i = 0; i < int(in.size()); ++i) {
f.push(in[i]);
T median = f.get();
if (i >= size/2) out.push_back(median);
}
while (out.size() < in.size()) {
f.push(T());
out.push_back(f.get());
}
return out;
}
private:
const int m_size;
T *const m_frame;
T *const m_sorted;
T *const m_sortend;
int m_index;
void put(T value) {
// precondition: m_sorted contains m_size-1 values, packed at start
// postcondition: m_sorted contains m_size values, one of which is value
T *point = std::lower_bound(m_sorted, m_sortend, value);
const int n = m_sortend - point;
for (int i = n; i > 0; --i) point[i] = point[i-1];
*point = value;
}
void drop(T value) {
// precondition: m_sorted contains m_size values, one of which is value
// postcondition: m_sorted contains m_size-1 values, packed at start
T *point = std::lower_bound(m_sorted, m_sortend + 1, value);
if (*point != value) {
std::cerr << "WARNING: MedianFilter::drop: *point is " << *point
<< ", expected " << value << std::endl;
}
const int n = m_sortend - point;
for (int i = 0; i < n; ++i) point[i] = point[i+1];
*m_sortend = T(0);
}
MedianFilter(const MedianFilter &); // not provided
MedianFilter &operator=(const MedianFilter &); // not provided
};
#endif

View File

@ -53,13 +53,13 @@ public:
const vector<double> &y,
vector<double> &coef);
private:
TPolyFit &operator = (const TPolyFit &); // disable assignment
TPolyFit(); // and instantiation
TPolyFit(const TPolyFit&); // and copying
static void Square (const Matrix &x, // Matrix multiplication routine
const vector<double> &y,
Matrix &a, // A = transpose X times X
@ -105,13 +105,13 @@ double TPolyFit::PolyFit2 (const vector<double> &x,
// nterms = coefs.size()
// npoints = x.size()
{
unsigned int i, j;
int i, j;
double xi, yi, yc, srs, sum_y, sum_y2;
Matrix xmatr; // Data matrix
Matrix a;
vector<double> g; // Constant vector
const unsigned int npoints(x.size());
const unsigned int nterms(coefs.size());
const int npoints(x.size());
const int nterms(coefs.size());
double correl_coef;
zeroise(g, nterms);
zeroise(a, nterms, nterms);
@ -124,7 +124,7 @@ double TPolyFit::PolyFit2 (const vector<double> &x,
std::cerr << "ERROR: PolyFit called with less than two points" << std::endl;
return 0;
}
if(npoints != y.size()) {
if(npoints != (int)y.size()) {
std::cerr << "ERROR: PolyFit called with x and y of unequal size" << std::endl;
return 0;
}
@ -260,8 +260,8 @@ bool TPolyFit::GaussJordan (Matrix &b,
for( int i = 0; i < ncol; ++i)
coef[i] = w[i][0];
return true;
} // end; { procedure GaussJordan }
//----------------------------------------------------------------------------------------------
@ -274,12 +274,11 @@ bool TPolyFit::GaussJordan2(Matrix &b,
{
//GaussJordan2; // first half of GaussJordan
// actual start of gaussj
double big, t;
double pivot;
double determ;
int irow = 0;
int icol = 0;
int irow, icol;
int ncol(b.size());
int nv = 1; // single constant vector
for(int i = 0; i < ncol; ++i)
@ -405,4 +404,4 @@ void NSUtility::zeroise(vector<vector<int> > &matrix, int m, int n)
#endif

View File

@ -2,19 +2,12 @@
#ifndef NAN_INF_H
#define NAN_INF_H
#include <math.h>
#ifdef sun
#include <ieeefp.h>
#define ISNAN(x) ((sizeof(x)==sizeof(float))?isnanf(x):isnand(x))
#define ISINF(x) (!finite(x))
#else
#define ISNAN(x) isnan(x)
#define ISINF(x) isinf(x)
#endif
#define ISNAN(x) (sizeof(x) == sizeof(double) ? ISNANd(x) : ISNANf(x))
static inline int ISNANf(float x) { return x != x; }
static inline int ISNANd(double x) { return x != x; }
#define ISINF(x) (sizeof(x) == sizeof(double) ? ISINFd(x) : ISINFf(x))
static inline int ISINFf(float x) { return !ISNANf(x) && ISNANf(x - x); }
static inline int ISINFd(double x) { return !ISNANd(x) && ISNANd(x - x); }
#endif

View File

@ -15,8 +15,8 @@
Earn/Bitnet: fionn@dgaeso51, fim@dgaipp1s, murtagh@stsci
Span: esomc1::fionn
Internet: murtagh@scivax.stsci.edu
F. Murtagh, Munich, 6 June 1989 */
F. Murtagh, Munich, 6 June 1989 */
/*********************************************************************/
#include <stdio.h>
@ -110,7 +110,7 @@ void tred2(double** a, int n, double* d, double* e)
{
int l, k, j, i;
double scale, hh, h, g, f;
for (i = n-1; i >= 1; i--)
{
l = i - 1;
@ -188,7 +188,7 @@ void tqli(double* d, double* e, int n, double** z)
{
int m, l, iter, i, k;
double s, r, p, g, f, dd, c, b;
for (i = 1; i < n; i++)
e[i-1] = e[i];
e[n-1] = 0.0;
@ -253,23 +253,23 @@ void pca_project(double** data, int n, int m, int ncomponents)
{
int i, j, k, k2;
double **symmat, **symmat2, *evals, *interm;
//TODO: assert ncomponents < m
symmat = (double**) malloc(m*sizeof(double*));
for (i = 0; i < m; i++)
symmat[i] = (double*) malloc(m*sizeof(double));
covcol(data, n, m, symmat);
/*********************************************************************
Eigen-reduction
**********************************************************************/
/* Allocate storage for dummy and new vectors. */
evals = (double*) malloc(m*sizeof(double)); /* Storage alloc. for vector of eigenvalues */
interm = (double*) malloc(m*sizeof(double)); /* Storage alloc. for 'intermediate' vector */
//MALLOC_ARRAY(symmat2,m,m,double);
//MALLOC_ARRAY(symmat2,m,m,double);
//for (i = 0; i < m; i++) {
// for (j = 0; j < m; j++) {
// symmat2[i][j] = symmat[i][j]; /* Needed below for col. projections */
@ -278,7 +278,7 @@ void pca_project(double** data, int n, int m, int ncomponents)
tred2(symmat, m, evals, interm); /* Triangular decomposition */
tqli(evals, interm, m, symmat); /* Reduction of sym. trid. matrix */
/* evals now contains the eigenvalues,
columns of symmat now contain the associated eigenvectors. */
columns of symmat now contain the associated eigenvectors. */
/*
printf("\nEigenvalues:\n");
@ -289,7 +289,7 @@ columns of symmat now contain the associated eigenvectors. */
printf("Eigenvalues are often expressed as cumulative\n");
printf("percentages, representing the 'percentage variance\n");
printf("explained' by the associated axis or principal component.)\n");
printf("\nEigenvectors:\n");
printf("(First three; their definition in terms of original vbes.)\n");
for (j = 0; j < m; j++) {
@ -310,7 +310,7 @@ for (i = 0; i < n; i++) {
}
}
/*
/*
printf("\nProjections of row-points on first 3 prin. comps.:\n");
for (i = 0; i < n; i++) {
for (j = 0; j < 3; j++) {

View File

@ -1,114 +0,0 @@
TEMPLATE = lib
CONFIG += staticlib warn_on release
CONFIG -= qt
OBJECTS_DIR = tmp_obj
MOC_DIR = tmp_moc
linux-g++* {
QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O3 -fno-exceptions -fPIC -ffast-math -msse -mfpmath=sse -ftree-vectorize -fomit-frame-pointer
DEFINES += USE_PTHREADS
INCLUDEPATH += ../vamp-plugin-sdk ../qm-dsp
LIBPATH += ../vamp-plugin-sdk/vamp-sdk ../qm-dsp
}
linux-g++-64 {
QMAKE_CXXFLAGS_RELEASE += -msse2
INCLUDEPATH += ../qm-vamp-plugins/build/linux/amd64
}
win32-x-g++ {
QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O2 -march=pentium3 -msse
INCLUDEPATH += . include ../include
}
macx-g++* {
QMAKE_MAC_SDK=/Developer/SDKs/MacOSX10.4u.sdk
CONFIG += x86 ppc
QMAKE_CXXFLAGS_RELEASE += -O2 -g0 -fvisibility=hidden -I/Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/Headers/
INCLUDEPATH += /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/Headers/
DEFINES += USE_PTHREADS
QMAKE_CXXFLAGS_RELEASE += -fvisibility=hidden
}
solaris* {
QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -fast
INCLUDEPATH += /opt/ATLAS3.9.14/include
DEFINES += USE_PTHREADS
}
INCLUDEPATH += .
# Input
HEADERS += base/Pitch.h \
base/Window.h \
dsp/chromagram/Chromagram.h \
dsp/chromagram/ConstantQ.h \
dsp/keydetection/GetKeyMode.h \
dsp/mfcc/MFCC.h \
dsp/onsets/DetectionFunction.h \
dsp/onsets/PeakPicking.h \
dsp/phasevocoder/PhaseVocoder.h \
dsp/rateconversion/Decimator.h \
dsp/rhythm/BeatSpectrum.h \
dsp/segmentation/cluster_melt.h \
dsp/segmentation/ClusterMeltSegmenter.h \
dsp/segmentation/cluster_segmenter.h \
dsp/segmentation/Segmenter.h \
dsp/segmentation/segment.h \
dsp/signalconditioning/DFProcess.h \
dsp/signalconditioning/Filter.h \
dsp/signalconditioning/FiltFilt.h \
dsp/signalconditioning/Framer.h \
dsp/tempotracking/DownBeat.h \
dsp/tempotracking/TempoTrack.h \
dsp/tempotracking/TempoTrackV2.h \
dsp/tonal/ChangeDetectionFunction.h \
dsp/tonal/TCSgram.h \
dsp/tonal/TonalEstimator.h \
dsp/transforms/FFT.h \
dsp/wavelet/Wavelet.h \
hmm/hmm.h \
maths/Correlation.h \
maths/CosineDistance.h \
maths/KLDivergence.h \
maths/MathAliases.h \
maths/MathUtilities.h \
maths/Polyfit.h \
maths/pca/pca.h \
thread/AsynchronousTask.h \
thread/BlockAllocator.h \
thread/Thread.h
SOURCES += base/Pitch.cpp \
dsp/chromagram/Chromagram.cpp \
dsp/chromagram/ConstantQ.cpp \
dsp/keydetection/GetKeyMode.cpp \
dsp/mfcc/MFCC.cpp \
dsp/onsets/DetectionFunction.cpp \
dsp/onsets/PeakPicking.cpp \
dsp/phasevocoder/PhaseVocoder.cpp \
dsp/rateconversion/Decimator.cpp \
dsp/rhythm/BeatSpectrum.cpp \
dsp/segmentation/cluster_melt.c \
dsp/segmentation/ClusterMeltSegmenter.cpp \
dsp/segmentation/cluster_segmenter.c \
dsp/segmentation/Segmenter.cpp \
dsp/signalconditioning/DFProcess.cpp \
dsp/signalconditioning/Filter.cpp \
dsp/signalconditioning/FiltFilt.cpp \
dsp/signalconditioning/Framer.cpp \
dsp/tempotracking/DownBeat.cpp \
dsp/tempotracking/TempoTrack.cpp \
dsp/tempotracking/TempoTrackV2.cpp \
dsp/tonal/ChangeDetectionFunction.cpp \
dsp/tonal/TCSgram.cpp \
dsp/tonal/TonalEstimator.cpp \
dsp/transforms/FFT.cpp \
dsp/wavelet/Wavelet.cpp \
hmm/hmm.c \
maths/Correlation.cpp \
maths/CosineDistance.cpp \
maths/KLDivergence.cpp \
maths/MathUtilities.cpp \
maths/pca/pca.c \
thread/Thread.cpp

View File

@ -77,7 +77,7 @@ protected:
}
virtual void performTask() = 0;
private:
virtual void run() {
m_todo.lock();

View File

@ -58,9 +58,9 @@ Thread::start()
#endif
m_extant = true;
}
}
}
void
void
Thread::wait()
{
if (m_extant) {
@ -233,7 +233,7 @@ Condition::unlock()
ReleaseMutex(m_mutex);
}
void
void
Condition::wait(int us)
{
if (us == 0) {
@ -248,7 +248,7 @@ Condition::wait(int us)
DWORD ms = us / 1000;
if (us > 0 && ms == 0) ms = 1;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
@ -310,9 +310,9 @@ Thread::start()
#endif
m_extant = true;
}
}
}
void
void
Thread::wait()
{
if (m_extant) {
@ -490,7 +490,7 @@ Condition::unlock()
pthread_mutex_unlock(&m_mutex);
}
void
void
Condition::wait(int us)
{
if (us == 0) {
@ -514,7 +514,7 @@ Condition::wait(int us)
struct timespec timeout;
timeout.tv_sec = now.tv_sec;
timeout.tv_nsec = now.tv_usec * 1000;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
@ -551,9 +551,9 @@ void
Thread::start()
{
abort();
}
}
void
void
Thread::wait()
{
abort();
@ -611,7 +611,7 @@ Condition::lock()
abort();
}
void
void
Condition::wait(int us)
{
abort();

View File

@ -13,8 +13,11 @@
#ifdef _WIN32
#include <windows.h>
#else /* !_WIN32 */
#define USE_PTHREADS
#ifdef USE_PTHREADS
#include <pthread.h>
#else
#error Must have either _WIN32 or USE_PTHREADS defined
#endif /* USE_PTHREADS */
#endif /* !_WIN32 */
@ -127,7 +130,7 @@ public:
void wait(int us = 0);
void signal();
private:
#ifdef _WIN32

View File

@ -35,32 +35,59 @@ def build(bld):
return
# Host Library
obj = bld(features = 'cxx cxxshlib')
obj = bld(features = 'c cxx cxxshlib')
obj.source = '''
base/Pitch.cpp
base/KaiserWindow.cpp
base/SincWindow.cpp
dsp/chromagram/Chromagram.cpp
dsp/chromagram/ConstantQ.cpp
dsp/keydetection/GetKeyMode.cpp
dsp/mfcc/MFCC.cpp
dsp/onsets/DetectionFunction.cpp
dsp/onsets/PeakPicking.cpp
dsp/phasevocoder/PhaseVocoder.cpp
dsp/rateconversion/Decimator.cpp
dsp/rateconversion/DecimatorB.cpp
dsp/rateconversion/Resampler.cpp
dsp/rhythm/BeatSpectrum.cpp
dsp/segmentation/cluster_melt.c
dsp/segmentation/ClusterMeltSegmenter.cpp
dsp/segmentation/cluster_segmenter.c
dsp/segmentation/Segmenter.cpp
dsp/signalconditioning/DFProcess.cpp
dsp/signalconditioning/Filter.cpp
dsp/signalconditioning/FiltFilt.cpp
dsp/signalconditioning/Framer.cpp
dsp/tempotracking/DownBeat.cpp
dsp/tempotracking/TempoTrack.cpp
dsp/tempotracking/TempoTrackV2.cpp
dsp/tonal/ChangeDetectionFunction.cpp
dsp/tonal/TCSgram.cpp
dsp/tonal/TonalEstimator.cpp
dsp/transforms/FFT.cpp
dsp/wavelet/Wavelet.cpp
hmm/hmm.c
maths/Correlation.cpp
maths/CosineDistance.cpp
maths/KLDivergence.cpp
maths/MathUtilities.cpp
base/Pitch.cpp
maths/pca/pca.c
thread/Thread.cpp
ext/kissfft/kiss_fft.c
ext/kissfft/tools/kiss_fftr.c
'''
autowaf.ensure_visible_symbols (obj, True)
obj.export_includes = ['.']
obj.includes = ['.']
obj.includes = ['.', 'include/', 'ext/kissfft', 'ext/kissfft/tools/']
obj.defines = ['kiss_fft_scalar=double']
obj.name = 'libqm-dsp'
obj.target = 'qm-dsp'
obj.vnum = QM_DSP_VERSION
obj.install_path = bld.env['LIBDIR']
if bld.env['build_target'] != 'mingw':
obj.cxxflags = [ '-fPIC' ]
obj.cflags = [ '-fPIC' ]
def shutdown():
autowaf.shutdown()