add queen mary DSP library
git-svn-id: svn://localhost/ardour2/branches/3.0@9029 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
fa41cfef58
commit
3deba1921b
|
@ -0,0 +1,280 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
|
||||
QM-DSP library
|
||||
==============
|
||||
|
||||
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,
|
||||
with the following exceptions:
|
||||
|
||||
maths/pca.c -- Fionn Murtagh, from StatLib; with permission
|
||||
|
||||
maths/Polyfit.h -- Allen Miller, David J Taylor and others; also for
|
||||
Delphi in the the JEDI Math Library, under the Mozilla Public License
|
||||
|
||||
thread/BlockAllocator.h -- derived from FSB Allocator by Juha Nieminen,
|
||||
under BSD-style license
|
||||
|
||||
See individual files for further authorship details.
|
||||
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
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.
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- 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 2006 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 "Pitch.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
float
|
||||
Pitch::getFrequencyForPitch(int midiPitch,
|
||||
float centsOffset,
|
||||
float concertA)
|
||||
{
|
||||
float p = float(midiPitch) + (centsOffset / 100);
|
||||
return concertA * powf(2.0, (p - 69.0) / 12.0);
|
||||
}
|
||||
|
||||
int
|
||||
Pitch::getPitchForFrequency(float frequency,
|
||||
float *centsOffsetReturn,
|
||||
float concertA)
|
||||
{
|
||||
float p = 12.0 * (log(frequency / (concertA / 2.0)) / log(2.0)) + 57.0;
|
||||
|
||||
int midiPitch = int(p + 0.00001);
|
||||
float centsOffset = (p - midiPitch) * 100.0;
|
||||
|
||||
if (centsOffset >= 50.0) {
|
||||
midiPitch = midiPitch + 1;
|
||||
centsOffset = -(100.0 - centsOffset);
|
||||
}
|
||||
|
||||
if (centsOffsetReturn) *centsOffsetReturn = centsOffset;
|
||||
return midiPitch;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- 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 2006 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 _PITCH_H_
|
||||
#define _PITCH_H_
|
||||
|
||||
class Pitch
|
||||
{
|
||||
public:
|
||||
static float getFrequencyForPitch(int midiPitch,
|
||||
float centsOffset = 0,
|
||||
float concertA = 440.0);
|
||||
|
||||
static int getPitchForFrequency(float frequency,
|
||||
float *centsOffsetReturn = 0,
|
||||
float concertA = 440.0);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,125 @@
|
|||
/* -*- 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 2006 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 _WINDOW_H_
|
||||
#define _WINDOW_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
enum WindowType {
|
||||
RectangularWindow,
|
||||
BartlettWindow,
|
||||
HammingWindow,
|
||||
HanningWindow,
|
||||
BlackmanWindow,
|
||||
GaussianWindow,
|
||||
ParzenWindow
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Window
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct a windower of the given type.
|
||||
*/
|
||||
Window(WindowType type, size_t 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;
|
||||
m_type = w.m_type;
|
||||
m_size = w.m_size;
|
||||
encache();
|
||||
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];
|
||||
}
|
||||
|
||||
WindowType getType() const { return m_type; }
|
||||
size_t getSize() const { return m_size; }
|
||||
|
||||
protected:
|
||||
WindowType m_type;
|
||||
size_t m_size;
|
||||
T *m_cache;
|
||||
|
||||
void encache();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void Window<T>::encache()
|
||||
{
|
||||
size_t n = m_size;
|
||||
T *mult = new T[n];
|
||||
size_t 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;
|
||||
}
|
||||
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)));
|
||||
}
|
||||
break;
|
||||
|
||||
case HammingWindow:
|
||||
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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
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)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
m_cache = mult;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,101 @@
|
|||
#
|
||||
# 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)
|
|
@ -0,0 +1,146 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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
|
|
@ -0,0 +1,106 @@
|
|||
|
||||
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
|
|
@ -0,0 +1,91 @@
|
|||
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
|
@ -0,0 +1,181 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include "maths/MathUtilities.h"
|
||||
#include "Chromagram.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Chromagram::Chromagram( ChromaConfig Config ) :
|
||||
m_skGenerated(false)
|
||||
{
|
||||
initialise( 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));
|
||||
|
||||
// Create array for chroma result
|
||||
m_chromadata = new double[ m_BPO ];
|
||||
|
||||
// Create Config Structure for ConstantQ operator
|
||||
CQConfig ConstantQConfig;
|
||||
|
||||
// Populate CQ config structure with parameters
|
||||
// inherited from the Chroma config
|
||||
ConstantQConfig.FS = Config.FS;
|
||||
ConstantQConfig.min = m_FMin;
|
||||
ConstantQConfig.max = m_FMax;
|
||||
ConstantQConfig.BPO = m_BPO;
|
||||
ConstantQConfig.CQThresh = Config.CQThresh;
|
||||
|
||||
// Initialise ConstantQ operator
|
||||
m_ConstantQ = new ConstantQ( ConstantQConfig );
|
||||
|
||||
// Initialise working arrays
|
||||
m_frameSize = m_ConstantQ->getfftlength();
|
||||
m_hopSize = m_ConstantQ->gethop();
|
||||
|
||||
// Initialise FFT object
|
||||
m_FFT = new FFTReal(m_frameSize);
|
||||
|
||||
m_FFTRe = new double[ m_frameSize ];
|
||||
m_FFTIm = new double[ m_frameSize ];
|
||||
m_CQRe = new double[ m_uK ];
|
||||
m_CQIm = new double[ m_uK ];
|
||||
|
||||
m_window = 0;
|
||||
m_windowbuf = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Chromagram::~Chromagram()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
int Chromagram::deInitialise()
|
||||
{
|
||||
delete[] m_windowbuf;
|
||||
delete m_window;
|
||||
|
||||
delete [] m_chromadata;
|
||||
|
||||
delete m_FFT;
|
||||
|
||||
delete m_ConstantQ;
|
||||
|
||||
delete [] m_FFTRe;
|
||||
delete [] m_FFTIm;
|
||||
delete [] m_CQRe;
|
||||
delete [] m_CQIm;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// returns the absolute value of complex number xx + i*yy
|
||||
double Chromagram::kabs(double xx, double yy)
|
||||
{
|
||||
double ab = sqrt(xx*xx + yy*yy);
|
||||
return(ab);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
void Chromagram::unityNormalise(double *src)
|
||||
{
|
||||
double min, max;
|
||||
|
||||
double val = 0;
|
||||
|
||||
MathUtilities::getFrameMinMax( src, m_BPO, & min, &max );
|
||||
|
||||
for( unsigned int i = 0; i < m_BPO; i++ )
|
||||
{
|
||||
val = src[ i ] / max;
|
||||
|
||||
src[ i ] = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double* Chromagram::process( const double *data )
|
||||
{
|
||||
if (!m_skGenerated) {
|
||||
// Generate CQ Kernel
|
||||
m_ConstantQ->sparsekernel();
|
||||
m_skGenerated = true;
|
||||
}
|
||||
|
||||
if (!m_window) {
|
||||
m_window = new Window<double>(HammingWindow, m_frameSize);
|
||||
m_windowbuf = new double[m_frameSize];
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_frameSize; ++i) {
|
||||
m_windowbuf[i] = data[i];
|
||||
}
|
||||
m_window->cut(m_windowbuf);
|
||||
|
||||
// FFT of current frame
|
||||
m_FFT->process(false, m_windowbuf, m_FFTRe, m_FFTIm);
|
||||
|
||||
return process(m_FFTRe, m_FFTIm);
|
||||
}
|
||||
|
||||
double* Chromagram::process( const double *real, const double *imag )
|
||||
{
|
||||
if (!m_skGenerated) {
|
||||
// Generate CQ Kernel
|
||||
m_ConstantQ->sparsekernel();
|
||||
m_skGenerated = true;
|
||||
}
|
||||
|
||||
// initialise chromadata to 0
|
||||
for (unsigned i = 0; i < m_BPO; i++) m_chromadata[i] = 0;
|
||||
|
||||
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++)
|
||||
{
|
||||
unsigned firstBin = octave*m_BPO;
|
||||
for (unsigned i = 0; i < m_BPO; i++)
|
||||
{
|
||||
m_chromadata[i] += kabs( m_CQRe[ firstBin + i ], m_CQIm[ firstBin + i ]);
|
||||
}
|
||||
}
|
||||
|
||||
MathUtilities::normalise(m_chromadata, m_BPO, m_normalise);
|
||||
|
||||
return m_chromadata;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef CHROMAGRAM_H
|
||||
#define CHROMAGRAM_H
|
||||
|
||||
#include "dsp/transforms/FFT.h"
|
||||
#include "base/Window.h"
|
||||
#include "ConstantQ.h"
|
||||
|
||||
struct ChromaConfig{
|
||||
unsigned int FS;
|
||||
double min;
|
||||
double max;
|
||||
unsigned int BPO;
|
||||
double CQThresh;
|
||||
MathUtilities::NormaliseType normalise;
|
||||
};
|
||||
|
||||
class Chromagram
|
||||
{
|
||||
|
||||
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; }
|
||||
unsigned int getHopSize() { return m_hopSize; }
|
||||
|
||||
private:
|
||||
int initialise( ChromaConfig Config );
|
||||
int deInitialise();
|
||||
|
||||
Window<double> *m_window;
|
||||
double *m_windowbuf;
|
||||
|
||||
double* m_chromadata;
|
||||
double m_FMin;
|
||||
double m_FMax;
|
||||
unsigned int m_BPO;
|
||||
unsigned int m_uK;
|
||||
|
||||
MathUtilities::NormaliseType m_normalise;
|
||||
|
||||
unsigned int m_frameSize;
|
||||
unsigned int m_hopSize;
|
||||
|
||||
FFTReal* m_FFT;
|
||||
ConstantQ* m_ConstantQ;
|
||||
|
||||
double* m_FFTRe;
|
||||
double* m_FFTIm;
|
||||
double* m_CQRe;
|
||||
double* m_CQIm;
|
||||
|
||||
bool m_skGenerated;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,351 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "ConstantQ.h"
|
||||
#include "dsp/transforms/FFT.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef NOT_DEFINED
|
||||
// see note in CQprecalc
|
||||
|
||||
#include "CQprecalc.cpp"
|
||||
|
||||
static bool push_precalculated(int uk, int fftlength,
|
||||
std::vector<unsigned> &is,
|
||||
std::vector<unsigned> &js,
|
||||
std::vector<double> &real,
|
||||
std::vector<double> &imag)
|
||||
{
|
||||
if (uk == 76 && fftlength == 16384) {
|
||||
push_76_16384(is, js, real, imag);
|
||||
return true;
|
||||
}
|
||||
if (uk == 144 && fftlength == 4096) {
|
||||
push_144_4096(is, js, real, imag);
|
||||
return true;
|
||||
}
|
||||
if (uk == 65 && fftlength == 2048) {
|
||||
push_65_2048(is, js, real, imag);
|
||||
return true;
|
||||
}
|
||||
if (uk == 84 && fftlength == 65536) {
|
||||
push_84_65536(is, js, real, imag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// nextpow2 returns the smallest integer n such that 2^n >= x.
|
||||
static double nextpow2(double x) {
|
||||
double y = ceil(log(x)/log(2.0));
|
||||
return(y);
|
||||
}
|
||||
|
||||
static double squaredModule(const double & xx, const double & yy) {
|
||||
return xx*xx + yy*yy;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ConstantQ::ConstantQ( CQConfig Config ) :
|
||||
m_sparseKernel(0)
|
||||
{
|
||||
initialise( Config );
|
||||
}
|
||||
|
||||
ConstantQ::~ConstantQ()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ConstantQ::sparsekernel()
|
||||
{
|
||||
// std::cerr << "ConstantQ: initialising sparse kernel, uK = " << m_uK << ", FFTLength = " << m_FFTLength << "...";
|
||||
|
||||
SparseKernel *sk = new SparseKernel();
|
||||
|
||||
#ifdef NOT_DEFINED
|
||||
if (push_precalculated(m_uK, m_FFTLength,
|
||||
sk->is, sk->js, sk->real, sk->imag)) {
|
||||
// std::cerr << "using precalculated kernel" << std::endl;
|
||||
m_sparseKernel = sk;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
//generates spectral kernel matrix (upside down?)
|
||||
// initialise temporal kernel with zeros, twice length to deal w. complex numbers
|
||||
|
||||
double* hammingWindowRe = new double [ m_FFTLength ];
|
||||
double* hammingWindowIm = new double [ m_FFTLength ];
|
||||
double* transfHammingWindowRe = new double [ m_FFTLength ];
|
||||
double* transfHammingWindowIm = new double [ m_FFTLength ];
|
||||
|
||||
for (unsigned u=0; u < m_FFTLength; u++)
|
||||
{
|
||||
hammingWindowRe[u] = 0;
|
||||
hammingWindowIm[u] = 0;
|
||||
}
|
||||
|
||||
// Here, fftleng*2 is a guess of the number of sparse cells in the matrix
|
||||
// The matrix K x fftlength but the non-zero cells are an antialiased
|
||||
// square root function. So mostly is a line, with some grey point.
|
||||
sk->is.reserve( m_FFTLength*2 );
|
||||
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
|
||||
//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 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++)
|
||||
{
|
||||
const double angle = 2*PI*m_dQ*i/hammingLength;
|
||||
const double real = cos(angle);
|
||||
const double imag = sin(angle);
|
||||
const double absol = hamming(hammingLength, i)/hammingLength;
|
||||
hammingWindowRe[ origin + i ] = absol*real;
|
||||
hammingWindowIm[ origin + i ] = absol*imag;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_FFTLength/2; ++i) {
|
||||
double temp = hammingWindowRe[i];
|
||||
hammingWindowRe[i] = hammingWindowRe[i + m_FFTLength/2];
|
||||
hammingWindowRe[i + m_FFTLength/2] = temp;
|
||||
temp = hammingWindowIm[i];
|
||||
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++)
|
||||
{
|
||||
// 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);
|
||||
|
||||
// take conjugate, normalise and add to array sparkernel
|
||||
sk->real.push_back( transfHammingWindowRe[ j ]/m_FFTLength);
|
||||
sk->imag.push_back(-transfHammingWindowIm[ j ]/m_FFTLength);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delete [] hammingWindowRe;
|
||||
delete [] hammingWindowIm;
|
||||
delete [] transfHammingWindowRe;
|
||||
delete [] transfHammingWindowIm;
|
||||
|
||||
/*
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
cout.precision(28);
|
||||
|
||||
int n = sk->is.size();
|
||||
int w = 8;
|
||||
cout << "static unsigned int sk_i_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (i % w == 0) cout << " ";
|
||||
cout << sk->is[i];
|
||||
if (i + 1 < n) cout << ", ";
|
||||
if (i % w == w-1) cout << endl;
|
||||
};
|
||||
if (n % w != 0) cout << endl;
|
||||
cout << "};" << endl;
|
||||
|
||||
n = sk->js.size();
|
||||
cout << "static unsigned int sk_j_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (i % w == 0) cout << " ";
|
||||
cout << sk->js[i];
|
||||
if (i + 1 < n) cout << ", ";
|
||||
if (i % w == w-1) cout << endl;
|
||||
};
|
||||
if (n % w != 0) cout << endl;
|
||||
cout << "};" << endl;
|
||||
|
||||
w = 2;
|
||||
n = sk->real.size();
|
||||
cout << "static double sk_real_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (i % w == 0) cout << " ";
|
||||
cout << sk->real[i];
|
||||
if (i + 1 < n) cout << ", ";
|
||||
if (i % w == w-1) cout << endl;
|
||||
};
|
||||
if (n % w != 0) cout << endl;
|
||||
cout << "};" << endl;
|
||||
|
||||
n = sk->imag.size();
|
||||
cout << "static double sk_imag_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (i % w == 0) cout << " ";
|
||||
cout << sk->imag[i];
|
||||
if (i + 1 < n) cout << ", ";
|
||||
if (i % w == w-1) cout << endl;
|
||||
};
|
||||
if (n % w != 0) cout << endl;
|
||||
cout << "};" << endl;
|
||||
|
||||
cout << "static void push_" << m_uK << "_" << m_FFTLength << "(vector<unsigned int> &is, vector<unsigned int> &js, vector<double> &real, vector<double> &imag)" << endl;
|
||||
cout << "{\n is.reserve(" << n << ");\n";
|
||||
cout << " js.reserve(" << n << ");\n";
|
||||
cout << " real.reserve(" << n << ");\n";
|
||||
cout << " imag.reserve(" << n << ");\n";
|
||||
cout << " for (int i = 0; i < " << n << "; ++i) {" << endl;
|
||||
cout << " is.push_back(sk_i_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
|
||||
cout << " js.push_back(sk_j_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
|
||||
cout << " real.push_back(sk_real_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
|
||||
cout << " imag.push_back(sk_imag_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
|
||||
cout << " }" << endl;
|
||||
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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
double* ConstantQ::process( const double* fftdata )
|
||||
{
|
||||
if (!m_sparseKernel) {
|
||||
std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
|
||||
return m_CQdata;
|
||||
}
|
||||
|
||||
SparseKernel *sk = m_sparseKernel;
|
||||
|
||||
for (unsigned row=0; row<2*m_uK; row++)
|
||||
{
|
||||
m_CQdata[ row ] = 0;
|
||||
m_CQdata[ row+1 ] = 0;
|
||||
}
|
||||
const unsigned *fftbin = &(sk->is[0]);
|
||||
const unsigned *cqbin = &(sk->js[0]);
|
||||
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];
|
||||
const unsigned col = fftbin[i];
|
||||
const double & r1 = real[i];
|
||||
const double & i1 = imag[i];
|
||||
const double & r2 = fftdata[ (2*m_FFTLength) - 2*col - 2 ];
|
||||
const double & i2 = fftdata[ (2*m_FFTLength) - 2*col - 2 + 1 ];
|
||||
// add the multiplication
|
||||
m_CQdata[ 2*row ] += (r1*r2 - i1*i2);
|
||||
m_CQdata[ 2*row+1] += (r1*i2 + i1*r2);
|
||||
}
|
||||
|
||||
return m_CQdata;
|
||||
}
|
||||
|
||||
|
||||
void ConstantQ::initialise( CQConfig Config )
|
||||
{
|
||||
m_FS = Config.FS;
|
||||
m_FMin = Config.min; // min freq
|
||||
m_FMax = Config.max; // max freq
|
||||
m_BPO = Config.BPO; // bins per octave
|
||||
m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation
|
||||
|
||||
m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); // Work out Q value for Filter bank
|
||||
m_uK = (unsigned int) ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0)); // No. of constant Q bins
|
||||
|
||||
// std::cerr << "ConstantQ::initialise: rate = " << m_FS << ", fmin = " << m_FMin << ", fmax = " << m_FMax << ", bpo = " << m_BPO << ", K = " << m_uK << ", Q = " << m_dQ << std::endl;
|
||||
|
||||
// work out length of fft required for this constant Q Filter bank
|
||||
m_FFTLength = (int) pow(2, nextpow2(ceil( m_dQ*m_FS/m_FMin )));
|
||||
|
||||
m_hop = m_FFTLength/8; // <------ hop size is window length divided by 32
|
||||
|
||||
// std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl;
|
||||
|
||||
// allocate memory for cqdata
|
||||
m_CQdata = new double [2*m_uK];
|
||||
}
|
||||
|
||||
void ConstantQ::deInitialise()
|
||||
{
|
||||
delete [] m_CQdata;
|
||||
delete m_sparseKernel;
|
||||
}
|
||||
|
||||
void ConstantQ::process(const double *FFTRe, const double* FFTIm,
|
||||
double *CQRe, double *CQIm)
|
||||
{
|
||||
if (!m_sparseKernel) {
|
||||
std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
SparseKernel *sk = m_sparseKernel;
|
||||
|
||||
for (unsigned row=0; row<m_uK; row++)
|
||||
{
|
||||
CQRe[ row ] = 0;
|
||||
CQIm[ row ] = 0;
|
||||
}
|
||||
|
||||
const unsigned *fftbin = &(sk->is[0]);
|
||||
const unsigned *cqbin = &(sk->js[0]);
|
||||
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];
|
||||
const unsigned col = fftbin[i];
|
||||
const double & r1 = real[i];
|
||||
const double & i1 = imag[i];
|
||||
const double & r2 = FFTRe[ m_FFTLength - col - 1 ];
|
||||
const double & i2 = FFTIm[ m_FFTLength - col - 1 ];
|
||||
// add the multiplication
|
||||
CQRe[ row ] += (r1*r2 - i1*i2);
|
||||
CQIm[ row ] += (r1*i2 + i1*r2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef CONSTANTQ_H
|
||||
#define CONSTANTQ_H
|
||||
|
||||
#include <vector>
|
||||
#include "maths/MathAliases.h"
|
||||
#include "maths/MathUtilities.h"
|
||||
|
||||
struct CQConfig{
|
||||
unsigned int FS; // samplerate
|
||||
double min; // minimum frequency
|
||||
double max; // maximum frequency
|
||||
unsigned int BPO; // bins per octave
|
||||
double CQThresh; // threshold
|
||||
};
|
||||
|
||||
class ConstantQ {
|
||||
|
||||
//public functions incl. sparsekernel so can keep out of loop in main
|
||||
public:
|
||||
void process( const double* FFTRe, const double* FFTIm,
|
||||
double* CQRe, double* CQIm );
|
||||
|
||||
ConstantQ( CQConfig Config );
|
||||
~ConstantQ();
|
||||
|
||||
double* process( const double* FFTData );
|
||||
|
||||
void sparsekernel();
|
||||
|
||||
double hamming(int len, int n) {
|
||||
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 ;}
|
||||
int getfftlength() { return m_FFTLength;}
|
||||
int gethop() { return m_hop;}
|
||||
|
||||
private:
|
||||
void initialise( CQConfig Config );
|
||||
void deInitialise();
|
||||
|
||||
double* m_CQdata;
|
||||
unsigned int m_FS;
|
||||
double m_FMin;
|
||||
double m_FMax;
|
||||
double m_dQ;
|
||||
double m_CQThresh;
|
||||
unsigned int m_numWin;
|
||||
unsigned int m_hop;
|
||||
unsigned int m_BPO;
|
||||
unsigned int m_FFTLength;
|
||||
unsigned int m_uK;
|
||||
|
||||
struct SparseKernel {
|
||||
std::vector<unsigned> is;
|
||||
std::vector<unsigned> js;
|
||||
std::vector<double> imag;
|
||||
std::vector<double> real;
|
||||
};
|
||||
|
||||
SparseKernel *m_sparseKernel;
|
||||
};
|
||||
|
||||
|
||||
#endif//CONSTANTQ_H
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||||
|
||||
// GetKeyMode.cpp: implementation of the CGetKeyMode class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "GetKeyMode.h"
|
||||
#include "maths/MathUtilities.h"
|
||||
#include "base/Pitch.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
// Chords profile
|
||||
static double MajProfile[36] =
|
||||
{ 0.0384, 0.0629, 0.0258, 0.0121, 0.0146, 0.0106, 0.0364, 0.0610, 0.0267,
|
||||
0.0126, 0.0121, 0.0086, 0.0364, 0.0623, 0.0279, 0.0275, 0.0414, 0.0186,
|
||||
0.0173, 0.0248, 0.0145, 0.0364, 0.0631, 0.0262, 0.0129, 0.0150, 0.0098,
|
||||
0.0312, 0.0521, 0.0235, 0.0129, 0.0142, 0.0095, 0.0289, 0.0478, 0.0239};
|
||||
|
||||
static double MinProfile[36] =
|
||||
{ 0.0375, 0.0682, 0.0299, 0.0119, 0.0138, 0.0093, 0.0296, 0.0543, 0.0257,
|
||||
0.0292, 0.0519, 0.0246, 0.0159, 0.0234, 0.0135, 0.0291, 0.0544, 0.0248,
|
||||
0.0137, 0.0176, 0.0104, 0.0352, 0.0670, 0.0302, 0.0222, 0.0349, 0.0164,
|
||||
0.0174, 0.0297, 0.0166, 0.0222, 0.0401, 0.0202, 0.0175, 0.0270, 0.0146};
|
||||
//
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
GetKeyMode::GetKeyMode( int sampleRate, float tuningFrequency,
|
||||
double hpcpAverage, double medianAverage ) :
|
||||
m_hpcpAverage( hpcpAverage ),
|
||||
m_medianAverage( medianAverage ),
|
||||
m_ChrPointer(0),
|
||||
m_DecimatedBuffer(0),
|
||||
m_ChromaBuffer(0),
|
||||
m_MeanHPCP(0),
|
||||
m_MajCorr(0),
|
||||
m_MinCorr(0),
|
||||
m_Keys(0),
|
||||
m_MedianFilterBuffer(0),
|
||||
m_SortedBuffer(0),
|
||||
m_keyStrengths(0)
|
||||
{
|
||||
m_DecimationFactor = 8;
|
||||
|
||||
// Chromagram configuration parameters
|
||||
m_ChromaConfig.normalise = MathUtilities::NormaliseUnitMax;
|
||||
m_ChromaConfig.FS = lrint(sampleRate/(double)m_DecimationFactor);
|
||||
if (m_ChromaConfig.FS < 1) m_ChromaConfig.FS = 1;
|
||||
|
||||
// Set C (= MIDI #12) as our base :
|
||||
// This implies that key = 1 => Cmaj, key = 12 => Bmaj, key = 13 => Cmin, etc.
|
||||
m_ChromaConfig.min = Pitch::getFrequencyForPitch
|
||||
(48, 0, tuningFrequency);
|
||||
m_ChromaConfig.max = Pitch::getFrequencyForPitch
|
||||
(96, 0, tuningFrequency);
|
||||
|
||||
m_ChromaConfig.BPO = 36;
|
||||
m_ChromaConfig.CQThresh = 0.0054;
|
||||
|
||||
// Chromagram inst.
|
||||
m_Chroma = new Chromagram( m_ChromaConfig );
|
||||
|
||||
// Get calculated parameters from chroma object
|
||||
m_ChromaFrameSize = m_Chroma->getFrameSize();
|
||||
// override hopsize for this application
|
||||
m_ChromaHopSize = m_ChromaFrameSize;
|
||||
m_BPO = m_ChromaConfig.BPO;
|
||||
|
||||
// std::cerr << "chroma frame size = " << m_ChromaFrameSize << ", decimation factor = " << m_DecimationFactor << " therefore block size = " << getBlockSize() << std::endl;
|
||||
|
||||
// Chromagram average and estimated key median filter lengths
|
||||
m_ChromaBuffersize = (int)ceil( m_hpcpAverage * m_ChromaConfig.FS/m_ChromaFrameSize );
|
||||
m_MedianWinsize = (int)ceil( m_medianAverage * m_ChromaConfig.FS/m_ChromaFrameSize );
|
||||
|
||||
// Reset counters
|
||||
m_bufferindex = 0;
|
||||
m_ChromaBufferFilling = 0;
|
||||
m_MedianBufferFilling = 0;
|
||||
|
||||
// Spawn objectc/arrays
|
||||
m_DecimatedBuffer = new double[m_ChromaFrameSize];
|
||||
|
||||
m_ChromaBuffer = new double[m_BPO * m_ChromaBuffersize];
|
||||
memset( m_ChromaBuffer, 0, sizeof(double) * m_BPO * m_ChromaBuffersize);
|
||||
|
||||
m_MeanHPCP = new double[m_BPO];
|
||||
|
||||
m_MajCorr = new double[m_BPO];
|
||||
m_MinCorr = new double[m_BPO];
|
||||
m_Keys = new double[2*m_BPO];
|
||||
|
||||
m_MedianFilterBuffer = new int[ m_MedianWinsize ];
|
||||
memset( m_MedianFilterBuffer, 0, sizeof(int)*m_MedianWinsize);
|
||||
|
||||
m_SortedBuffer = new int[ m_MedianWinsize ];
|
||||
memset( m_SortedBuffer, 0, sizeof(int)*m_MedianWinsize);
|
||||
|
||||
m_Decimator = new Decimator
|
||||
( m_ChromaFrameSize*m_DecimationFactor, m_DecimationFactor );
|
||||
|
||||
m_keyStrengths = new double[24];
|
||||
}
|
||||
|
||||
GetKeyMode::~GetKeyMode()
|
||||
{
|
||||
|
||||
delete m_Chroma;
|
||||
delete m_Decimator;
|
||||
|
||||
delete [] m_DecimatedBuffer;
|
||||
delete [] m_ChromaBuffer;
|
||||
delete [] m_MeanHPCP;
|
||||
delete [] m_MajCorr;
|
||||
delete [] m_MinCorr;
|
||||
delete [] m_Keys;
|
||||
delete [] m_MedianFilterBuffer;
|
||||
delete [] m_SortedBuffer;
|
||||
|
||||
delete[] m_keyStrengths;
|
||||
}
|
||||
|
||||
double GetKeyMode::krumCorr(double *pData1, double *pData2, unsigned int length)
|
||||
{
|
||||
double retVal= 0.0;
|
||||
|
||||
double num = 0;
|
||||
double den = 0;
|
||||
double mX = MathUtilities::mean( pData1, length );
|
||||
double mY = MathUtilities::mean( pData2, length );
|
||||
|
||||
double sum1 = 0;
|
||||
double sum2 = 0;
|
||||
|
||||
for( unsigned int i = 0; i <length; i++ )
|
||||
{
|
||||
num += ( pData1[i] - mX ) * ( pData2[i] - mY );
|
||||
|
||||
sum1 += ( (pData1[i]-mX) * (pData1[i]-mX) );
|
||||
sum2 += ( (pData2[i]-mY) * (pData2[i]-mY) );
|
||||
}
|
||||
|
||||
den = sqrt(sum1 * sum2);
|
||||
|
||||
if( den>0 )
|
||||
retVal = num/den;
|
||||
else
|
||||
retVal = 0;
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int GetKeyMode::process(double *PCMData)
|
||||
{
|
||||
int key;
|
||||
|
||||
unsigned int j,k;
|
||||
|
||||
//////////////////////////////////////////////
|
||||
m_Decimator->process( PCMData, m_DecimatedBuffer);
|
||||
|
||||
m_ChrPointer = m_Chroma->process( m_DecimatedBuffer );
|
||||
|
||||
|
||||
// Move bins such that the centre of the base note is in the
|
||||
// middle of its three bins :
|
||||
// Added 21.11.07 by Chris Sutton based on debugging with Katy
|
||||
// Noland + comparison with Matlab equivalent.
|
||||
MathUtilities::circShift( m_ChrPointer, m_BPO, 1);
|
||||
/*
|
||||
std::cout << "raw chroma: ";
|
||||
for (int ii = 0; ii < m_BPO; ++ii) {
|
||||
if (ii % (m_BPO/12) == 0) std::cout << "\n";
|
||||
std::cout << m_ChrPointer[ii] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
*/
|
||||
// populate hpcp values;
|
||||
int cbidx;
|
||||
for( j = 0; j < m_BPO; j++ )
|
||||
{
|
||||
cbidx = (m_bufferindex * m_BPO) + j;
|
||||
m_ChromaBuffer[ cbidx ] = m_ChrPointer[j];
|
||||
}
|
||||
|
||||
//keep track of input buffers;
|
||||
if( m_bufferindex++ >= m_ChromaBuffersize - 1)
|
||||
m_bufferindex = 0;
|
||||
|
||||
// track filling of chroma matrix
|
||||
if( m_ChromaBufferFilling++ >= m_ChromaBuffersize)
|
||||
m_ChromaBufferFilling = m_ChromaBuffersize;
|
||||
|
||||
//calculate mean
|
||||
for( k = 0; k < m_BPO; k++ )
|
||||
{
|
||||
double mnVal = 0.0;
|
||||
for( j = 0; j < m_ChromaBufferFilling; j++ )
|
||||
{
|
||||
mnVal += m_ChromaBuffer[ k + (j*m_BPO) ];
|
||||
}
|
||||
|
||||
m_MeanHPCP[k] = mnVal/(double)m_ChromaBufferFilling;
|
||||
}
|
||||
|
||||
|
||||
for( k = 0; k < m_BPO; k++ )
|
||||
{
|
||||
m_MajCorr[k] = krumCorr( m_MeanHPCP, MajProfile, m_BPO );
|
||||
m_MinCorr[k] = krumCorr( m_MeanHPCP, MinProfile, m_BPO );
|
||||
|
||||
MathUtilities::circShift( MajProfile, m_BPO, 1 );
|
||||
MathUtilities::circShift( MinProfile, m_BPO, 1 );
|
||||
}
|
||||
|
||||
for( k = 0; k < m_BPO; k++ )
|
||||
{
|
||||
m_Keys[k] = m_MajCorr[k];
|
||||
m_Keys[k+m_BPO] = m_MinCorr[k];
|
||||
}
|
||||
|
||||
for (k = 0; k < 24; ++k) {
|
||||
m_keyStrengths[k] = 0;
|
||||
}
|
||||
|
||||
for( k = 0; k < m_BPO*2; k++ )
|
||||
{
|
||||
int idx = k / (m_BPO/12);
|
||||
int rem = k % (m_BPO/12);
|
||||
if (rem == 0 || m_Keys[k] > m_keyStrengths[idx]) {
|
||||
m_keyStrengths[idx] = m_Keys[k];
|
||||
}
|
||||
|
||||
// m_keyStrengths[k/(m_BPO/12)] += m_Keys[k];
|
||||
}
|
||||
|
||||
/*
|
||||
std::cout << "raw keys: ";
|
||||
for (int ii = 0; ii < 2*m_BPO; ++ii) {
|
||||
if (ii % (m_BPO/12) == 0) std::cout << "\n";
|
||||
std::cout << m_Keys[ii] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "key strengths: ";
|
||||
for (int ii = 0; ii < 24; ++ii) {
|
||||
if (ii % 6 == 0) std::cout << "\n";
|
||||
std::cout << m_keyStrengths[ii] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
*/
|
||||
double dummy;
|
||||
// '1 +' because we number keys 1-24, not 0-23.
|
||||
key = 1 + (int)ceil( (double)MathUtilities::getMax( m_Keys, 2* m_BPO, &dummy )/3 );
|
||||
|
||||
// std::cout << "key pre-sorting: " << key << std::endl;
|
||||
|
||||
|
||||
//Median filtering
|
||||
|
||||
// track Median buffer initial filling
|
||||
if( m_MedianBufferFilling++ >= m_MedianWinsize)
|
||||
m_MedianBufferFilling = m_MedianWinsize;
|
||||
|
||||
//shift median buffer
|
||||
for( k = 1; k < m_MedianWinsize; k++ )
|
||||
{
|
||||
m_MedianFilterBuffer[ k - 1 ] = m_MedianFilterBuffer[ k ];
|
||||
}
|
||||
|
||||
//write new key value into median buffer
|
||||
m_MedianFilterBuffer[ m_MedianWinsize - 1 ] = key;
|
||||
|
||||
|
||||
//Copy median into sorting buffer, reversed
|
||||
unsigned int ijx = 0;
|
||||
for( k = 0; k < m_MedianWinsize; k++ )
|
||||
{
|
||||
m_SortedBuffer[k] = m_MedianFilterBuffer[m_MedianWinsize-1-ijx];
|
||||
ijx++;
|
||||
}
|
||||
|
||||
qsort(m_SortedBuffer, m_MedianBufferFilling, sizeof(unsigned int),
|
||||
MathUtilities::compareInt);
|
||||
/*
|
||||
std::cout << "sorted: ";
|
||||
for (int ii = 0; ii < m_MedianBufferFilling; ++ii) {
|
||||
std::cout << m_SortedBuffer[ii] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
*/
|
||||
int sortlength = m_MedianBufferFilling;
|
||||
int midpoint = (int)ceil((double)sortlength/2);
|
||||
|
||||
// std::cout << "midpoint = " << midpoint << endl;
|
||||
|
||||
if( midpoint <= 0 )
|
||||
midpoint = 1;
|
||||
|
||||
key = m_SortedBuffer[midpoint-1];
|
||||
|
||||
// std::cout << "returning key = " << key << endl;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
bool GetKeyMode::isModeMinor( int key )
|
||||
{
|
||||
return (key > 12);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GETKEYMODE_H
|
||||
#define GETKEYMODE_H
|
||||
|
||||
|
||||
#include "dsp/rateconversion/Decimator.h"
|
||||
#include "dsp/chromagram/Chromagram.h"
|
||||
|
||||
|
||||
class GetKeyMode
|
||||
{
|
||||
public:
|
||||
GetKeyMode( int sampleRate, float tuningFrequency,
|
||||
double hpcpAverage, double medianAverage );
|
||||
|
||||
virtual ~GetKeyMode();
|
||||
|
||||
int process( double* PCMData );
|
||||
|
||||
double krumCorr( double* pData1, double* pData2, unsigned int length );
|
||||
|
||||
unsigned int getBlockSize() { return m_ChromaFrameSize*m_DecimationFactor; }
|
||||
unsigned int getHopSize() { return m_ChromaHopSize*m_DecimationFactor; }
|
||||
|
||||
double* getChroma() { return m_ChrPointer; }
|
||||
unsigned int getChromaSize() { return m_BPO; }
|
||||
|
||||
double* getMeanHPCP() { return m_MeanHPCP; }
|
||||
|
||||
double *getKeyStrengths() { return m_keyStrengths; }
|
||||
|
||||
bool isModeMinor( int key );
|
||||
|
||||
protected:
|
||||
|
||||
double m_hpcpAverage;
|
||||
double m_medianAverage;
|
||||
unsigned int m_DecimationFactor;
|
||||
|
||||
//Decimator (fixed)
|
||||
Decimator* m_Decimator;
|
||||
|
||||
//chroma configuration
|
||||
ChromaConfig m_ChromaConfig;
|
||||
|
||||
//Chromagram object
|
||||
Chromagram* m_Chroma;
|
||||
|
||||
//Chromagram output pointer
|
||||
double* m_ChrPointer;
|
||||
|
||||
//Framesize
|
||||
unsigned int m_ChromaFrameSize;
|
||||
//Hop
|
||||
unsigned int m_ChromaHopSize;
|
||||
//Bins per octave
|
||||
unsigned int m_BPO;
|
||||
|
||||
|
||||
unsigned int m_ChromaBuffersize;
|
||||
unsigned int m_MedianWinsize;
|
||||
|
||||
unsigned int m_bufferindex;
|
||||
unsigned int m_ChromaBufferFilling;
|
||||
unsigned int m_MedianBufferFilling;
|
||||
|
||||
|
||||
double* m_DecimatedBuffer;
|
||||
double* m_ChromaBuffer;
|
||||
double* m_MeanHPCP;
|
||||
|
||||
double* m_MajCorr;
|
||||
double* m_MinCorr;
|
||||
double* m_Keys;
|
||||
int* m_MedianFilterBuffer;
|
||||
int* m_SortedBuffer;
|
||||
|
||||
double *m_keyStrengths;
|
||||
};
|
||||
|
||||
#endif // !defined GETKEYMODE_H
|
|
@ -0,0 +1,276 @@
|
|||
/* -*- 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 2005 Nicolas Chetry, copyright 2008 QMUL.
|
||||
|
||||
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 <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "MFCC.h"
|
||||
#include "dsp/transforms/FFT.h"
|
||||
#include "base/Window.h"
|
||||
|
||||
MFCC::MFCC(MFCCConfig config)
|
||||
{
|
||||
int i,j;
|
||||
|
||||
/* 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));
|
||||
}
|
||||
|
||||
mfccFilterWeights = (double**)calloc(totalFilters, sizeof(double*));
|
||||
for (i = 0; i < totalFilters; i++) {
|
||||
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] *
|
||||
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 ) *
|
||||
(double) samplingRate);
|
||||
}
|
||||
|
||||
/* Build now the mccFilterWeight matrix */
|
||||
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]);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mfccFilterWeights[i][j] = 0.0;
|
||||
}
|
||||
|
||||
if ((fftFreqs[j]>center[i]) && (fftFreqs[j]<upper[i])) {
|
||||
|
||||
mfccFilterWeights[i][j] = mfccFilterWeights[i][j]
|
||||
+ triangleHeight[i] * (upper[i]-fftFreqs[j])
|
||||
/ (upper[i]-center[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfccFilterWeights[i][j] = mfccFilterWeights[i][j] + 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.))
|
||||
* cos((double) i * ((double) j + 0.5) / (double) totalFilters * pi);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* Allocate memory for the FFT */
|
||||
realOut = (double*)calloc(fftSize, sizeof(double));
|
||||
imagOut = (double*)calloc(fftSize, sizeof(double));
|
||||
|
||||
earMag = (double*)calloc(totalFilters, sizeof(double));
|
||||
fftMag = (double*)calloc(fftSize/2, sizeof(double));
|
||||
|
||||
free(freqs);
|
||||
free(lower);
|
||||
free(center);
|
||||
free(upper);
|
||||
free(triangleHeight);
|
||||
free(fftFreqs);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
delete fft;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* 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);
|
||||
|
||||
free(inputData);
|
||||
|
||||
return process(realOut, imagOut, outceps);
|
||||
}
|
||||
|
||||
int MFCC::process(const double *real, const double *imag, double *outceps)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < fftSize/2; ++i) {
|
||||
fftMag[i] = sqrt(real[i] * real[i] + imag[i] * imag[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < totalFilters; ++i) {
|
||||
earMag[i] = 0.0;
|
||||
}
|
||||
|
||||
/* Multiply by mfccFilterWeights */
|
||||
for (i = 0; i < totalFilters; i++) {
|
||||
double tmp = 0.0;
|
||||
for (j = 0; j < fftSize/2; j++) {
|
||||
tmp = tmp + (mfccFilterWeights[i][j] * fftMag[j]);
|
||||
}
|
||||
if (tmp > 0) earMag[i] = log10(tmp);
|
||||
else earMag[i] = 0.0;
|
||||
|
||||
if (logPower != 1.0) {
|
||||
earMag[i] = pow(earMag[i], logPower);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* 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++){
|
||||
tmp = tmp + mfccDCTMatrix[i][j] * earMag[j];
|
||||
}
|
||||
outceps[i] = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 1; i < nceps+1; i++) {
|
||||
double tmp = 0.;
|
||||
for (j = 0; j < totalFilters; j++){
|
||||
tmp = tmp + mfccDCTMatrix[i][j] * earMag[j];
|
||||
}
|
||||
outceps[i-1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return nceps;
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/* -*- 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 2005 Nicolas Chetry, copyright 2008 QMUL.
|
||||
|
||||
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 MFCC_H
|
||||
#define MFCC_H
|
||||
|
||||
#include "base/Window.h"
|
||||
|
||||
class FFTReal;
|
||||
|
||||
struct MFCCConfig {
|
||||
int FS;
|
||||
int fftsize;
|
||||
int nceps;
|
||||
double logpower;
|
||||
bool want_c0;
|
||||
WindowType window;
|
||||
MFCCConfig(int _FS) :
|
||||
FS(_FS), fftsize(2048), nceps(19),
|
||||
logpower(1.0), want_c0(true), window(HammingWindow) { }
|
||||
};
|
||||
|
||||
class MFCC
|
||||
{
|
||||
public:
|
||||
MFCC(MFCCConfig config);
|
||||
virtual ~MFCC();
|
||||
|
||||
/**
|
||||
* Process time-domain input data. inframe must contain
|
||||
* getfftlength() samples. outceps must contain space for nceps
|
||||
* values, plus one if want_c0 is specified.
|
||||
*/
|
||||
int process(const double *inframe, double *outceps);
|
||||
|
||||
/**
|
||||
* Process time-domain input data. real and imag must contain
|
||||
* getfftlength()/2+1 elements (i.e. the conjugate half of the FFT
|
||||
* is not expected). outceps must contain space for nceps values,
|
||||
* plus one if want_c0 is specified.
|
||||
*/
|
||||
int process(const double *real, const double *imag, double *outceps);
|
||||
|
||||
int getfftlength() const { return fftSize; }
|
||||
|
||||
private:
|
||||
/* Filter bank parameters */
|
||||
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;
|
||||
double *fftMag;
|
||||
double *earMag;
|
||||
FFTReal *fft;
|
||||
|
||||
/* Set if user want C0 */
|
||||
int WANT_C0;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "DetectionFunction.h"
|
||||
#include <cstring>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
DetectionFunction::DetectionFunction( DFConfig Config ) :
|
||||
m_window(0)
|
||||
{
|
||||
m_magHistory = NULL;
|
||||
m_phaseHistory = NULL;
|
||||
m_phaseHistoryOld = NULL;
|
||||
m_magPeaks = NULL;
|
||||
|
||||
initialise( Config );
|
||||
}
|
||||
|
||||
DetectionFunction::~DetectionFunction()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
|
||||
void DetectionFunction::initialise( DFConfig Config )
|
||||
{
|
||||
m_dataLength = Config.frameLength;
|
||||
m_halfLength = m_dataLength/2;
|
||||
|
||||
m_DFType = Config.DFType;
|
||||
m_stepSize = Config.stepSize;
|
||||
|
||||
m_whiten = Config.adaptiveWhitening;
|
||||
m_whitenRelaxCoeff = Config.whiteningRelaxCoeff;
|
||||
m_whitenFloor = Config.whiteningFloor;
|
||||
if (m_whitenRelaxCoeff < 0) m_whitenRelaxCoeff = 0.9997;
|
||||
if (m_whitenFloor < 0) m_whitenFloor = 0.01;
|
||||
|
||||
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));
|
||||
|
||||
m_phaseHistoryOld = new double[ m_halfLength ];
|
||||
memset(m_phaseHistoryOld,0, m_halfLength*sizeof(double));
|
||||
|
||||
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_DFWindowedFrame = new double[ m_dataLength ];
|
||||
m_magnitude = new double[ m_halfLength ];
|
||||
m_thetaAngle = new double[ m_halfLength ];
|
||||
|
||||
m_window = new Window<double>(HanningWindow, m_dataLength);
|
||||
}
|
||||
|
||||
void DetectionFunction::deInitialise()
|
||||
{
|
||||
delete [] m_magHistory ;
|
||||
delete [] m_phaseHistory ;
|
||||
delete [] m_phaseHistoryOld ;
|
||||
delete [] m_magPeaks ;
|
||||
|
||||
delete m_phaseVoc;
|
||||
|
||||
delete [] m_DFWindowedFrame;
|
||||
delete [] m_magnitude;
|
||||
delete [] m_thetaAngle;
|
||||
|
||||
delete m_window;
|
||||
}
|
||||
|
||||
double DetectionFunction::process( const double *TDomain )
|
||||
{
|
||||
m_window->cut( TDomain, m_DFWindowedFrame );
|
||||
|
||||
// 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!
|
||||
|
||||
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 (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);
|
||||
|
||||
if (m_whiten) whiten();
|
||||
|
||||
return runDF();
|
||||
}
|
||||
|
||||
double DetectionFunction::process( const double *magnitudes, const double *phases )
|
||||
{
|
||||
for (size_t i = 0; i < m_halfLength; ++i) {
|
||||
m_magnitude[i] = magnitudes[i];
|
||||
m_thetaAngle[i] = phases[i];
|
||||
}
|
||||
|
||||
if (m_whiten) whiten();
|
||||
|
||||
return runDF();
|
||||
}
|
||||
|
||||
void DetectionFunction::whiten()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_halfLength; ++i) {
|
||||
double m = m_magnitude[i];
|
||||
if (m < m_magPeaks[i]) {
|
||||
m = m + (m_magPeaks[i] - m) * m_whitenRelaxCoeff;
|
||||
}
|
||||
if (m < m_whitenFloor) m = m_whitenFloor;
|
||||
m_magPeaks[i] = m;
|
||||
m_magnitude[i] /= m;
|
||||
}
|
||||
}
|
||||
|
||||
double DetectionFunction::runDF()
|
||||
{
|
||||
double retVal = 0;
|
||||
|
||||
switch( m_DFType )
|
||||
{
|
||||
case DF_HFC:
|
||||
retVal = HFC( m_halfLength, m_magnitude);
|
||||
break;
|
||||
|
||||
case DF_SPECDIFF:
|
||||
retVal = specDiff( m_halfLength, m_magnitude);
|
||||
break;
|
||||
|
||||
case DF_PHASEDEV:
|
||||
retVal = phaseDev( m_halfLength, m_thetaAngle);
|
||||
break;
|
||||
|
||||
case DF_COMPLEXSD:
|
||||
retVal = complexSD( m_halfLength, m_magnitude, m_thetaAngle);
|
||||
break;
|
||||
|
||||
case DF_BROADBAND:
|
||||
retVal = broadband( m_halfLength, m_magnitude);
|
||||
break;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
double DetectionFunction::HFC(unsigned int length, double *src)
|
||||
{
|
||||
unsigned int i;
|
||||
double val = 0;
|
||||
|
||||
for( i = 0; i < length; i++)
|
||||
{
|
||||
val += src[ i ] * ( i + 1);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
double DetectionFunction::specDiff(unsigned int length, double *src)
|
||||
{
|
||||
unsigned int i;
|
||||
double val = 0.0;
|
||||
double temp = 0.0;
|
||||
double diff = 0.0;
|
||||
|
||||
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.)
|
||||
|
||||
val += diff;
|
||||
|
||||
m_magHistory[ i ] = src[ i ];
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
double DetectionFunction::phaseDev(unsigned int length, double *srcPhase)
|
||||
{
|
||||
unsigned int i;
|
||||
double tmpPhase = 0;
|
||||
double tmpVal = 0;
|
||||
double val = 0;
|
||||
|
||||
double dev = 0;
|
||||
|
||||
for( i = 0; i < length; i++)
|
||||
{
|
||||
tmpPhase = (srcPhase[ i ]- 2*m_phaseHistory[ i ]+m_phaseHistoryOld[ i ]);
|
||||
dev = MathUtilities::princarg( tmpPhase );
|
||||
|
||||
// A previous version of this code only counted the value here
|
||||
// if the magnitude exceeded 0.1. My impression is that
|
||||
// doesn't greatly improve the results for "loud" music (so
|
||||
// long as the peak picker is reasonably sophisticated), but
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
double DetectionFunction::complexSD(unsigned int length, double *srcMagnitude, double *srcPhase)
|
||||
{
|
||||
unsigned int i;
|
||||
double val = 0;
|
||||
double tmpPhase = 0;
|
||||
double tmpReal = 0;
|
||||
double tmpImag = 0;
|
||||
|
||||
double dev = 0;
|
||||
ComplexData meas = ComplexData( 0, 0 );
|
||||
ComplexData j = ComplexData( 0, 1 );
|
||||
|
||||
for( i = 0; i < length; i++)
|
||||
{
|
||||
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 ];
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
double DetectionFunction::broadband(unsigned int length, double *src)
|
||||
{
|
||||
double val = 0;
|
||||
for (unsigned int i = 0; i < length; ++i) {
|
||||
double sqrmag = src[i] * src[i];
|
||||
if (m_magHistory[i] > 0.0) {
|
||||
double diff = 10.0 * log10(sqrmag / m_magHistory[i]);
|
||||
if (diff > m_dbRise) val = val + 1;
|
||||
}
|
||||
m_magHistory[i] = sqrmag;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
double* DetectionFunction::getSpectrumMagnitude()
|
||||
{
|
||||
return m_magnitude;
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef DETECTIONFUNCTION_H
|
||||
#define DETECTIONFUNCTION_H
|
||||
|
||||
#include "maths/MathUtilities.h"
|
||||
#include "maths/MathAliases.h"
|
||||
#include "dsp/phasevocoder/PhaseVocoder.h"
|
||||
#include "base/Window.h"
|
||||
|
||||
#define DF_HFC (1)
|
||||
#define DF_SPECDIFF (2)
|
||||
#define DF_PHASEDEV (3)
|
||||
#define DF_COMPLEXSD (4)
|
||||
#define DF_BROADBAND (5)
|
||||
|
||||
struct DFConfig{
|
||||
unsigned int stepSize; // DF step in samples
|
||||
unsigned int frameLength; // DF analysis window - usually 2*step
|
||||
int DFType; // type of detection function ( see defines )
|
||||
double dbRise; // only used for broadband df (and required for it)
|
||||
bool adaptiveWhitening; // perform adaptive whitening
|
||||
double whiteningRelaxCoeff; // if < 0, a sensible default will be used
|
||||
double whiteningFloor; // if < 0, a sensible default will be used
|
||||
};
|
||||
|
||||
class DetectionFunction
|
||||
{
|
||||
public:
|
||||
double* getSpectrumMagnitude();
|
||||
DetectionFunction( DFConfig Config );
|
||||
virtual ~DetectionFunction();
|
||||
double process( const double* TDomain );
|
||||
double process( const double* magnitudes, const double* phases );
|
||||
|
||||
private:
|
||||
void whiten();
|
||||
double runDF();
|
||||
|
||||
double HFC( unsigned int length, double* src);
|
||||
double specDiff( unsigned int length, double* src);
|
||||
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();
|
||||
|
||||
int m_DFType;
|
||||
unsigned int m_dataLength;
|
||||
unsigned int m_halfLength;
|
||||
unsigned int m_stepSize;
|
||||
double m_dbRise;
|
||||
bool m_whiten;
|
||||
double m_whitenRelaxCoeff;
|
||||
double m_whitenFloor;
|
||||
|
||||
double* m_magHistory;
|
||||
double* m_phaseHistory;
|
||||
double* m_phaseHistoryOld;
|
||||
double* m_magPeaks;
|
||||
|
||||
double* m_DFWindowedFrame; // Array for windowed analysis frame
|
||||
double* m_magnitude; // Magnitude of analysis frame ( frequency domain )
|
||||
double* m_thetaAngle;// Phase of analysis frame ( frequency domain )
|
||||
|
||||
Window<double> *m_window;
|
||||
PhaseVocoder* m_phaseVoc; // Phase Vocoder
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,148 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "PeakPicking.h"
|
||||
#include "maths/Polyfit.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
PeakPicking::PeakPicking( PPickParams Config )
|
||||
{
|
||||
m_workBuffer = NULL;
|
||||
initialise( Config );
|
||||
}
|
||||
|
||||
PeakPicking::~PeakPicking()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
void PeakPicking::initialise( PPickParams Config )
|
||||
{
|
||||
m_DFLength = Config.length ;
|
||||
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.winPre = Config.WinT.pre;
|
||||
m_DFProcessingParams.winPost = Config.WinT.post;
|
||||
m_DFProcessingParams.AlphaNormParam = Config.alpha;
|
||||
m_DFProcessingParams.isMedianPositive = false;
|
||||
|
||||
m_DFSmoothing = new DFProcess( m_DFProcessingParams );
|
||||
|
||||
m_workBuffer = new double[ m_DFLength ];
|
||||
memset( m_workBuffer, 0, sizeof(double)*m_DFLength);
|
||||
}
|
||||
|
||||
void PeakPicking::deInitialise()
|
||||
{
|
||||
delete [] m_workBuffer;
|
||||
delete m_DFSmoothing;
|
||||
m_workBuffer = NULL;
|
||||
}
|
||||
|
||||
void PeakPicking::process( double* src, unsigned int len, vector<int> &onsets )
|
||||
{
|
||||
if (len < 4) return;
|
||||
|
||||
vector <double> m_maxima;
|
||||
|
||||
// Signal conditioning
|
||||
m_DFSmoothing->process( src, m_workBuffer );
|
||||
|
||||
for( unsigned int u = 0; u < len; u++)
|
||||
{
|
||||
m_maxima.push_back( m_workBuffer[ u ] );
|
||||
}
|
||||
|
||||
quadEval( m_maxima, onsets );
|
||||
|
||||
for( int b = 0; b < m_maxima.size(); b++)
|
||||
{
|
||||
src[ b ] = m_maxima[ b ];
|
||||
}
|
||||
}
|
||||
|
||||
int PeakPicking::quadEval( vector<double> &src, vector<int> &idx )
|
||||
{
|
||||
unsigned int maxLength;
|
||||
|
||||
vector <int> m_maxIndex;
|
||||
vector <int> m_onsetPosition;
|
||||
|
||||
vector <double> m_maxFit;
|
||||
vector <double> m_poly;
|
||||
vector <double> m_err;
|
||||
|
||||
double p;
|
||||
|
||||
m_poly.push_back(0);
|
||||
m_poly.push_back(0);
|
||||
m_poly.push_back(0);
|
||||
|
||||
for( int t = -2; t < 3; t++)
|
||||
{
|
||||
m_err.push_back( (double)t );
|
||||
}
|
||||
for( unsigned int i = 2; i < src.size() - 2; i++)
|
||||
{
|
||||
if( (src[i] > src[i-1]) && (src[i] > src[i+1]) && (src[i] > 0) )
|
||||
{
|
||||
// m_maxIndex.push_back( i + 1 );
|
||||
m_maxIndex.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
maxLength = m_maxIndex.size();
|
||||
|
||||
double selMax = 0;
|
||||
|
||||
for( unsigned int j = 0; j < maxLength ; j++)
|
||||
{
|
||||
for (int k = -2; k <= 2; ++k)
|
||||
{
|
||||
selMax = src[ m_maxIndex[j] + k ] ;
|
||||
m_maxFit.push_back(selMax);
|
||||
}
|
||||
|
||||
p = TPolyFit::PolyFit2( m_err, m_maxFit, m_poly);
|
||||
|
||||
double f = m_poly[0];
|
||||
double g = m_poly[1];
|
||||
double h = m_poly[2];
|
||||
|
||||
int kk = m_poly.size();
|
||||
|
||||
if (h < -Qfilta || f > Qfiltc)
|
||||
{
|
||||
idx.push_back(m_maxIndex[j]);
|
||||
}
|
||||
|
||||
m_maxFit.clear();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
// PeakPicking.h: interface for the PeakPicking class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef PEAKPICKING_H
|
||||
#define PEAKPICKING_H
|
||||
|
||||
#include "maths/MathUtilities.h"
|
||||
#include "maths/MathAliases.h"
|
||||
#include "dsp/signalconditioning/DFProcess.h"
|
||||
|
||||
|
||||
struct PPWinThresh
|
||||
{
|
||||
unsigned int pre;
|
||||
unsigned int post;
|
||||
};
|
||||
|
||||
struct QFitThresh
|
||||
{
|
||||
double a;
|
||||
double b;
|
||||
double c;
|
||||
};
|
||||
|
||||
struct PPickParams
|
||||
{
|
||||
unsigned int length; //Detection FunctionLength
|
||||
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
|
||||
double* LPACoeffs; //low pass Filter den coefficients
|
||||
double* LPBCoeffs; //low pass Filter num coefficients
|
||||
PPWinThresh WinT;//window size in frames for adaptive thresholding [pre post]:
|
||||
QFitThresh QuadThresh;
|
||||
};
|
||||
|
||||
class PeakPicking
|
||||
{
|
||||
public:
|
||||
PeakPicking( PPickParams Config );
|
||||
virtual ~PeakPicking();
|
||||
|
||||
void process( double* src, unsigned int len, vector<int> &onsets );
|
||||
|
||||
|
||||
private:
|
||||
void initialise( PPickParams Config );
|
||||
void deInitialise();
|
||||
int quadEval( vector<double> &src, vector<int> &idx );
|
||||
|
||||
DFProcConfig m_DFProcessingParams;
|
||||
|
||||
unsigned int m_DFLength ;
|
||||
double Qfilta ;
|
||||
double Qfiltb;
|
||||
double Qfiltc;
|
||||
|
||||
|
||||
double* m_workBuffer;
|
||||
|
||||
DFProcess* m_DFSmoothing;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "PhaseVocoder.h"
|
||||
#include "dsp/transforms/FFT.h"
|
||||
#include <math.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
PhaseVocoder::PhaseVocoder(unsigned int n) :
|
||||
m_n(n)
|
||||
{
|
||||
m_fft = new FFTReal(m_n);
|
||||
m_realOut = new double[m_n];
|
||||
m_imagOut = new double[m_n];
|
||||
}
|
||||
|
||||
PhaseVocoder::~PhaseVocoder()
|
||||
{
|
||||
delete [] m_realOut;
|
||||
delete [] m_imagOut;
|
||||
delete m_fft;
|
||||
}
|
||||
|
||||
void PhaseVocoder::FFTShift(unsigned int size, double *src)
|
||||
{
|
||||
const int hs = size/2;
|
||||
for (int i = 0; i < hs; ++i) {
|
||||
double tmp = src[i];
|
||||
src[i] = src[i + hs];
|
||||
src[i + hs] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void PhaseVocoder::process(double *src, double *mag, double *theta)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void PhaseVocoder::getMagnitude(unsigned int size, double *mag, double *real, double *imag)
|
||||
{
|
||||
unsigned int j;
|
||||
|
||||
for( j = 0; j < size; j++)
|
||||
{
|
||||
mag[ j ] = sqrt( real[ j ] * real[ j ] + imag[ j ] * imag[ j ]);
|
||||
}
|
||||
}
|
||||
|
||||
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 ]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef PHASEVOCODER_H
|
||||
#define PHASEVOCODER_H
|
||||
|
||||
class FFTReal;
|
||||
|
||||
class PhaseVocoder
|
||||
{
|
||||
public:
|
||||
PhaseVocoder( unsigned int size );
|
||||
virtual ~PhaseVocoder();
|
||||
|
||||
void process( double* src, double* mag, double* theta);
|
||||
|
||||
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);
|
||||
|
||||
unsigned int m_n;
|
||||
FFTReal *m_fft;
|
||||
double *m_imagOut;
|
||||
double *m_realOut;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,226 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "Decimator.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Decimator::Decimator( unsigned int inLength, unsigned int decFactor )
|
||||
{
|
||||
|
||||
m_inputLength = 0;
|
||||
m_outputLength = 0;
|
||||
m_decFactor = 1;
|
||||
|
||||
initialise( inLength, decFactor );
|
||||
}
|
||||
|
||||
Decimator::~Decimator()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
void Decimator::initialise( unsigned int inLength, unsigned int decFactor)
|
||||
{
|
||||
m_inputLength = inLength;
|
||||
m_decFactor = decFactor;
|
||||
m_outputLength = m_inputLength / m_decFactor;
|
||||
|
||||
decBuffer = new double[ m_inputLength ];
|
||||
|
||||
// If adding new factors here, add them to
|
||||
// getHighestSupportedFactor in the header as well
|
||||
|
||||
if(m_decFactor == 8)
|
||||
{
|
||||
//////////////////////////////////////////////////
|
||||
b[0] = 0.060111378492136;
|
||||
b[1] = -0.257323420830598;
|
||||
b[2] = 0.420583503165928;
|
||||
b[3] = -0.222750785197418;
|
||||
b[4] = -0.222750785197418;
|
||||
b[5] = 0.420583503165928;
|
||||
b[6] = -0.257323420830598;
|
||||
b[7] = 0.060111378492136;
|
||||
|
||||
a[0] = 1;
|
||||
a[1] = -5.667654878577432;
|
||||
a[2] = 14.062452278088417;
|
||||
a[3] = -19.737303840697738;
|
||||
a[4] = 16.889698874608641;
|
||||
a[5] = -8.796600612325928;
|
||||
a[6] = 2.577553446979888;
|
||||
a[7] = -0.326903916815751;
|
||||
//////////////////////////////////////////////////
|
||||
}
|
||||
else if( m_decFactor == 4 )
|
||||
{
|
||||
//////////////////////////////////////////////////
|
||||
b[ 0 ] = 0.10133306904918619;
|
||||
b[ 1 ] = -0.2447523353702363;
|
||||
b[ 2 ] = 0.33622528590120965;
|
||||
b[ 3 ] = -0.13936581560633518;
|
||||
b[ 4 ] = -0.13936581560633382;
|
||||
b[ 5 ] = 0.3362252859012087;
|
||||
b[ 6 ] = -0.2447523353702358;
|
||||
b[ 7 ] = 0.10133306904918594;
|
||||
|
||||
a[ 0 ] = 1;
|
||||
a[ 1 ] = -3.9035590278139427;
|
||||
a[ 2 ] = 7.5299379980621133;
|
||||
a[ 3 ] = -8.6890803793177511;
|
||||
a[ 4 ] = 6.4578667096099176;
|
||||
a[ 5 ] = -3.0242979431223631;
|
||||
a[ 6 ] = 0.83043385136748382;
|
||||
a[ 7 ] = -0.094420800837809335;
|
||||
//////////////////////////////////////////////////
|
||||
}
|
||||
else if( m_decFactor == 2 )
|
||||
{
|
||||
//////////////////////////////////////////////////
|
||||
b[ 0 ] = 0.20898944260075727;
|
||||
b[ 1 ] = 0.40011234879814367;
|
||||
b[ 2 ] = 0.819741973072733;
|
||||
b[ 3 ] = 1.0087419911682323;
|
||||
b[ 4 ] = 1.0087419911682325;
|
||||
b[ 5 ] = 0.81974197307273156;
|
||||
b[ 6 ] = 0.40011234879814295;
|
||||
b[ 7 ] = 0.20898944260075661;
|
||||
|
||||
a[ 0 ] = 1;
|
||||
a[ 1 ] = 0.0077331184208358217;
|
||||
a[ 2 ] = 1.9853971155964376;
|
||||
a[ 3 ] = 0.19296739275341004;
|
||||
a[ 4 ] = 1.2330748872852182;
|
||||
a[ 5 ] = 0.18705341389316466;
|
||||
a[ 6 ] = 0.23659265908013868;
|
||||
a[ 7 ] = 0.032352924250533946;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_decFactor != 1 ) {
|
||||
std::cerr << "WARNING: Decimator::initialise: unsupported decimation factor " << m_decFactor << ", no antialiasing filter will be used" << std::endl;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
b[ 0 ] = 1;
|
||||
b[ 1 ] = 0;
|
||||
b[ 2 ] = 0;
|
||||
b[ 3 ] = 0;
|
||||
b[ 4 ] = 0;
|
||||
b[ 5 ] = 0;
|
||||
b[ 6 ] = 0;
|
||||
b[ 7 ] = 0;
|
||||
|
||||
a[ 0 ] = 1;
|
||||
a[ 1 ] = 0;
|
||||
a[ 2 ] = 0;
|
||||
a[ 3 ] = 0;
|
||||
a[ 4 ] = 0;
|
||||
a[ 5 ] = 0;
|
||||
a[ 6 ] = 0;
|
||||
a[ 7 ] = 0;
|
||||
}
|
||||
|
||||
resetFilter();
|
||||
}
|
||||
|
||||
void Decimator::deInitialise()
|
||||
{
|
||||
delete [] decBuffer;
|
||||
}
|
||||
|
||||
void Decimator::resetFilter()
|
||||
{
|
||||
Input = Output = 0;
|
||||
|
||||
o1=o2=o3=o4=o5=o6=o7=0;
|
||||
}
|
||||
|
||||
void Decimator::doAntiAlias(const double *src, double *dst, unsigned int length)
|
||||
{
|
||||
|
||||
for( unsigned int i = 0; i < length; i++ )
|
||||
{
|
||||
Input = (double)src[ i ];
|
||||
|
||||
Output = Input * b[ 0 ] + o1;
|
||||
|
||||
o1 = Input * b[ 1 ] - Output * a[ 1 ] + o2;
|
||||
o2 = Input * b[ 2 ] - Output * a[ 2 ] + o3;
|
||||
o3 = Input * b[ 3 ] - Output * a[ 3 ] + o4;
|
||||
o4 = Input * b[ 4 ] - Output * a[ 4 ] + o5;
|
||||
o5 = Input * b[ 5 ] - Output * a[ 5 ] + o6;
|
||||
o6 = Input * b[ 6 ] - Output * a[ 6 ] + o7;
|
||||
o7 = Input * b[ 7 ] - Output * a[ 7 ] ;
|
||||
|
||||
dst[ i ] = Output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Decimator::doAntiAlias(const float *src, double *dst, unsigned int length)
|
||||
{
|
||||
|
||||
for( unsigned int i = 0; i < length; i++ )
|
||||
{
|
||||
Input = (double)src[ i ];
|
||||
|
||||
Output = Input * b[ 0 ] + o1;
|
||||
|
||||
o1 = Input * b[ 1 ] - Output * a[ 1 ] + o2;
|
||||
o2 = Input * b[ 2 ] - Output * a[ 2 ] + o3;
|
||||
o3 = Input * b[ 3 ] - Output * a[ 3 ] + o4;
|
||||
o4 = Input * b[ 4 ] - Output * a[ 4 ] + o5;
|
||||
o5 = Input * b[ 5 ] - Output * a[ 5 ] + o6;
|
||||
o6 = Input * b[ 6 ] - Output * a[ 6 ] + o7;
|
||||
o7 = Input * b[ 7 ] - Output * a[ 7 ] ;
|
||||
|
||||
dst[ i ] = Output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Decimator::process(const double *src, double *dst)
|
||||
{
|
||||
if( m_decFactor != 1 )
|
||||
{
|
||||
doAntiAlias( src, decBuffer, m_inputLength );
|
||||
}
|
||||
unsigned idx = 0;
|
||||
|
||||
for( unsigned int i = 0; i < m_outputLength; i++ )
|
||||
{
|
||||
dst[ idx++ ] = decBuffer[ m_decFactor * i ];
|
||||
}
|
||||
}
|
||||
|
||||
void Decimator::process(const float *src, float *dst)
|
||||
{
|
||||
if( m_decFactor != 1 )
|
||||
{
|
||||
doAntiAlias( src, decBuffer, m_inputLength );
|
||||
}
|
||||
unsigned idx = 0;
|
||||
|
||||
for( unsigned int i = 0; i < m_outputLength; i++ )
|
||||
{
|
||||
dst[ idx++ ] = decBuffer[ m_decFactor * i ];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef DECIMATOR_H
|
||||
#define DECIMATOR_H
|
||||
|
||||
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
|
||||
* a multiple of decFactor. Output blocks will be of length
|
||||
* inLength / decFactor.
|
||||
*
|
||||
* decFactor must be a power of two. The highest supported factor
|
||||
* is obtained through getHighestSupportedFactor(); for higher
|
||||
* factors, you will need to chain more than one decimator.
|
||||
*/
|
||||
Decimator( unsigned int inLength, unsigned int decFactor );
|
||||
virtual ~Decimator();
|
||||
|
||||
int getFactor() const { return m_decFactor; }
|
||||
static int getHighestSupportedFactor() { return 8; }
|
||||
|
||||
private:
|
||||
void resetFilter();
|
||||
void deInitialise();
|
||||
void initialise( unsigned int inLength, unsigned int decFactor );
|
||||
void doAntiAlias( const double* src, double* dst, unsigned int length );
|
||||
void doAntiAlias( const float* src, double* dst, unsigned int length );
|
||||
|
||||
unsigned int m_inputLength;
|
||||
unsigned int m_outputLength;
|
||||
unsigned int m_decFactor;
|
||||
|
||||
double Input;
|
||||
double Output ;
|
||||
|
||||
double o1,o2,o3,o4,o5,o6,o7;
|
||||
|
||||
double a[ 9 ];
|
||||
double b[ 9 ];
|
||||
|
||||
double* decBuffer;
|
||||
};
|
||||
|
||||
#endif //
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- 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 2008 Kurt Jacobson and QMUL.
|
||||
|
||||
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 "BeatSpectrum.h"
|
||||
|
||||
#include "maths/CosineDistance.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
vector<double> BeatSpectrum::process(const vector<vector<double> > &m)
|
||||
{
|
||||
int origin = 0;
|
||||
int sz = m.size()/2;
|
||||
|
||||
int i, j, k;
|
||||
|
||||
vector<double> v(sz);
|
||||
for (i = 0; i < sz; ++i) v[i] = 0.0;
|
||||
|
||||
CosineDistance cd;
|
||||
|
||||
for (i = origin; i < origin + sz; ++i) {
|
||||
|
||||
k = 0;
|
||||
|
||||
for (j = i + 1; j < i + sz + 1; ++j) {
|
||||
|
||||
v[k++] += cd.distance(m[i], m[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// normalize
|
||||
|
||||
double max = 0.0;
|
||||
|
||||
for (i = 0; i < sz; ++i) {
|
||||
if (v[i] > max) max = v[i];
|
||||
}
|
||||
|
||||
if (max > 0.0) {
|
||||
for (i = 0; i < sz; ++i) {
|
||||
v[i] /= max;
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* -*- 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 2008 Kurt Jacobson and QMUL.
|
||||
|
||||
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 BEATSPECTRUM_H
|
||||
#define BEATSPECTRUM_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Given a matrix of "feature values", calculate a self-similarity
|
||||
* vector. The resulting vector will have half as many elements as
|
||||
* the number of columns in the matrix. This is based on the
|
||||
* SoundBite rhythmic similarity code.
|
||||
*/
|
||||
|
||||
class BeatSpectrum
|
||||
{
|
||||
public:
|
||||
BeatSpectrum() { }
|
||||
~BeatSpectrum() { }
|
||||
|
||||
std::vector<double> process(const std::vector<std::vector<double> > &inmatrix);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||||
|
||||
/*
|
||||
* ClusterMeltSegmenter.cpp
|
||||
*
|
||||
* Created by Mark Levy on 23/03/2006.
|
||||
* Copyright 2006 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 <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
#include "ClusterMeltSegmenter.h"
|
||||
#include "cluster_segmenter.h"
|
||||
#include "segment.h"
|
||||
|
||||
#include "dsp/transforms/FFT.h"
|
||||
#include "dsp/chromagram/ConstantQ.h"
|
||||
#include "dsp/rateconversion/Decimator.h"
|
||||
#include "dsp/mfcc/MFCC.h"
|
||||
|
||||
ClusterMeltSegmenter::ClusterMeltSegmenter(ClusterMeltSegmenterParams params) :
|
||||
window(NULL),
|
||||
fft(NULL),
|
||||
constq(NULL),
|
||||
mfcc(NULL),
|
||||
featureType(params.featureType),
|
||||
hopSize(params.hopSize),
|
||||
windowSize(params.windowSize),
|
||||
fmin(params.fmin),
|
||||
fmax(params.fmax),
|
||||
nbins(params.nbins),
|
||||
ncomponents(params.ncomponents), // NB currently not passed - no. of PCA components is set in cluser_segmenter.c
|
||||
nHMMStates(params.nHMMStates),
|
||||
nclusters(params.nclusters),
|
||||
histogramLength(params.histogramLength),
|
||||
neighbourhoodLimit(params.neighbourhoodLimit),
|
||||
decimator(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void ClusterMeltSegmenter::initialise(int fs)
|
||||
{
|
||||
samplerate = fs;
|
||||
|
||||
if (featureType == FEATURE_TYPE_CONSTQ ||
|
||||
featureType == FEATURE_TYPE_CHROMA) {
|
||||
|
||||
// run internal processing at 11025 or thereabouts
|
||||
int internalRate = 11025;
|
||||
int decimationFactor = samplerate / internalRate;
|
||||
if (decimationFactor < 1) decimationFactor = 1;
|
||||
|
||||
// must be a power of two
|
||||
while (decimationFactor & (decimationFactor - 1)) ++decimationFactor;
|
||||
|
||||
if (decimationFactor > Decimator::getHighestSupportedFactor()) {
|
||||
decimationFactor = Decimator::getHighestSupportedFactor();
|
||||
}
|
||||
|
||||
if (decimationFactor > 1) {
|
||||
decimator = new Decimator(getWindowsize(), decimationFactor);
|
||||
}
|
||||
|
||||
CQConfig config;
|
||||
config.FS = samplerate / decimationFactor;
|
||||
config.min = fmin;
|
||||
config.max = fmax;
|
||||
config.BPO = nbins;
|
||||
config.CQThresh = 0.0054;
|
||||
|
||||
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
|
||||
int internalRate = 22050;
|
||||
int decimationFactor = samplerate / internalRate;
|
||||
if (decimationFactor < 1) decimationFactor = 1;
|
||||
|
||||
// must be a power of two
|
||||
while (decimationFactor & (decimationFactor - 1)) ++decimationFactor;
|
||||
|
||||
if (decimationFactor > Decimator::getHighestSupportedFactor()) {
|
||||
decimationFactor = Decimator::getHighestSupportedFactor();
|
||||
}
|
||||
|
||||
if (decimationFactor > 1) {
|
||||
decimator = new Decimator(getWindowsize(), decimationFactor);
|
||||
}
|
||||
|
||||
MFCCConfig config(samplerate / decimationFactor);
|
||||
config.fftsize = 2048;
|
||||
config.nceps = 19;
|
||||
config.want_c0 = true;
|
||||
|
||||
mfcc = new MFCC(config);
|
||||
ncoeff = config.nceps + 1;
|
||||
}
|
||||
}
|
||||
|
||||
ClusterMeltSegmenter::~ClusterMeltSegmenter()
|
||||
{
|
||||
delete window;
|
||||
delete constq;
|
||||
delete decimator;
|
||||
delete fft;
|
||||
}
|
||||
|
||||
int
|
||||
ClusterMeltSegmenter::getWindowsize()
|
||||
{
|
||||
return static_cast<int>(windowSize * samplerate + 0.001);
|
||||
}
|
||||
|
||||
int
|
||||
ClusterMeltSegmenter::getHopsize()
|
||||
{
|
||||
return static_cast<int>(hopSize * samplerate + 0.001);
|
||||
}
|
||||
|
||||
void ClusterMeltSegmenter::extractFeatures(const double* samples, int nsamples)
|
||||
{
|
||||
if (featureType == FEATURE_TYPE_CONSTQ ||
|
||||
featureType == FEATURE_TYPE_CHROMA) {
|
||||
extractFeaturesConstQ(samples, nsamples);
|
||||
} else if (featureType == FEATURE_TYPE_MFCC) {
|
||||
extractFeaturesMFCC(samples, nsamples);
|
||||
}
|
||||
}
|
||||
|
||||
void ClusterMeltSegmenter::extractFeaturesConstQ(const double* samples, int nsamples)
|
||||
{
|
||||
if (!constq) {
|
||||
std::cerr << "ERROR: ClusterMeltSegmenter::extractFeaturesConstQ: "
|
||||
<< "No const-q: initialise not called?"
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (nsamples < getWindowsize()) {
|
||||
std::cerr << "ERROR: ClusterMeltSegmenter::extractFeatures: nsamples < windowsize (" << nsamples << " < " << getWindowsize() << ")" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
int fftsize = constq->getfftlength();
|
||||
|
||||
if (!window || window->getSize() != fftsize) {
|
||||
delete window;
|
||||
window = new Window<double>(HammingWindow, fftsize);
|
||||
}
|
||||
|
||||
vector<double> cq(ncoeff);
|
||||
|
||||
for (int i = 0; i < ncoeff; ++i) cq[i] = 0.0;
|
||||
|
||||
const double *psource = samples;
|
||||
int pcount = nsamples;
|
||||
|
||||
if (decimator) {
|
||||
pcount = nsamples / decimator->getFactor();
|
||||
double *decout = new double[pcount];
|
||||
decimator->process(samples, decout);
|
||||
psource = decout;
|
||||
}
|
||||
|
||||
int origin = 0;
|
||||
|
||||
// std::cerr << "nsamples = " << nsamples << ", pcount = " << pcount << std::endl;
|
||||
|
||||
int frames = 0;
|
||||
|
||||
double *frame = new double[fftsize];
|
||||
double *real = new double[fftsize];
|
||||
double *imag = new double[fftsize];
|
||||
double *cqre = new double[ncoeff];
|
||||
double *cqim = new double[ncoeff];
|
||||
|
||||
while (origin <= pcount) {
|
||||
|
||||
// always need at least one fft window per block, but after
|
||||
// that we want to avoid having any incomplete ones
|
||||
if (origin > 0 && origin + fftsize >= pcount) break;
|
||||
|
||||
for (int i = 0; i < fftsize; ++i) {
|
||||
if (origin + i < pcount) {
|
||||
frame[i] = psource[origin + i];
|
||||
} else {
|
||||
frame[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fftsize/2; ++i) {
|
||||
double value = frame[i];
|
||||
frame[i] = frame[i + fftsize/2];
|
||||
frame[i + fftsize/2] = value;
|
||||
}
|
||||
|
||||
window->cut(frame);
|
||||
|
||||
fft->process(false, 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]);
|
||||
}
|
||||
++frames;
|
||||
|
||||
origin += fftsize/2;
|
||||
}
|
||||
|
||||
delete [] cqre;
|
||||
delete [] cqim;
|
||||
delete [] real;
|
||||
delete [] imag;
|
||||
delete [] frame;
|
||||
|
||||
for (int i = 0; i < ncoeff; ++i) {
|
||||
cq[i] /= frames;
|
||||
}
|
||||
|
||||
if (decimator) delete[] psource;
|
||||
|
||||
features.push_back(cq);
|
||||
}
|
||||
|
||||
void ClusterMeltSegmenter::extractFeaturesMFCC(const double* samples, int nsamples)
|
||||
{
|
||||
if (!mfcc) {
|
||||
std::cerr << "ERROR: ClusterMeltSegmenter::extractFeaturesMFCC: "
|
||||
<< "No mfcc: initialise not called?"
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (nsamples < getWindowsize()) {
|
||||
std::cerr << "ERROR: ClusterMeltSegmenter::extractFeatures: nsamples < windowsize (" << nsamples << " < " << getWindowsize() << ")" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
int fftsize = mfcc->getfftlength();
|
||||
|
||||
vector<double> cc(ncoeff);
|
||||
|
||||
for (int i = 0; i < ncoeff; ++i) cc[i] = 0.0;
|
||||
|
||||
const double *psource = samples;
|
||||
int pcount = nsamples;
|
||||
|
||||
if (decimator) {
|
||||
pcount = nsamples / decimator->getFactor();
|
||||
double *decout = new double[pcount];
|
||||
decimator->process(samples, decout);
|
||||
psource = decout;
|
||||
}
|
||||
|
||||
int origin = 0;
|
||||
int frames = 0;
|
||||
|
||||
double *frame = new double[fftsize];
|
||||
double *ccout = new double[ncoeff];
|
||||
|
||||
while (origin <= pcount) {
|
||||
|
||||
// always need at least one fft window per block, but after
|
||||
// that we want to avoid having any incomplete ones
|
||||
if (origin > 0 && origin + fftsize >= pcount) break;
|
||||
|
||||
for (int i = 0; i < fftsize; ++i) {
|
||||
if (origin + i < pcount) {
|
||||
frame[i] = psource[origin + i];
|
||||
} else {
|
||||
frame[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
mfcc->process(frame, ccout);
|
||||
|
||||
for (int i = 0; i < ncoeff; ++i) {
|
||||
cc[i] += ccout[i];
|
||||
}
|
||||
++frames;
|
||||
|
||||
origin += fftsize/2;
|
||||
}
|
||||
|
||||
delete [] ccout;
|
||||
delete [] frame;
|
||||
|
||||
for (int i = 0; i < ncoeff; ++i) {
|
||||
cc[i] /= frames;
|
||||
}
|
||||
|
||||
if (decimator) delete[] psource;
|
||||
|
||||
features.push_back(cc);
|
||||
}
|
||||
|
||||
void ClusterMeltSegmenter::segment(int m)
|
||||
{
|
||||
nclusters = m;
|
||||
segment();
|
||||
}
|
||||
|
||||
void ClusterMeltSegmenter::setFeatures(const vector<vector<double> >& f)
|
||||
{
|
||||
features = f;
|
||||
featureType = FEATURE_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
void ClusterMeltSegmenter::segment()
|
||||
{
|
||||
delete constq;
|
||||
constq = 0;
|
||||
delete mfcc;
|
||||
mfcc = 0;
|
||||
delete decimator;
|
||||
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()];
|
||||
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];
|
||||
} 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];
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
nclusters, neighbourhoodLimit);
|
||||
else
|
||||
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());
|
||||
|
||||
// de-allocate arrays
|
||||
delete [] q;
|
||||
for (int i = 0; i < features.size(); i++)
|
||||
delete [] arrFeatures[i];
|
||||
delete [] arrFeatures;
|
||||
|
||||
// clear the features
|
||||
clear();
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
segment.end = i * getHopsize();
|
||||
segmentation.segments.push_back(segment);
|
||||
segment.type = q[i];
|
||||
segment.start = segment.end;
|
||||
}
|
||||
}
|
||||
segment.end = len * getHopsize();
|
||||
segmentation.segments.push_back(segment);
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||||
|
||||
/*
|
||||
* ClusterMeltSegmenter.h
|
||||
*
|
||||
* Created by Mark Levy on 23/03/2006.
|
||||
* Copyright 2006 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 <vector>
|
||||
|
||||
#include "segment.h"
|
||||
#include "Segmenter.h"
|
||||
#include "hmm/hmm.h"
|
||||
#include "base/Window.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
class Decimator;
|
||||
class ConstantQ;
|
||||
class MFCC;
|
||||
class FFTReal;
|
||||
|
||||
class ClusterMeltSegmenterParams
|
||||
// defaults are sensible for 11025Hz with 0.2 second hopsize
|
||||
{
|
||||
public:
|
||||
ClusterMeltSegmenterParams() :
|
||||
featureType(FEATURE_TYPE_CONSTQ),
|
||||
hopSize(0.2),
|
||||
windowSize(0.6),
|
||||
fmin(62),
|
||||
fmax(16000),
|
||||
nbins(8),
|
||||
ncomponents(20),
|
||||
nHMMStates(40),
|
||||
nclusters(10),
|
||||
histogramLength(15),
|
||||
neighbourhoodLimit(20) { }
|
||||
feature_types featureType;
|
||||
double hopSize; // in secs
|
||||
double windowSize; // in secs
|
||||
int fmin;
|
||||
int fmax;
|
||||
int nbins;
|
||||
int ncomponents;
|
||||
int nHMMStates;
|
||||
int nclusters;
|
||||
int histogramLength;
|
||||
int neighbourhoodLimit;
|
||||
};
|
||||
|
||||
class ClusterMeltSegmenter : public Segmenter
|
||||
{
|
||||
public:
|
||||
ClusterMeltSegmenter(ClusterMeltSegmenterParams params);
|
||||
virtual ~ClusterMeltSegmenter();
|
||||
virtual void initialise(int samplerate);
|
||||
virtual int getWindowsize();
|
||||
virtual int getHopsize();
|
||||
virtual void extractFeatures(const double* samples, int nsamples);
|
||||
void setFeatures(const vector<vector<double> >& f); // provide the features yourself
|
||||
virtual void segment(); // segment into default number of segment-types
|
||||
void segment(int m); // segment into m segment-types
|
||||
int getNSegmentTypes() { return nclusters; }
|
||||
|
||||
protected:
|
||||
void makeSegmentation(int* q, int len);
|
||||
|
||||
void extractFeaturesConstQ(const double *, int);
|
||||
void extractFeaturesMFCC(const double *, int);
|
||||
|
||||
Window<double> *window;
|
||||
FFTReal *fft;
|
||||
ConstantQ* constq;
|
||||
MFCC* mfcc;
|
||||
model_t* model; // the HMM
|
||||
int* q; // the decoded HMM state sequence
|
||||
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;
|
||||
int neighbourhoodLimit;
|
||||
|
||||
Decimator *decimator;
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Segmenter.cpp
|
||||
*
|
||||
* Created by Mark Levy on 04/04/2006.
|
||||
* Copyright 2006 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 <iomanip>
|
||||
|
||||
#include "Segmenter.h"
|
||||
|
||||
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)
|
||||
<< '\t' << std::setprecision(6) << seg.end / static_cast<double>(s.samplerate) << "\n";
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef _SEGMENTER_H
|
||||
#define _SEGMENTER_H
|
||||
|
||||
/*
|
||||
* Segmenter.h
|
||||
* soundbite
|
||||
*
|
||||
* Created by Mark Levy on 23/03/2006.
|
||||
* Copyright 2006 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 <vector>
|
||||
#include <iostream>
|
||||
|
||||
using std::vector;
|
||||
using std::ostream;
|
||||
|
||||
class Segment
|
||||
{
|
||||
public:
|
||||
int start; // in samples
|
||||
int end;
|
||||
int type;
|
||||
};
|
||||
|
||||
class Segmentation
|
||||
{
|
||||
public:
|
||||
int nsegtypes; // number of segment types, so possible types are {0,1,...,nsegtypes-1}
|
||||
int samplerate;
|
||||
vector<Segment> segments;
|
||||
};
|
||||
|
||||
ostream& operator<<(ostream& os, const Segmentation& s);
|
||||
|
||||
class Segmenter
|
||||
{
|
||||
public:
|
||||
Segmenter() {}
|
||||
virtual ~Segmenter() {}
|
||||
virtual void initialise(int samplerate) = 0; // must be called before any other methods
|
||||
virtual int getWindowsize() = 0; // required window size for calls to extractFeatures()
|
||||
virtual int getHopsize() = 0; // required hop size for calls to extractFeatures()
|
||||
virtual void extractFeatures(const double* samples, int nsamples) = 0;
|
||||
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; }
|
||||
protected:
|
||||
vector<vector<double> > features;
|
||||
Segmentation segmentation;
|
||||
int samplerate;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* cluster.c
|
||||
* cluster_melt
|
||||
*
|
||||
* Created by Mark Levy on 21/02/2006.
|
||||
* Copyright 2006 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 <stdlib.h>
|
||||
|
||||
#include "cluster_melt.h"
|
||||
|
||||
#define DEFAULT_LAMBDA 0.02;
|
||||
#define DEFAULT_LIMIT 20;
|
||||
|
||||
double kldist(double* a, double* b, int n) {
|
||||
/* NB assume that all a[i], b[i] are non-negative
|
||||
because a, b represent probability distributions */
|
||||
double q, d;
|
||||
int i;
|
||||
|
||||
d = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
q = (a[i] + b[i]) / 2.0;
|
||||
if (q > 0)
|
||||
{
|
||||
if (a[i] > 0)
|
||||
d += a[i] * log(a[i] / q);
|
||||
if (b[i] > 0)
|
||||
d += b[i] * log(b[i] / q);
|
||||
}
|
||||
}
|
||||
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;
|
||||
int i, j, a, b, b0, b1, limit, B, it, maxiter, maxiter0, maxiter1;
|
||||
double** cl; /* reference histograms for each cluster */
|
||||
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)
|
||||
limit = l;
|
||||
else
|
||||
limit = DEFAULT_LIMIT; /* use default if no valid neighbourhood limit supplied */
|
||||
B = 2 * limit + 1;
|
||||
maxiter0 = 20; /* number of iterations at initial temperature */
|
||||
maxiter1 = 5; /* number of iterations at subsequent temperatures */
|
||||
|
||||
/* 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++)
|
||||
{
|
||||
sum = 0;
|
||||
for (j = 0; j < m; j++)
|
||||
{
|
||||
cl[i][j] = rand(); /* random initial reference histograms */
|
||||
sum += cl[i][j] * cl[i][j];
|
||||
}
|
||||
sum = sqrt(sum);
|
||||
for (j = 0; j < m; j++)
|
||||
{
|
||||
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;
|
||||
if (i >= limit && i <= n - 1 - limit)
|
||||
{
|
||||
for (b = i - limit; b <= i + limit; b++)
|
||||
{
|
||||
if (c[b] == j+1)
|
||||
nc[i][j]++;
|
||||
}
|
||||
nc[i][j] = B - nc[i][j];
|
||||
}
|
||||
*/
|
||||
b0 = i - limit;
|
||||
if (b0 < 0)
|
||||
b0 = 0;
|
||||
b1 = i + limit;
|
||||
if (b1 >= n)
|
||||
b1 = n - 1;
|
||||
nc[i][j] = b1 - b0 + 1; /* = B except at edges */
|
||||
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];
|
||||
}
|
||||
//print_array(lp, n, k);
|
||||
/*
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
maxlp = lp[i][0];
|
||||
c[i] = 1;
|
||||
for (j = 1; j < k; j++)
|
||||
if (lp[i][j] > maxlp)
|
||||
{
|
||||
maxlp = lp[i][j];
|
||||
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++)
|
||||
{
|
||||
for (b = 0; b < m; b++)
|
||||
{
|
||||
cl[j][b] = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
cl[j][b] += exp(lp[i][j]) * h[i*m+b];
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
free(cl);
|
||||
for (i = 0; i < n; i++)
|
||||
free(nc[i]);
|
||||
free(nc);
|
||||
for (i = 0; i < n; i++)
|
||||
free(lp[i]);
|
||||
free(lp);
|
||||
free(oldc);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef _CLUSTER_MELT_H
|
||||
#define _CLUSTER_MELT_H
|
||||
/*
|
||||
* cluster_melt.h
|
||||
* cluster_melt
|
||||
*
|
||||
* Created by Mark Levy on 21/02/2006.
|
||||
* Copyright 2006 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 <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void cluster_melt(double *h, /* normalised histograms, as a vector in row major order */
|
||||
int m, /* number of dimensions (i.e. histogram bins) */
|
||||
int n, /* number of histograms */
|
||||
double *Bsched, /* inverse temperature schedule */
|
||||
int t, /* length of schedule */
|
||||
int k, /* number of clusters */
|
||||
int l, /* neighbourhood limit (supply zero to use default value) */
|
||||
int *c /* sequence of cluster assignments */
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* cluster_segmenter.c
|
||||
* soundbite
|
||||
*
|
||||
* Created by Mark Levy on 06/04/2006.
|
||||
* Copyright 2006 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 "cluster_segmenter.h"
|
||||
|
||||
extern int readmatarray_size(const char *filepath, int n_array, int* t, int* d);
|
||||
extern int readmatarray(const char *filepath, int n_array, int t, int d, double** arr);
|
||||
|
||||
/* converts constant-Q features to normalised chroma */
|
||||
void cq2chroma(double** cq, int nframes, int ncoeff, int bins, double** chroma)
|
||||
{
|
||||
int noct = ncoeff / bins; /* number of complete octaves in constant-Q */
|
||||
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++)
|
||||
chroma[t][b] = 0;
|
||||
for (oct = 0; oct < noct; oct++)
|
||||
{
|
||||
ix = oct * bins;
|
||||
for (b = 0; b < bins; b++)
|
||||
chroma[t][b] += fabs(cq[t][ix+b]);
|
||||
}
|
||||
/* normalise to unit sum
|
||||
sum = 0;
|
||||
for (b = 0; b < bins; b++)
|
||||
sum += chroma[t][b];
|
||||
for (b = 0; b < bins; b++)
|
||||
chroma[t][b] /= sum;
|
||||
*/
|
||||
/* normalise to unit max - NO this made results much worse!
|
||||
maxchroma = 0;
|
||||
for (b = 0; b < bins; b++)
|
||||
if (chroma[t][b] > maxchroma)
|
||||
maxchroma = chroma[t][b];
|
||||
if (maxchroma > 0)
|
||||
for (b = 0; b < bins; b++)
|
||||
chroma[t][b] /= maxchroma;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/* applies MPEG-7 normalisation to constant-Q features, storing normalised envelope (norm) in last feature dimension */
|
||||
void mpeg7_constq(double** features, int nframes, int ncoeff)
|
||||
{
|
||||
int i, j;
|
||||
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 */
|
||||
for (i = 0; i < nframes; i++)
|
||||
{
|
||||
ss = 0;
|
||||
for (j = 0; j < ncoeff; j++)
|
||||
ss += features[i][j] * features[i][j];
|
||||
env = sqrt(ss);
|
||||
for (j = 0; j < ncoeff; j++)
|
||||
features[i][j] /= env;
|
||||
features[i][ncoeff] = env;
|
||||
if (env > maxenv)
|
||||
maxenv = env;
|
||||
}
|
||||
/* normalise the envelopes */
|
||||
for (i = 0; i < nframes; i++)
|
||||
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) */
|
||||
/* NB h is a vector in row major order, as required by cluster_melt() */
|
||||
/* for historical reasons we normalise the histograms by their norm (not to sum to one) */
|
||||
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++)
|
||||
h[i] = 0;
|
||||
|
||||
for (i = hlen/2; i < nx-hlen/2; i++)
|
||||
{
|
||||
for (j = 0; j < m; j++)
|
||||
h[i*m+j] = 0;
|
||||
for (t = i-hlen/2; t <= i+hlen/2; t++)
|
||||
++h[i*m+x[t]];
|
||||
norm = 0;
|
||||
for (j = 0; j < m; j++)
|
||||
norm += h[i*m+j] * h[i*m+j];
|
||||
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++)
|
||||
h[i*m+j] = h[hlen/2*m+j];
|
||||
for (i = nx-hlen/2; i < nx; i++)
|
||||
for (j = 0; j < m; j++)
|
||||
h[i*m+j] = h[(nx-hlen/2-1)*m+j];
|
||||
}
|
||||
|
||||
/* segment using HMM and then histogram clustering */
|
||||
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' */
|
||||
nHMM_states = feature_length + 1; /* allow a 'zero' state */
|
||||
double chroma_thresh = 0.05;
|
||||
double maxval;
|
||||
int maxbin;
|
||||
for (i = 0; i < frames_read; i++)
|
||||
{
|
||||
maxval = 0;
|
||||
for (j = 0; j < feature_length; j++)
|
||||
{
|
||||
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);
|
||||
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 */
|
||||
double b0 = 100;
|
||||
double alpha = 0.7;
|
||||
bsched[0] = b0;
|
||||
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(bsched);
|
||||
}
|
||||
|
||||
/* segment constant-Q or chroma features */
|
||||
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];
|
||||
|
||||
feature_length = ncomponents + 1;
|
||||
|
||||
/**************************************
|
||||
//TEST
|
||||
// feature file name
|
||||
char* dir = "/Users/mark/documents/semma/audio/";
|
||||
char* file_name = (char*) malloc((strlen(dir) + strlen(trackname) + strlen("_features_c20r8h0.2f0.6.mat") + 1)*sizeof(char));
|
||||
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);
|
||||
readmatarray(file_name, 2, frames_in_file, feature_length, features);
|
||||
// copy final frame to ensure that we get as many as we expected
|
||||
int missing_frames = frames_read - frames_in_file;
|
||||
while (missing_frames > 0)
|
||||
{
|
||||
for (i = 0; i < feature_length; i++)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef _CLUSTER_SEGMENTER_H
|
||||
#define _CLUSTER_SEGMENTER_H
|
||||
|
||||
/*
|
||||
* cluster_segmenter.h
|
||||
* soundbite
|
||||
*
|
||||
* Created by Mark Levy on 06/04/2006.
|
||||
* Copyright 2006 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#include "segment.h"
|
||||
#include "cluster_melt.h"
|
||||
#include "hmm/hmm.h"
|
||||
#include "maths/pca/pca.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* applies MPEG-7 normalisation to constant-Q features, storing normalised envelope (norm) in last feature dimension */
|
||||
void mpeg7_constq(double** features, int nframes, int ncoeff);
|
||||
|
||||
/* converts constant-Q features to normalised chroma */
|
||||
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,
|
||||
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,
|
||||
int nHMM_states, int histogram_length, int nclusters, int neighbour_limit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef _SEGMENT_H
|
||||
#define _SEGMENT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* segment.h
|
||||
*
|
||||
* Created by Mark Levy on 06/04/2006.
|
||||
* Copyright 2006 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.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct segment_t
|
||||
{
|
||||
long start; /* in samples */
|
||||
long end;
|
||||
int type;
|
||||
} segment_t;
|
||||
|
||||
typedef struct segmentation_t
|
||||
{
|
||||
int nsegs; /* number of segments */
|
||||
int nsegtypes; /* number of segment types, so possible types are {0,1,...,nsegtypes-1} */
|
||||
int samplerate;
|
||||
segment_t* segments;
|
||||
} segmentation_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FEATURE_TYPE_UNKNOWN = 0,
|
||||
FEATURE_TYPE_CONSTQ = 1,
|
||||
FEATURE_TYPE_CHROMA = 2,
|
||||
FEATURE_TYPE_MFCC = 3
|
||||
} feature_types;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "DFProcess.h"
|
||||
#include "maths/MathUtilities.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
DFProcess::DFProcess( DFProcConfig Config )
|
||||
{
|
||||
filtSrc = NULL;
|
||||
filtDst = NULL;
|
||||
m_filtScratchIn = NULL;
|
||||
m_filtScratchOut = NULL;
|
||||
|
||||
m_FFOrd = 0;
|
||||
|
||||
initialise( Config );
|
||||
}
|
||||
|
||||
DFProcess::~DFProcess()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
void DFProcess::initialise( DFProcConfig Config )
|
||||
{
|
||||
m_length = Config.length;
|
||||
m_winPre = Config.winPre;
|
||||
m_winPost = Config.winPost;
|
||||
m_alphaNormParam = Config.AlphaNormParam;
|
||||
|
||||
m_isMedianPositive = Config.isMedianPositive;
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
void DFProcess::deInitialise()
|
||||
{
|
||||
delete [] filtSrc;
|
||||
|
||||
delete [] filtDst;
|
||||
|
||||
delete [] m_filtScratchIn;
|
||||
|
||||
delete [] m_filtScratchOut;
|
||||
|
||||
delete m_FiltFilt;
|
||||
}
|
||||
|
||||
void DFProcess::process(double *src, double* dst)
|
||||
{
|
||||
if (m_length == 0) return;
|
||||
|
||||
removeDCNormalize( src, filtSrc );
|
||||
|
||||
m_FiltFilt->process( filtSrc, filtDst, m_length );
|
||||
|
||||
medianFilter( filtDst, dst );
|
||||
}
|
||||
|
||||
|
||||
void DFProcess::medianFilter(double *src, double *dst)
|
||||
{
|
||||
int i,k,j,l;
|
||||
int index = 0;
|
||||
|
||||
double val = 0;
|
||||
|
||||
double* y = new double[ m_winPost + m_winPre + 1];
|
||||
memset( y, 0, sizeof( double ) * ( m_winPost + m_winPre + 1) );
|
||||
|
||||
double* scratch = new double[ m_length ];
|
||||
|
||||
for( i = 0; i < m_winPre; i++)
|
||||
{
|
||||
if (index >= m_length) break;
|
||||
|
||||
k = i + m_winPost + 1;
|
||||
|
||||
for( j = 0; j < k; j++)
|
||||
{
|
||||
y[ j ] = src[ j ];
|
||||
}
|
||||
scratch[ index ] = MathUtilities::median( y, k );
|
||||
index++;
|
||||
}
|
||||
|
||||
for( i = 0; i + m_winPost + m_winPre < m_length; i ++)
|
||||
{
|
||||
if (index >= m_length) break;
|
||||
|
||||
|
||||
l = 0;
|
||||
for( j = i; j < ( i + m_winPost + m_winPre + 1); j++)
|
||||
{
|
||||
y[ l ] = src[ j ];
|
||||
l++;
|
||||
}
|
||||
|
||||
scratch[ index++ ] = MathUtilities::median( y, (m_winPost + m_winPre + 1 ));
|
||||
}
|
||||
|
||||
for( i = std::max( m_length - m_winPost, 1); i < m_length; i++)
|
||||
{
|
||||
if (index >= m_length) break;
|
||||
|
||||
k = std::max( i - m_winPre, 1);
|
||||
|
||||
l = 0;
|
||||
for( j = k; j < m_length; j++)
|
||||
{
|
||||
y[ l ] = src[ j ];
|
||||
|
||||
l++;
|
||||
}
|
||||
|
||||
scratch[ index++ ] = MathUtilities::median( y, l);
|
||||
}
|
||||
|
||||
|
||||
for( i = 0; i < m_length; i++ )
|
||||
{
|
||||
val = src[ i ] - scratch[ i ];// - 0.033;
|
||||
|
||||
if( m_isMedianPositive )
|
||||
{
|
||||
if( val > 0 )
|
||||
{
|
||||
dst[ i ] = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[ i ] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[ i ] = val;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] y;
|
||||
delete [] scratch;
|
||||
}
|
||||
|
||||
|
||||
void DFProcess::removeDCNormalize( double *src, double*dst )
|
||||
{
|
||||
double DFmax = 0;
|
||||
double DFMin = 0;
|
||||
double DFAlphaNorm = 0;
|
||||
|
||||
MathUtilities::getFrameMinMax( src, m_length, &DFMin, &DFmax );
|
||||
|
||||
MathUtilities::getAlphaNorm( src, m_length, m_alphaNormParam, &DFAlphaNorm );
|
||||
|
||||
for( unsigned int i = 0; i< m_length; i++)
|
||||
{
|
||||
dst[ i ] = ( src[ i ] - DFMin ) / DFAlphaNorm;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef CDFPROCESS_H
|
||||
#define CDFPROCESS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "FiltFilt.h"
|
||||
|
||||
struct DFProcConfig{
|
||||
unsigned int length;
|
||||
unsigned int LPOrd;
|
||||
double *LPACoeffs;
|
||||
double *LPBCoeffs;
|
||||
unsigned int winPre;
|
||||
unsigned int winPost;
|
||||
double AlphaNormParam;
|
||||
bool isMedianPositive;
|
||||
};
|
||||
|
||||
class DFProcess
|
||||
{
|
||||
public:
|
||||
DFProcess( DFProcConfig Config );
|
||||
virtual ~DFProcess();
|
||||
|
||||
void process( double* src, double* dst );
|
||||
|
||||
|
||||
private:
|
||||
void initialise( DFProcConfig Config );
|
||||
void deInitialise();
|
||||
void removeDCNormalize( double *src, double*dst );
|
||||
void medianFilter( double* src, double* dst );
|
||||
|
||||
int m_length;
|
||||
int m_FFOrd;
|
||||
|
||||
int m_winPre;
|
||||
int m_winPost;
|
||||
|
||||
double m_alphaNormParam;
|
||||
|
||||
double* filtSrc;
|
||||
double* filtDst;
|
||||
|
||||
double* m_filtScratchIn;
|
||||
double* m_filtScratchOut;
|
||||
|
||||
FiltFiltConfig m_FilterConfigParams;
|
||||
|
||||
FiltFilt* m_FiltFilt;
|
||||
|
||||
bool m_isMedianPositive;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,130 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "FiltFilt.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
FiltFilt::FiltFilt( FiltFiltConfig Config )
|
||||
{
|
||||
m_filtScratchIn = NULL;
|
||||
m_filtScratchOut = NULL;
|
||||
m_ord = 0;
|
||||
|
||||
initialise( Config );
|
||||
}
|
||||
|
||||
FiltFilt::~FiltFilt()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
void FiltFilt::initialise( FiltFiltConfig 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 );
|
||||
}
|
||||
|
||||
void FiltFilt::deInitialise()
|
||||
{
|
||||
delete m_filter;
|
||||
}
|
||||
|
||||
|
||||
void FiltFilt::process(double *src, double *dst, unsigned int length)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (length == 0) return;
|
||||
|
||||
unsigned int nFilt = m_ord + 1;
|
||||
unsigned int nFact = 3 * ( nFilt - 1);
|
||||
unsigned int nExt = length + 2 * nFact;
|
||||
|
||||
m_filtScratchIn = new double[ nExt ];
|
||||
m_filtScratchOut = new double[ nExt ];
|
||||
|
||||
|
||||
for( i = 0; i< nExt; i++ )
|
||||
{
|
||||
m_filtScratchIn[ i ] = 0.0;
|
||||
m_filtScratchOut[ i ] = 0.0;
|
||||
}
|
||||
|
||||
// Edge transients reflection
|
||||
double sample0 = 2 * src[ 0 ];
|
||||
double sampleN = 2 * src[ length - 1 ];
|
||||
|
||||
unsigned int index = 0;
|
||||
for( i = nFact; i > 0; i-- )
|
||||
{
|
||||
m_filtScratchIn[ index++ ] = sample0 - src[ i ];
|
||||
}
|
||||
index = 0;
|
||||
for( i = 0; i < nFact; i++ )
|
||||
{
|
||||
m_filtScratchIn[ (nExt - nFact) + index++ ] = sampleN - src[ (length - 2) - i ];
|
||||
}
|
||||
|
||||
index = 0;
|
||||
for( i = 0; i < length; i++ )
|
||||
{
|
||||
m_filtScratchIn[ i + nFact ] = src[ i ];
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Do 0Ph filtering
|
||||
m_filter->process( m_filtScratchIn, m_filtScratchOut, nExt);
|
||||
|
||||
// reverse the series for FILTFILT
|
||||
for ( i = 0; i < nExt; i++)
|
||||
{
|
||||
m_filtScratchIn[ i ] = m_filtScratchOut[ nExt - i - 1];
|
||||
}
|
||||
|
||||
// do FILTER again
|
||||
m_filter->process( m_filtScratchIn, m_filtScratchOut, nExt);
|
||||
|
||||
// reverse the series back
|
||||
for ( i = 0; i < nExt; i++)
|
||||
{
|
||||
m_filtScratchIn[ i ] = m_filtScratchOut[ nExt - i - 1 ];
|
||||
}
|
||||
for ( i = 0;i < nExt; i++)
|
||||
{
|
||||
m_filtScratchOut[ i ] = m_filtScratchIn[ i ];
|
||||
}
|
||||
|
||||
index = 0;
|
||||
for( i = 0; i < length; i++ )
|
||||
{
|
||||
dst[ index++ ] = m_filtScratchOut[ i + nFact ];
|
||||
}
|
||||
|
||||
delete [] m_filtScratchIn;
|
||||
delete [] m_filtScratchOut;
|
||||
|
||||
}
|
||||
|
||||
void FiltFilt::reset()
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef FILTFILT_H
|
||||
#define FILTFILT_H
|
||||
|
||||
#include "Filter.h"
|
||||
|
||||
struct FiltFiltConfig{
|
||||
unsigned int ord;
|
||||
double* ACoeffs;
|
||||
double* BCoeffs;
|
||||
};
|
||||
|
||||
class FiltFilt
|
||||
{
|
||||
public:
|
||||
FiltFilt( FiltFiltConfig Config );
|
||||
virtual ~FiltFilt();
|
||||
|
||||
void reset();
|
||||
void process( double* src, double* dst, unsigned int length );
|
||||
|
||||
private:
|
||||
void initialise( FiltFiltConfig Config );
|
||||
void deInitialise();
|
||||
|
||||
unsigned int m_ord;
|
||||
|
||||
Filter* m_filter;
|
||||
|
||||
double* m_filtScratchIn;
|
||||
double* m_filtScratchOut;
|
||||
|
||||
FilterConfig m_filterConfig;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "Filter.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Filter::Filter( FilterConfig Config )
|
||||
{
|
||||
m_ord = 0;
|
||||
m_outBuffer = NULL;
|
||||
m_inBuffer = NULL;
|
||||
|
||||
initialise( Config );
|
||||
}
|
||||
|
||||
Filter::~Filter()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
void Filter::initialise( FilterConfig Config )
|
||||
{
|
||||
m_ord = Config.ord;
|
||||
m_ACoeffs = Config.ACoeffs;
|
||||
m_BCoeffs = Config.BCoeffs;
|
||||
|
||||
m_inBuffer = new double[ m_ord + 1 ];
|
||||
m_outBuffer = new double[ m_ord + 1 ];
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void Filter::deInitialise()
|
||||
{
|
||||
delete[] m_inBuffer;
|
||||
delete[] m_outBuffer;
|
||||
}
|
||||
|
||||
void Filter::reset()
|
||||
{
|
||||
for( unsigned int i = 0; i < m_ord+1; i++ ){ m_inBuffer[ i ] = 0.0; }
|
||||
for(unsigned int i = 0; i < m_ord+1; i++ ){ m_outBuffer[ i ] = 0.0; }
|
||||
}
|
||||
|
||||
void Filter::process( double *src, double *dst, unsigned int length )
|
||||
{
|
||||
unsigned int SP,i,j;
|
||||
|
||||
double xin,xout;
|
||||
|
||||
for (SP=0;SP<length;SP++)
|
||||
{
|
||||
xin=src[SP];
|
||||
/* move buffer */
|
||||
for ( i = 0; i < m_ord; i++) {m_inBuffer[ m_ord - i ]=m_inBuffer[ m_ord - i - 1 ];}
|
||||
m_inBuffer[0]=xin;
|
||||
|
||||
xout=0.0;
|
||||
for (j=0;j< m_ord + 1; j++)
|
||||
xout = xout + m_BCoeffs[ j ] * m_inBuffer[ j ];
|
||||
for (j = 0; j < m_ord; j++)
|
||||
xout= xout - m_ACoeffs[ j + 1 ] * m_outBuffer[ j ];
|
||||
|
||||
dst[ SP ] = xout;
|
||||
for ( i = 0; i < m_ord - 1; i++ ) { m_outBuffer[ m_ord - i - 1 ] = m_outBuffer[ m_ord - i - 2 ];}
|
||||
m_outBuffer[0]=xout;
|
||||
|
||||
} /* end of SP loop */
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef FILTER_H
|
||||
#define FILTER_H
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
struct FilterConfig{
|
||||
unsigned int ord;
|
||||
double* ACoeffs;
|
||||
double* BCoeffs;
|
||||
};
|
||||
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
Filter( FilterConfig Config );
|
||||
virtual ~Filter();
|
||||
|
||||
void reset();
|
||||
|
||||
void process( double *src, double *dst, unsigned int length );
|
||||
|
||||
|
||||
private:
|
||||
void initialise( FilterConfig Config );
|
||||
void deInitialise();
|
||||
|
||||
unsigned int m_ord;
|
||||
|
||||
double* m_inBuffer;
|
||||
double* m_outBuffer;
|
||||
|
||||
double* m_ACoeffs;
|
||||
double* m_BCoeffs;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,109 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "Framer.h"
|
||||
#include <math.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Framer::Framer()
|
||||
{
|
||||
m_dataFrame = NULL;
|
||||
m_strideFrame = NULL;
|
||||
}
|
||||
|
||||
Framer::~Framer()
|
||||
{
|
||||
if( m_dataFrame != NULL )
|
||||
delete [] m_dataFrame;
|
||||
|
||||
if( m_strideFrame != NULL )
|
||||
delete [] m_strideFrame;
|
||||
}
|
||||
|
||||
void Framer::configure( unsigned int frameLength, unsigned int hop )
|
||||
{
|
||||
m_frameLength = frameLength;
|
||||
m_stepSize = hop;
|
||||
|
||||
resetCounters();
|
||||
|
||||
if( m_dataFrame != NULL )
|
||||
{
|
||||
delete [] m_dataFrame;
|
||||
m_dataFrame = NULL;
|
||||
}
|
||||
m_dataFrame = new double[ m_frameLength ];
|
||||
|
||||
if( m_strideFrame != NULL )
|
||||
{
|
||||
delete [] m_strideFrame;
|
||||
m_strideFrame = NULL;
|
||||
}
|
||||
m_strideFrame = new double[ m_stepSize ];
|
||||
}
|
||||
|
||||
void Framer::getFrame(double *dst)
|
||||
{
|
||||
|
||||
if( (m_ulSrcIndex + ( m_frameLength) ) < m_ulSampleLen )
|
||||
{
|
||||
for( unsigned int u = 0; u < m_frameLength; u++)
|
||||
{
|
||||
dst[ u ] = m_srcBuffer[ m_ulSrcIndex++ ];
|
||||
}
|
||||
m_ulSrcIndex -= ( m_frameLength - m_stepSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int rem = (m_ulSampleLen - m_ulSrcIndex );
|
||||
unsigned int zero = m_frameLength - rem;
|
||||
|
||||
for( unsigned int u = 0; u < rem; u++ )
|
||||
{
|
||||
dst[ u ] = m_srcBuffer[ m_ulSrcIndex++ ];
|
||||
}
|
||||
|
||||
for( unsigned int u = 0; u < zero; u++ )
|
||||
{
|
||||
dst[ rem + u ] = 0;
|
||||
}
|
||||
|
||||
m_ulSrcIndex -= (( rem - m_stepSize ) );
|
||||
}
|
||||
|
||||
m_framesRead++;
|
||||
}
|
||||
|
||||
void Framer::resetCounters()
|
||||
{
|
||||
m_framesRead = 0;
|
||||
m_ulSrcIndex = 0;
|
||||
}
|
||||
|
||||
unsigned int Framer::getMaxNoFrames()
|
||||
{
|
||||
return m_maxFrames;
|
||||
}
|
||||
|
||||
void Framer::setSource(double *src, unsigned int length)
|
||||
{
|
||||
m_srcBuffer = src;
|
||||
m_ulSampleLen = length;
|
||||
|
||||
m_maxFrames = (unsigned int)ceil( (double)m_ulSampleLen/(double)m_stepSize ) ;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef FRAMER_H
|
||||
#define FRAMER_H
|
||||
|
||||
//#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
class Framer
|
||||
{
|
||||
public:
|
||||
void setSource( double* src, unsigned int length );
|
||||
unsigned int getMaxNoFrames();
|
||||
void getFrame( double* dst );
|
||||
void configure( unsigned int frameLength, unsigned int hop );
|
||||
Framer();
|
||||
virtual ~Framer();
|
||||
|
||||
void resetCounters();
|
||||
|
||||
private:
|
||||
|
||||
unsigned long m_ulSampleLen; // DataLength (samples)
|
||||
unsigned int m_framesRead; // Read Frames Index
|
||||
|
||||
double* m_srcBuffer;
|
||||
double* m_dataFrame; // Analysis Frame Buffer
|
||||
double* m_strideFrame; // Stride Frame Buffer
|
||||
unsigned int m_frameLength; // Analysis Frame Length
|
||||
unsigned int m_stepSize; // Analysis Frame Stride
|
||||
|
||||
unsigned int m_maxFrames;
|
||||
|
||||
unsigned long m_ulSrcIndex;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,308 @@
|
|||
/* -*- 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 2008-2009 Matthew Davies and QMUL.
|
||||
|
||||
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 "DownBeat.h"
|
||||
|
||||
#include "maths/MathAliases.h"
|
||||
#include "maths/MathUtilities.h"
|
||||
#include "maths/KLDivergence.h"
|
||||
#include "dsp/transforms/FFT.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
DownBeat::DownBeat(float originalSampleRate,
|
||||
size_t decimationFactor,
|
||||
size_t dfIncrement) :
|
||||
m_bpb(0),
|
||||
m_rate(originalSampleRate),
|
||||
m_factor(decimationFactor),
|
||||
m_increment(dfIncrement),
|
||||
m_decimator1(0),
|
||||
m_decimator2(0),
|
||||
m_buffer(0),
|
||||
m_decbuf(0),
|
||||
m_bufsiz(0),
|
||||
m_buffill(0),
|
||||
m_beatframesize(0),
|
||||
m_beatframe(0)
|
||||
{
|
||||
// beat frame size is next power of two up from 1.3 seconds at the
|
||||
// downsampled rate (happens to produce 4096 for 44100 or 48000 at
|
||||
// 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;
|
||||
m_beatframe = new double[m_beatframesize];
|
||||
m_fftRealOut = new double[m_beatframesize];
|
||||
m_fftImagOut = new double[m_beatframesize];
|
||||
m_fft = new FFTReal(m_beatframesize);
|
||||
}
|
||||
|
||||
DownBeat::~DownBeat()
|
||||
{
|
||||
delete m_decimator1;
|
||||
delete m_decimator2;
|
||||
if (m_buffer) free(m_buffer);
|
||||
delete[] m_decbuf;
|
||||
delete[] m_beatframe;
|
||||
delete[] m_fftRealOut;
|
||||
delete[] m_fftImagOut;
|
||||
delete m_fft;
|
||||
}
|
||||
|
||||
void
|
||||
DownBeat::setBeatsPerBar(int bpb)
|
||||
{
|
||||
m_bpb = bpb;
|
||||
}
|
||||
|
||||
void
|
||||
DownBeat::makeDecimators()
|
||||
{
|
||||
// std::cerr << "m_factor = " << m_factor << std::endl;
|
||||
if (m_factor < 2) return;
|
||||
size_t highest = Decimator::getHighestSupportedFactor();
|
||||
if (m_factor <= highest) {
|
||||
m_decimator1 = new Decimator(m_increment, m_factor);
|
||||
// std::cerr << "DownBeat: decimator 1 factor " << m_factor << ", size " << m_increment << std::endl;
|
||||
return;
|
||||
}
|
||||
m_decimator1 = new Decimator(m_increment, highest);
|
||||
// std::cerr << "DownBeat: decimator 1 factor " << highest << ", size " << m_increment << std::endl;
|
||||
m_decimator2 = new Decimator(m_increment / highest, m_factor / highest);
|
||||
// std::cerr << "DownBeat: decimator 2 factor " << m_factor / highest << ", size " << m_increment / highest << std::endl;
|
||||
m_decbuf = new float[m_increment / highest];
|
||||
}
|
||||
|
||||
void
|
||||
DownBeat::pushAudioBlock(const float *audio)
|
||||
{
|
||||
if (m_buffill + (m_increment / m_factor) > m_bufsiz) {
|
||||
if (m_bufsiz == 0) m_bufsiz = m_increment * 16;
|
||||
else m_bufsiz = m_bufsiz * 2;
|
||||
if (!m_buffer) {
|
||||
m_buffer = (float *)malloc(m_bufsiz * sizeof(float));
|
||||
} else {
|
||||
// std::cerr << "DownBeat::pushAudioBlock: realloc m_buffer to " << m_bufsiz << std::endl;
|
||||
m_buffer = (float *)realloc(m_buffer, m_bufsiz * sizeof(float));
|
||||
}
|
||||
}
|
||||
if (!m_decimator1 && m_factor > 1) makeDecimators();
|
||||
// float rmsin = 0, rmsout = 0;
|
||||
// for (int i = 0; i < m_increment; ++i) {
|
||||
// rmsin += audio[i] * audio[i];
|
||||
// }
|
||||
if (m_decimator2) {
|
||||
m_decimator1->process(audio, m_decbuf);
|
||||
m_decimator2->process(m_decbuf, m_buffer + m_buffill);
|
||||
} else if (m_decimator1) {
|
||||
m_decimator1->process(audio, m_buffer + m_buffill);
|
||||
} else {
|
||||
// just copy across (m_factor is presumably 1)
|
||||
for (size_t i = 0; i < m_increment; ++i) {
|
||||
(m_buffer + m_buffill)[i] = audio[i];
|
||||
}
|
||||
}
|
||||
// for (int i = 0; i < m_increment / m_factor; ++i) {
|
||||
// rmsout += m_buffer[m_buffill + i] * m_buffer[m_buffill + i];
|
||||
// }
|
||||
// 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
|
||||
{
|
||||
length = m_buffill;
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
DownBeat::resetAudioBuffer()
|
||||
{
|
||||
if (m_buffer) free(m_buffer);
|
||||
m_buffer = 0;
|
||||
m_buffill = 0;
|
||||
m_bufsiz = 0;
|
||||
}
|
||||
|
||||
void
|
||||
DownBeat::findDownBeats(const float *audio,
|
||||
size_t audioLength,
|
||||
const d_vec_t &beats,
|
||||
i_vec_t &downbeats)
|
||||
{
|
||||
// FIND DOWNBEATS BY PARTITIONING THE INPUT AUDIO FILE INTO BEAT SEGMENTS
|
||||
// WHERE THE AUDIO FRAMES ARE DOWNSAMPLED BY A FACTOR OF 16 (fs ~= 2700Hz)
|
||||
// THEN TAKING THE JENSEN-SHANNON DIVERGENCE BETWEEN BEAT SYNCHRONOUS SPECTRAL FRAMES
|
||||
|
||||
// IMPLEMENTATION (MOSTLY) FOLLOWS:
|
||||
// DAVIES AND PLUMBLEY "A SPECTRAL DIFFERENCE APPROACH TO EXTRACTING DOWNBEATS IN MUSICAL AUDIO"
|
||||
// EUSIPCO 2006, FLORENCE, ITALY
|
||||
|
||||
d_vec_t newspec(m_beatframesize / 2); // magnitude spectrum of current beat
|
||||
d_vec_t oldspec(m_beatframesize / 2); // magnitude spectrum of previous beat
|
||||
|
||||
m_beatsd.clear();
|
||||
|
||||
if (audioLength == 0) return;
|
||||
|
||||
for (size_t i = 0; i + 1 < beats.size(); ++i) {
|
||||
|
||||
// Copy the extents of the current beat from downsampled array
|
||||
// into beat frame buffer
|
||||
|
||||
size_t beatstart = (beats[i] * m_increment) / m_factor;
|
||||
size_t beatend = (beats[i+1] * m_increment) / m_factor;
|
||||
if (beatend >= audioLength) beatend = audioLength - 1;
|
||||
if (beatend < beatstart) beatend = beatstart;
|
||||
size_t beatlen = beatend - beatstart;
|
||||
|
||||
// Also apply a Hanning window to the beat frame buffer, sized
|
||||
// to the beat extents rather than the frame size. (Because
|
||||
// the size varies, it's easier to do this by hand than use
|
||||
// our Window abstraction.)
|
||||
|
||||
// std::cerr << "beatlen = " << beatlen << std::endl;
|
||||
|
||||
// float rms = 0;
|
||||
for (size_t j = 0; j < beatlen && j < m_beatframesize; ++j) {
|
||||
double mul = 0.5 * (1.0 - cos(TWO_PI * (double(j) / double(beatlen))));
|
||||
m_beatframe[j] = audio[beatstart + j] * mul;
|
||||
// rms += m_beatframe[j] * m_beatframe[j];
|
||||
}
|
||||
// rms = sqrt(rms);
|
||||
// std::cerr << "beat " << i << ": audio rms " << rms << std::endl;
|
||||
|
||||
for (size_t j = beatlen; j < m_beatframesize; ++j) {
|
||||
m_beatframe[j] = 0.0;
|
||||
}
|
||||
|
||||
// Now FFT beat frame
|
||||
|
||||
m_fft->process(false, m_beatframe, m_fftRealOut, m_fftImagOut);
|
||||
|
||||
// Calculate magnitudes
|
||||
|
||||
for (size_t j = 0; j < m_beatframesize/2; ++j) {
|
||||
newspec[j] = sqrt(m_fftRealOut[j] * m_fftRealOut[j] +
|
||||
m_fftImagOut[j] * m_fftImagOut[j]);
|
||||
}
|
||||
|
||||
// Preserve peaks by applying adaptive threshold
|
||||
|
||||
MathUtilities::adaptiveThreshold(newspec);
|
||||
|
||||
// Calculate JS divergence between new and old spectral frames
|
||||
|
||||
if (i > 0) { // otherwise we have no previous frame
|
||||
m_beatsd.push_back(measureSpecDiff(oldspec, newspec));
|
||||
// std::cerr << "specdiff: " << m_beatsd[m_beatsd.size()-1] << std::endl;
|
||||
}
|
||||
|
||||
// Copy newspec across to old
|
||||
|
||||
for (size_t j = 0; j < m_beatframesize/2; ++j) {
|
||||
oldspec[j] = newspec[j];
|
||||
}
|
||||
}
|
||||
|
||||
// We now have all spectral difference measures in specdiff
|
||||
|
||||
int timesig = m_bpb;
|
||||
if (timesig == 0) timesig = 4;
|
||||
|
||||
d_vec_t dbcand(timesig); // downbeat candidates
|
||||
|
||||
for (int beat = 0; beat < timesig; ++beat) {
|
||||
dbcand[beat] = 0;
|
||||
}
|
||||
|
||||
// look for beat transition which leads to greatest spectral change
|
||||
for (int beat = 0; beat < timesig; ++beat) {
|
||||
int count = 0;
|
||||
for (int example = beat-1; example < (int)m_beatsd.size(); example += timesig) {
|
||||
if (example < 0) continue;
|
||||
dbcand[beat] += (m_beatsd[example]) / timesig;
|
||||
++count;
|
||||
}
|
||||
if (count > 0) dbcand[beat] /= count;
|
||||
// std::cerr << "dbcand[" << beat << "] = " << dbcand[beat] << std::endl;
|
||||
}
|
||||
|
||||
// first downbeat is beat at index of maximum value of dbcand
|
||||
int dbind = MathUtilities::getMax(dbcand);
|
||||
|
||||
// remaining downbeats are at timesig intervals from the first
|
||||
for (int i = dbind; i < (int)beats.size(); i += timesig) {
|
||||
downbeats.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
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.
|
||||
if (SPECSIZE > oldspec.size()/4) {
|
||||
SPECSIZE = oldspec.size()/4;
|
||||
}
|
||||
double SD = 0.;
|
||||
double sd1 = 0.;
|
||||
|
||||
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];
|
||||
SD = SD + (-sd1*log(sd1)) + (0.5*(oldspec[i]*log(oldspec[i]))) + (0.5*(newspec[i]*log(newspec[i])));
|
||||
}
|
||||
|
||||
return SD;
|
||||
}
|
||||
|
||||
void
|
||||
DownBeat::getBeatSD(vector<double> &beatsd) const
|
||||
{
|
||||
for (int i = 0; i < (int)m_beatsd.size(); ++i) beatsd.push_back(m_beatsd[i]);
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/* -*- 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 2008-2009 Matthew Davies and QMUL.
|
||||
|
||||
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 DOWNBEAT_H
|
||||
#define DOWNBEAT_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "dsp/rateconversion/Decimator.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
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
|
||||
* provided.
|
||||
*/
|
||||
class DownBeat
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct a downbeat locator that will operate on audio at the
|
||||
* downsampled by the given decimation factor from the given
|
||||
* original sample rate, plus beats extracted from the same audio
|
||||
* at the given original sample rate with the given frame
|
||||
* increment.
|
||||
*
|
||||
* decimationFactor must be a power of two no greater than 64, and
|
||||
* dfIncrement must be a multiple of decimationFactor.
|
||||
*/
|
||||
DownBeat(float originalSampleRate,
|
||||
size_t decimationFactor,
|
||||
size_t dfIncrement);
|
||||
~DownBeat();
|
||||
|
||||
void setBeatsPerBar(int bpb);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* beats contains a series of beat positions expressed in
|
||||
* multiples of the df increment at the audio's original sample
|
||||
* rate, as described to the constructor.
|
||||
*
|
||||
* The returned downbeat array contains a series of indices to the
|
||||
* beats array.
|
||||
*/
|
||||
void findDownBeats(const float *audio, // downsampled
|
||||
size_t audioLength, // after downsampling
|
||||
const vector<double> &beats,
|
||||
vector<int> &downbeats);
|
||||
|
||||
/**
|
||||
* Return the beat spectral difference function. This is
|
||||
* calculated during findDownBeats, so this function can only be
|
||||
* meaningfully called after that has completed. The returned
|
||||
* vector contains one value for each of the beat times passed in
|
||||
* to findDownBeats, less one. Each value contains the spectral
|
||||
* difference between region prior to the beat's nominal position
|
||||
* 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.
|
||||
*/
|
||||
const float *getBufferedAudio(size_t &length) const;
|
||||
|
||||
/**
|
||||
* Clear any buffered downsampled audio data.
|
||||
*/
|
||||
void resetAudioBuffer();
|
||||
|
||||
private:
|
||||
typedef vector<int> i_vec_t;
|
||||
typedef vector<vector<int> > i_mat_t;
|
||||
typedef vector<double> d_vec_t;
|
||||
typedef vector<vector<double> > d_mat_t;
|
||||
|
||||
void makeDecimators();
|
||||
double measureSpecDiff(d_vec_t oldspec, d_vec_t newspec);
|
||||
|
||||
int m_bpb;
|
||||
float m_rate;
|
||||
size_t m_factor;
|
||||
size_t m_increment;
|
||||
Decimator *m_decimator1;
|
||||
Decimator *m_decimator2;
|
||||
float *m_buffer;
|
||||
float *m_decbuf;
|
||||
size_t m_bufsiz;
|
||||
size_t m_buffill;
|
||||
size_t m_beatframesize;
|
||||
double *m_beatframe;
|
||||
FFTReal *m_fft;
|
||||
double *m_fftRealOut;
|
||||
double *m_fftImagOut;
|
||||
d_vec_t m_beatsd;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,869 @@
|
|||
/* -*- 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 2005-2006 Christian Landone.and Matthew Davies.
|
||||
|
||||
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 "TempoTrack.h"
|
||||
|
||||
#include "maths/MathAliases.h"
|
||||
#include "maths/MathUtilities.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
//#define DEBUG_TEMPO_TRACK 1
|
||||
|
||||
|
||||
#define RAY43VAL
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
TempoTrack::TempoTrack( TTParams Params )
|
||||
{
|
||||
m_tempoScratch = NULL;
|
||||
m_rawDFFrame = NULL;
|
||||
m_smoothDFFrame = NULL;
|
||||
m_frameACF = NULL;
|
||||
m_smoothRCF = NULL;
|
||||
|
||||
m_dataLength = 0;
|
||||
m_winLength = 0;
|
||||
m_lagLength = 0;
|
||||
|
||||
m_rayparam = 0;
|
||||
m_sigma = 0;
|
||||
m_DFWVNnorm = 0;
|
||||
|
||||
initialise( Params );
|
||||
}
|
||||
|
||||
TempoTrack::~TempoTrack()
|
||||
{
|
||||
deInitialise();
|
||||
}
|
||||
|
||||
void TempoTrack::initialise( TTParams Params )
|
||||
{
|
||||
m_winLength = Params.winLength;
|
||||
m_lagLength = Params.lagLength;
|
||||
|
||||
m_rayparam = 43.0;
|
||||
m_sigma = sqrt(3.9017);
|
||||
m_DFWVNnorm = exp( ( log( 2.0 ) / m_rayparam ) * ( m_winLength + 2 ) );
|
||||
|
||||
m_rawDFFrame = new double[ m_winLength ];
|
||||
m_smoothDFFrame = new double[ m_winLength ];
|
||||
m_frameACF = new double[ m_winLength ];
|
||||
m_tempoScratch = new double[ m_lagLength ];
|
||||
m_smoothRCF = new double[ m_lagLength ];
|
||||
|
||||
|
||||
unsigned int winPre = Params.WinT.pre;
|
||||
unsigned int winPost = Params.WinT.post;
|
||||
|
||||
m_DFFramer.configure( m_winLength, m_lagLength );
|
||||
|
||||
m_DFPParams.length = m_winLength;
|
||||
m_DFPParams.AlphaNormParam = Params.alpha;
|
||||
m_DFPParams.LPOrd = Params.LPOrd;
|
||||
m_DFPParams.LPACoeffs = Params.LPACoeffs;
|
||||
m_DFPParams.LPBCoeffs = Params.LPBCoeffs;
|
||||
m_DFPParams.winPre = Params.WinT.pre;
|
||||
m_DFPParams.winPost = Params.WinT.post;
|
||||
m_DFPParams.isMedianPositive = true;
|
||||
|
||||
m_DFConditioning = new DFProcess( m_DFPParams );
|
||||
|
||||
|
||||
// these are parameters for smoothing m_tempoScratch
|
||||
m_RCFPParams.length = m_lagLength;
|
||||
m_RCFPParams.AlphaNormParam = Params.alpha;
|
||||
m_RCFPParams.LPOrd = Params.LPOrd;
|
||||
m_RCFPParams.LPACoeffs = Params.LPACoeffs;
|
||||
m_RCFPParams.LPBCoeffs = Params.LPBCoeffs;
|
||||
m_RCFPParams.winPre = Params.WinT.pre;
|
||||
m_RCFPParams.winPost = Params.WinT.post;
|
||||
m_RCFPParams.isMedianPositive = true;
|
||||
|
||||
m_RCFConditioning = new DFProcess( m_RCFPParams );
|
||||
|
||||
}
|
||||
|
||||
void TempoTrack::deInitialise()
|
||||
{
|
||||
delete [] m_rawDFFrame;
|
||||
|
||||
delete [] m_smoothDFFrame;
|
||||
|
||||
delete [] m_smoothRCF;
|
||||
|
||||
delete [] m_frameACF;
|
||||
|
||||
delete [] m_tempoScratch;
|
||||
|
||||
delete m_DFConditioning;
|
||||
|
||||
delete m_RCFConditioning;
|
||||
|
||||
}
|
||||
|
||||
void TempoTrack::createCombFilter(double* Filter, unsigned int winLength, unsigned int TSig, double beatLag)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if( beatLag == 0 )
|
||||
{
|
||||
for( i = 0; i < winLength; i++ )
|
||||
{
|
||||
Filter[ i ] = ( ( i + 1 ) / pow( m_rayparam, 2.0) ) * exp( ( -pow(( i + 1 ),2.0 ) / ( 2.0 * pow( m_rayparam, 2.0))));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sigma = beatLag/4;
|
||||
for( i = 0; i < winLength; i++ )
|
||||
{
|
||||
double dlag = (double)(i+1) - beatLag;
|
||||
Filter[ i ] = exp(-0.5 * pow(( dlag / m_sigma), 2.0) ) / (sqrt( 2 * PI) * m_sigma);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double TempoTrack::tempoMM(double* ACF, double* weight, int tsig)
|
||||
{
|
||||
|
||||
double period = 0;
|
||||
double maxValRCF = 0.0;
|
||||
unsigned int maxIndexRCF = 0;
|
||||
|
||||
double* pdPeaks;
|
||||
|
||||
unsigned int maxIndexTemp;
|
||||
double maxValTemp;
|
||||
unsigned int count;
|
||||
|
||||
unsigned int numelem,i,j;
|
||||
int a, b;
|
||||
|
||||
for( i = 0; i < m_lagLength; i++ )
|
||||
m_tempoScratch[ i ] = 0.0;
|
||||
|
||||
if( tsig == 0 )
|
||||
{
|
||||
//if time sig is unknown, use metrically unbiased version of Filterbank
|
||||
numelem = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
numelem = tsig;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "tempoMM: m_winLength = " << m_winLength << ", m_lagLength = " << m_lagLength << ", numelem = " << numelem << std::endl;
|
||||
#endif
|
||||
|
||||
for(i=1;i<m_lagLength-1;i++)
|
||||
{
|
||||
//first and last output values are left intentionally as zero
|
||||
for (a=1;a<=numelem;a++)
|
||||
{
|
||||
for(b=(1-a);b<a;b++)
|
||||
{
|
||||
if( tsig == 0 )
|
||||
{
|
||||
m_tempoScratch[i] += ACF[a*(i+1)+b-1] * (1.0 / (2.0 * (double)a-1)) * weight[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tempoScratch[i] += ACF[a*(i+1)+b-1] * 1 * weight[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// MODIFIED BEAT PERIOD EXTRACTION //////////////
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
// find smoothed version of RCF ( as applied to Detection Function)
|
||||
m_RCFConditioning->process( m_tempoScratch, m_smoothRCF);
|
||||
|
||||
if (tsig != 0) // i.e. in context dependent state
|
||||
{
|
||||
// NOW FIND MAX INDEX OF ACFOUT
|
||||
for( i = 0; i < m_lagLength; i++)
|
||||
{
|
||||
if( m_tempoScratch[ i ] > maxValRCF)
|
||||
{
|
||||
maxValRCF = m_tempoScratch[ i ];
|
||||
maxIndexRCF = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // using rayleigh weighting
|
||||
{
|
||||
vector <vector<double> > rcfMat;
|
||||
|
||||
double sumRcf = 0.;
|
||||
|
||||
double maxVal = 0.;
|
||||
// now find the two values which minimise rcfMat
|
||||
double minVal = 0.;
|
||||
int p_i = 1; // periodicity for row i;
|
||||
int p_j = 1; //periodicity for column j;
|
||||
|
||||
|
||||
for ( i=0; i<m_lagLength; i++)
|
||||
{
|
||||
m_tempoScratch[i] =m_smoothRCF[i];
|
||||
}
|
||||
|
||||
// normalise m_tempoScratch so that it sums to zero.
|
||||
for ( i=0; i<m_lagLength; i++)
|
||||
{
|
||||
sumRcf += m_tempoScratch[i];
|
||||
}
|
||||
|
||||
for( i=0; i<m_lagLength; i++)
|
||||
{
|
||||
m_tempoScratch[i] /= sumRcf;
|
||||
}
|
||||
|
||||
// create a matrix to store m_tempoScratchValues modified by log2 ratio
|
||||
for ( i=0; i<m_lagLength; i++)
|
||||
{
|
||||
rcfMat.push_back ( vector<double>() ); // adds a new row...
|
||||
}
|
||||
|
||||
for (i=0; i<m_lagLength; i++)
|
||||
{
|
||||
for (j=0; j<m_lagLength; j++)
|
||||
{
|
||||
rcfMat[i].push_back (0.);
|
||||
}
|
||||
}
|
||||
|
||||
// the 'i' and 'j' indices deliberately start from '1' and not '0'
|
||||
for ( i=1; i<m_lagLength; i++)
|
||||
{
|
||||
for (j=1; j<m_lagLength; j++)
|
||||
{
|
||||
double log2PeriodRatio = log( static_cast<double>(i)/static_cast<double>(j) ) / log(2.0);
|
||||
rcfMat[i][j] = ( abs(1.0-abs(log2PeriodRatio)) );
|
||||
rcfMat[i][j] += ( 0.01*( 1./(m_tempoScratch[i]+m_tempoScratch[j]) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// set diagonal equal to maximum value in rcfMat
|
||||
// we don't want to pick one strong middle peak - we need a combination of two peaks.
|
||||
|
||||
for ( i=1; i<m_lagLength; i++)
|
||||
{
|
||||
for (j=1; j<m_lagLength; j++)
|
||||
{
|
||||
if (rcfMat[i][j] > maxVal)
|
||||
{
|
||||
maxVal = rcfMat[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( i=1; i<m_lagLength; i++)
|
||||
{
|
||||
rcfMat[i][i] = maxVal;
|
||||
}
|
||||
|
||||
// now find the row and column number which minimise rcfMat
|
||||
minVal = maxVal;
|
||||
|
||||
for ( i=1; i<m_lagLength; i++)
|
||||
{
|
||||
for ( j=1; j<m_lagLength; j++)
|
||||
{
|
||||
if (rcfMat[i][j] < minVal)
|
||||
{
|
||||
minVal = rcfMat[i][j];
|
||||
p_i = i;
|
||||
p_j = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// initially choose p_j (arbitrary) - saves on an else statement
|
||||
int beatPeriod = p_j;
|
||||
if (m_tempoScratch[p_i] > m_tempoScratch[p_j])
|
||||
{
|
||||
beatPeriod = p_i;
|
||||
}
|
||||
|
||||
// now write the output
|
||||
maxIndexRCF = static_cast<int>(beatPeriod);
|
||||
}
|
||||
|
||||
|
||||
double locked = 5168.f / maxIndexRCF;
|
||||
if (locked >= 30 && locked <= 180) {
|
||||
m_lockedTempo = locked;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "tempoMM: locked tempo = " << m_lockedTempo << std::endl;
|
||||
#endif
|
||||
|
||||
if( tsig == 0 )
|
||||
tsig = 4;
|
||||
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "tempoMM: maxIndexRCF = " << maxIndexRCF << std::endl;
|
||||
#endif
|
||||
|
||||
if( tsig == 4 )
|
||||
{
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "tsig == 4" << std::endl;
|
||||
#endif
|
||||
|
||||
pdPeaks = new double[ 4 ];
|
||||
for( i = 0; i < 4; i++ ){ pdPeaks[ i ] = 0.0;}
|
||||
|
||||
pdPeaks[ 0 ] = ( double )maxIndexRCF + 1;
|
||||
|
||||
maxIndexTemp = 0;
|
||||
maxValTemp = 0.0;
|
||||
count = 0;
|
||||
|
||||
for( i = (2 * maxIndexRCF + 1) - 1; i < (2 * maxIndexRCF + 1) + 2; i++ )
|
||||
{
|
||||
if( ACF[ i ] > maxValTemp )
|
||||
{
|
||||
maxValTemp = ACF[ i ];
|
||||
maxIndexTemp = count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
pdPeaks[ 1 ] = (double)( maxIndexTemp + 1 + ( (2 * maxIndexRCF + 1 ) - 2 ) + 1 )/2;
|
||||
|
||||
maxIndexTemp = 0;
|
||||
maxValTemp = 0.0;
|
||||
count = 0;
|
||||
|
||||
for( i = (3 * maxIndexRCF + 2 ) - 2; i < (3 * maxIndexRCF + 2 ) + 3; i++ )
|
||||
{
|
||||
if( ACF[ i ] > maxValTemp )
|
||||
{
|
||||
maxValTemp = ACF[ i ];
|
||||
maxIndexTemp = count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
pdPeaks[ 2 ] = (double)( maxIndexTemp + 1 + ( (3 * maxIndexRCF + 2) - 4 ) + 1 )/3;
|
||||
|
||||
maxIndexTemp = 0;
|
||||
maxValTemp = 0.0;
|
||||
count = 0;
|
||||
|
||||
for( i = ( 4 * maxIndexRCF + 3) - 3; i < ( 4 * maxIndexRCF + 3) + 4; i++ )
|
||||
{
|
||||
if( ACF[ i ] > maxValTemp )
|
||||
{
|
||||
maxValTemp = ACF[ i ];
|
||||
maxIndexTemp = count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
pdPeaks[ 3 ] = (double)( maxIndexTemp + 1 + ( (4 * maxIndexRCF + 3) - 9 ) + 1 )/4 ;
|
||||
|
||||
|
||||
period = MathUtilities::mean( pdPeaks, 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "tsig != 4" << std::endl;
|
||||
#endif
|
||||
|
||||
pdPeaks = new double[ 3 ];
|
||||
for( i = 0; i < 3; i++ ){ pdPeaks[ i ] = 0.0;}
|
||||
|
||||
pdPeaks[ 0 ] = ( double )maxIndexRCF + 1;
|
||||
|
||||
maxIndexTemp = 0;
|
||||
maxValTemp = 0.0;
|
||||
count = 0;
|
||||
|
||||
for( i = (2 * maxIndexRCF + 1) - 1; i < (2 * maxIndexRCF + 1) + 2; i++ )
|
||||
{
|
||||
if( ACF[ i ] > maxValTemp )
|
||||
{
|
||||
maxValTemp = ACF[ i ];
|
||||
maxIndexTemp = count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
pdPeaks[ 1 ] = (double)( maxIndexTemp + 1 + ( (2 * maxIndexRCF + 1 ) - 2 ) + 1 )/2;
|
||||
|
||||
maxIndexTemp = 0;
|
||||
maxValTemp = 0.0;
|
||||
count = 0;
|
||||
|
||||
for( i = (3 * maxIndexRCF + 2 ) - 2; i < (3 * maxIndexRCF + 2 ) + 3; i++ )
|
||||
{
|
||||
if( ACF[ i ] > maxValTemp )
|
||||
{
|
||||
maxValTemp = ACF[ i ];
|
||||
maxIndexTemp = count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
pdPeaks[ 2 ] = (double)( maxIndexTemp + 1 + ( (3 * maxIndexRCF + 2) - 4 ) + 1 )/3;
|
||||
|
||||
|
||||
period = MathUtilities::mean( pdPeaks, 3 );
|
||||
}
|
||||
|
||||
delete [] pdPeaks;
|
||||
|
||||
return period;
|
||||
}
|
||||
|
||||
void TempoTrack::stepDetect( double* periodP, double* periodG, int currentIdx, int* flag )
|
||||
{
|
||||
double stepthresh = 1 * 3.9017;
|
||||
|
||||
if( *flag )
|
||||
{
|
||||
if(abs(periodG[ currentIdx ] - periodP[ currentIdx ]) > stepthresh)
|
||||
{
|
||||
// do nuffin'
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(fabs(periodG[ currentIdx ]-periodP[ currentIdx ]) > stepthresh)
|
||||
{
|
||||
*flag = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TempoTrack::constDetect( double* periodP, int currentIdx, int* flag )
|
||||
{
|
||||
double constthresh = 2 * 3.9017;
|
||||
|
||||
if( fabs( 2 * periodP[ currentIdx ] - periodP[ currentIdx - 1] - periodP[ currentIdx - 2] ) < constthresh)
|
||||
{
|
||||
*flag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*flag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int TempoTrack::findMeter(double *ACF, unsigned int len, double period)
|
||||
{
|
||||
int i;
|
||||
int p = (int)MathUtilities::round( period );
|
||||
int tsig;
|
||||
|
||||
double Energy_3 = 0.0;
|
||||
double Energy_4 = 0.0;
|
||||
|
||||
double temp3A = 0.0;
|
||||
double temp3B = 0.0;
|
||||
double temp4A = 0.0;
|
||||
double temp4B = 0.0;
|
||||
|
||||
double* dbf = new double[ len ]; int t = 0;
|
||||
for( unsigned int u = 0; u < len; u++ ){ dbf[ u ] = 0.0; }
|
||||
|
||||
if( (double)len < 6 * p + 2 )
|
||||
{
|
||||
for( i = ( 3 * p - 2 ); i < ( 3 * p + 2 ) + 1; i++ )
|
||||
{
|
||||
temp3A += ACF[ i ];
|
||||
dbf[ t++ ] = ACF[ i ];
|
||||
}
|
||||
|
||||
for( i = ( 4 * p - 2 ); i < ( 4 * p + 2 ) + 1; i++ )
|
||||
{
|
||||
temp4A += ACF[ i ];
|
||||
}
|
||||
|
||||
Energy_3 = temp3A;
|
||||
Energy_4 = temp4A;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = ( 3 * p - 2 ); i < ( 3 * p + 2 ) + 1; i++ )
|
||||
{
|
||||
temp3A += ACF[ i ];
|
||||
}
|
||||
|
||||
for( i = ( 4 * p - 2 ); i < ( 4 * p + 2 ) + 1; i++ )
|
||||
{
|
||||
temp4A += ACF[ i ];
|
||||
}
|
||||
|
||||
for( i = ( 6 * p - 2 ); i < ( 6 * p + 2 ) + 1; i++ )
|
||||
{
|
||||
temp3B += ACF[ i ];
|
||||
}
|
||||
|
||||
for( i = ( 2 * p - 2 ); i < ( 2 * p + 2 ) + 1; i++ )
|
||||
{
|
||||
temp4B += ACF[ i ];
|
||||
}
|
||||
|
||||
Energy_3 = temp3A + temp3B;
|
||||
Energy_4 = temp4A + temp4B;
|
||||
}
|
||||
|
||||
if (Energy_3 > Energy_4)
|
||||
{
|
||||
tsig = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
tsig = 4;
|
||||
}
|
||||
|
||||
|
||||
return tsig;
|
||||
}
|
||||
|
||||
void TempoTrack::createPhaseExtractor(double *Filter, unsigned int winLength, double period, unsigned int fsp, unsigned int lastBeat)
|
||||
{
|
||||
int p = (int)MathUtilities::round( period );
|
||||
int predictedOffset = 0;
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "TempoTrack::createPhaseExtractor: period = " << period << ", p = " << p << std::endl;
|
||||
#endif
|
||||
|
||||
if (p > 10000) {
|
||||
std::cerr << "TempoTrack::createPhaseExtractor: WARNING! Highly implausible period value " << p << "!" << std::endl;
|
||||
period = 5168 / 120;
|
||||
}
|
||||
|
||||
double* phaseScratch = new double[ p*2 + 2 ];
|
||||
for (int i = 0; i < p*2 + 2; ++i) phaseScratch[i] = 0.0;
|
||||
|
||||
|
||||
if( lastBeat != 0 )
|
||||
{
|
||||
lastBeat = (int)MathUtilities::round((double)lastBeat );///(double)winLength);
|
||||
|
||||
predictedOffset = lastBeat + p - fsp;
|
||||
|
||||
if (predictedOffset < 0)
|
||||
{
|
||||
lastBeat = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( lastBeat != 0 )
|
||||
{
|
||||
int mu = p;
|
||||
double sigma = (double)p/8;
|
||||
double PhaseMin = 0.0;
|
||||
double PhaseMax = 0.0;
|
||||
unsigned int scratchLength = p*2;
|
||||
double temp = 0.0;
|
||||
|
||||
for( int i = 0; i < scratchLength; i++ )
|
||||
{
|
||||
phaseScratch[ i ] = exp( -0.5 * pow( ( i - mu ) / sigma, 2 ) ) / ( sqrt( 2*PI ) *sigma );
|
||||
}
|
||||
|
||||
MathUtilities::getFrameMinMax( phaseScratch, scratchLength, &PhaseMin, &PhaseMax );
|
||||
|
||||
for(int i = 0; i < scratchLength; i ++)
|
||||
{
|
||||
temp = phaseScratch[ i ];
|
||||
phaseScratch[ i ] = (temp - PhaseMin)/PhaseMax;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "predictedOffset = " << predictedOffset << std::endl;
|
||||
#endif
|
||||
|
||||
unsigned int index = 0;
|
||||
for (int i = p - ( predictedOffset - 1); i < p + ( p - predictedOffset) + 1; i++)
|
||||
{
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "assigning to filter index " << index << " (size = " << p*2 << ")" << " value " << phaseScratch[i] << " from scratch index " << i << std::endl;
|
||||
#endif
|
||||
Filter[ index++ ] = phaseScratch[ i ];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 0; i < p; i ++)
|
||||
{
|
||||
Filter[ i ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] phaseScratch;
|
||||
}
|
||||
|
||||
int TempoTrack::phaseMM(double *DF, double *weighting, unsigned int winLength, double period)
|
||||
{
|
||||
int alignment = 0;
|
||||
int p = (int)MathUtilities::round( period );
|
||||
|
||||
double temp = 0.0;
|
||||
|
||||
double* y = new double[ winLength ];
|
||||
double* align = new double[ p ];
|
||||
|
||||
for( int i = 0; i < winLength; i++ )
|
||||
{
|
||||
y[ i ] = (double)( -i + winLength )/(double)winLength;
|
||||
y[ i ] = pow(y [i ],2.0); // raise to power 2.
|
||||
}
|
||||
|
||||
for( int o = 0; o < p; o++ )
|
||||
{
|
||||
temp = 0.0;
|
||||
for(int i = 1 + (o - 1); i< winLength; i += (p + 1))
|
||||
{
|
||||
temp = temp + DF[ i ] * y[ i ];
|
||||
}
|
||||
align[ o ] = temp * weighting[ o ];
|
||||
}
|
||||
|
||||
|
||||
double valTemp = 0.0;
|
||||
for(int i = 0; i < p; i++)
|
||||
{
|
||||
if( align[ i ] > valTemp )
|
||||
{
|
||||
valTemp = align[ i ];
|
||||
alignment = i;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] y;
|
||||
delete [] align;
|
||||
|
||||
return alignment;
|
||||
}
|
||||
|
||||
int TempoTrack::beatPredict(unsigned int FSP0, double alignment, double period, unsigned int step )
|
||||
{
|
||||
int beat = 0;
|
||||
|
||||
int p = (int)MathUtilities::round( period );
|
||||
int align = (int)MathUtilities::round( alignment );
|
||||
int FSP = (int)MathUtilities::round( FSP0 );
|
||||
|
||||
int FEP = FSP + ( step );
|
||||
|
||||
beat = FSP + align;
|
||||
|
||||
m_beats.push_back( beat );
|
||||
|
||||
while( beat + p < FEP )
|
||||
{
|
||||
beat += p;
|
||||
|
||||
m_beats.push_back( beat );
|
||||
}
|
||||
|
||||
return beat;
|
||||
}
|
||||
|
||||
|
||||
|
||||
vector<int> TempoTrack::process( vector <double> DF,
|
||||
vector <double> *tempoReturn )
|
||||
{
|
||||
m_dataLength = DF.size();
|
||||
|
||||
m_lockedTempo = 0.0;
|
||||
|
||||
double period = 0.0;
|
||||
int stepFlag = 0;
|
||||
int constFlag = 0;
|
||||
int FSP = 0;
|
||||
int tsig = 0;
|
||||
int lastBeat = 0;
|
||||
|
||||
vector <double> causalDF;
|
||||
|
||||
causalDF = DF;
|
||||
|
||||
//Prepare Causal Extension DFData
|
||||
unsigned int DFCLength = m_dataLength + m_winLength;
|
||||
|
||||
for( unsigned int j = 0; j < m_winLength; j++ )
|
||||
{
|
||||
causalDF.push_back( 0 );
|
||||
}
|
||||
|
||||
|
||||
double* RW = new double[ m_lagLength ];
|
||||
for( unsigned int clear = 0; clear < m_lagLength; clear++){ RW[ clear ] = 0.0;}
|
||||
|
||||
double* GW = new double[ m_lagLength ];
|
||||
for(unsigned int clear = 0; clear < m_lagLength; clear++){ GW[ clear ] = 0.0;}
|
||||
|
||||
double* PW = new double[ m_lagLength ];
|
||||
for(unsigned clear = 0; clear < m_lagLength; clear++){ PW[ clear ] = 0.0;}
|
||||
|
||||
m_DFFramer.setSource( &causalDF[0], m_dataLength );
|
||||
|
||||
unsigned int TTFrames = m_DFFramer.getMaxNoFrames();
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "TTFrames = " << TTFrames << std::endl;
|
||||
#endif
|
||||
|
||||
double* periodP = new double[ TTFrames ];
|
||||
for(unsigned clear = 0; clear < TTFrames; clear++){ periodP[ clear ] = 0.0;}
|
||||
|
||||
double* periodG = new double[ TTFrames ];
|
||||
for(unsigned clear = 0; clear < TTFrames; clear++){ periodG[ clear ] = 0.0;}
|
||||
|
||||
double* alignment = new double[ TTFrames ];
|
||||
for(unsigned clear = 0; clear < TTFrames; clear++){ alignment[ clear ] = 0.0;}
|
||||
|
||||
m_beats.clear();
|
||||
|
||||
createCombFilter( RW, m_lagLength, 0, 0 );
|
||||
|
||||
int TTLoopIndex = 0;
|
||||
|
||||
for( unsigned int i = 0; i < TTFrames; i++ )
|
||||
{
|
||||
m_DFFramer.getFrame( m_rawDFFrame );
|
||||
|
||||
m_DFConditioning->process( m_rawDFFrame, m_smoothDFFrame );
|
||||
|
||||
m_correlator.doAutoUnBiased( m_smoothDFFrame, m_frameACF, m_winLength );
|
||||
|
||||
periodP[ TTLoopIndex ] = tempoMM( m_frameACF, RW, 0 );
|
||||
|
||||
if( GW[ 0 ] != 0 )
|
||||
{
|
||||
periodG[ TTLoopIndex ] = tempoMM( m_frameACF, GW, tsig );
|
||||
}
|
||||
else
|
||||
{
|
||||
periodG[ TTLoopIndex ] = 0.0;
|
||||
}
|
||||
|
||||
stepDetect( periodP, periodG, TTLoopIndex, &stepFlag );
|
||||
|
||||
if( stepFlag == 1)
|
||||
{
|
||||
constDetect( periodP, TTLoopIndex, &constFlag );
|
||||
stepFlag = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
stepFlag -= 1;
|
||||
}
|
||||
|
||||
if( stepFlag < 0 )
|
||||
{
|
||||
stepFlag = 0;
|
||||
}
|
||||
|
||||
if( constFlag != 0)
|
||||
{
|
||||
tsig = findMeter( m_frameACF, m_winLength, periodP[ TTLoopIndex ] );
|
||||
|
||||
createCombFilter( GW, m_lagLength, tsig, periodP[ TTLoopIndex ] );
|
||||
|
||||
periodG[ TTLoopIndex ] = tempoMM( m_frameACF, GW, tsig );
|
||||
|
||||
period = periodG[ TTLoopIndex ];
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "TempoTrack::process: constFlag == " << constFlag << ", TTLoopIndex = " << TTLoopIndex << ", period from periodG = " << period << std::endl;
|
||||
#endif
|
||||
|
||||
createPhaseExtractor( PW, m_winLength, period, FSP, 0 );
|
||||
|
||||
constFlag = 0;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if( GW[ 0 ] != 0 )
|
||||
{
|
||||
period = periodG[ TTLoopIndex ];
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "TempoTrack::process: GW[0] == " << GW[0] << ", TTLoopIndex = " << TTLoopIndex << ", period from periodG = " << period << std::endl;
|
||||
#endif
|
||||
|
||||
if (period > 10000) {
|
||||
std::cerr << "TempoTrack::process: WARNING! Highly implausible period value " << period << "!" << std::endl;
|
||||
std::cerr << "periodG contains (of " << TTFrames << " frames): " << std::endl;
|
||||
for (int i = 0; i < TTLoopIndex + 3 && i < TTFrames; ++i) {
|
||||
std::cerr << i << " -> " << periodG[i] << std::endl;
|
||||
}
|
||||
std::cerr << "periodP contains (of " << TTFrames << " frames): " << std::endl;
|
||||
for (int i = 0; i < TTLoopIndex + 3 && i < TTFrames; ++i) {
|
||||
std::cerr << i << " -> " << periodP[i] << std::endl;
|
||||
}
|
||||
period = 5168 / 120;
|
||||
}
|
||||
|
||||
createPhaseExtractor( PW, m_winLength, period, FSP, lastBeat );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
period = periodP[ TTLoopIndex ];
|
||||
|
||||
#ifdef DEBUG_TEMPO_TRACK
|
||||
std::cerr << "TempoTrack::process: GW[0] == " << GW[0] << ", TTLoopIndex = " << TTLoopIndex << ", period from periodP = " << period << std::endl;
|
||||
#endif
|
||||
|
||||
createPhaseExtractor( PW, m_winLength, period, FSP, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
alignment[ TTLoopIndex ] = phaseMM( m_rawDFFrame, PW, m_winLength, period );
|
||||
|
||||
lastBeat = beatPredict(FSP, alignment[ TTLoopIndex ], period, m_lagLength );
|
||||
|
||||
FSP += (m_lagLength);
|
||||
|
||||
if (tempoReturn) tempoReturn->push_back(m_lockedTempo);
|
||||
|
||||
TTLoopIndex++;
|
||||
}
|
||||
|
||||
|
||||
delete [] periodP;
|
||||
delete [] periodG;
|
||||
delete [] alignment;
|
||||
|
||||
delete [] RW;
|
||||
delete [] GW;
|
||||
delete [] PW;
|
||||
|
||||
return m_beats;
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef TEMPOTRACK_H
|
||||
#define TEMPOTRACK_H
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#include "dsp/signalconditioning/DFProcess.h"
|
||||
#include "maths/Correlation.h"
|
||||
#include "dsp/signalconditioning/Framer.h"
|
||||
|
||||
|
||||
|
||||
using std::vector;
|
||||
|
||||
struct WinThresh
|
||||
{
|
||||
unsigned int pre;
|
||||
unsigned int post;
|
||||
};
|
||||
|
||||
struct TTParams
|
||||
{
|
||||
unsigned int winLength; //Analysis window length
|
||||
unsigned int lagLength; //Lag & Stride size
|
||||
unsigned int alpha; //alpha-norm parameter
|
||||
unsigned int LPOrd; // low-pass Filter order
|
||||
double* LPACoeffs; //low pass Filter den coefficients
|
||||
double* LPBCoeffs; //low pass Filter num coefficients
|
||||
WinThresh WinT;//window size in frames for adaptive thresholding [pre post]:
|
||||
};
|
||||
|
||||
|
||||
class TempoTrack
|
||||
{
|
||||
public:
|
||||
TempoTrack( TTParams Params );
|
||||
virtual ~TempoTrack();
|
||||
|
||||
vector<int> process( vector <double> DF, vector <double> *tempoReturn = 0);
|
||||
|
||||
|
||||
private:
|
||||
void initialise( TTParams Params );
|
||||
void deInitialise();
|
||||
|
||||
int beatPredict( unsigned int FSP, double alignment, double period, unsigned int step);
|
||||
int phaseMM( double* DF, double* weighting, unsigned int winLength, double period );
|
||||
void createPhaseExtractor( double* Filter, unsigned int winLength, double period, unsigned int fsp, unsigned int lastBeat );
|
||||
int findMeter( double* ACF, unsigned int len, double period );
|
||||
void constDetect( double* periodP, int currentIdx, int* flag );
|
||||
void stepDetect( double* periodP, double* periodG, int currentIdx, int* flag );
|
||||
void createCombFilter( double* Filter, unsigned int winLength, unsigned int TSig, double beatLag );
|
||||
double tempoMM( double* ACF, double* weight, int sig );
|
||||
|
||||
unsigned int m_dataLength;
|
||||
unsigned int m_winLength;
|
||||
unsigned int m_lagLength;
|
||||
|
||||
double m_rayparam;
|
||||
double m_sigma;
|
||||
double m_DFWVNnorm;
|
||||
|
||||
vector<int> m_beats; // Vector of detected beats
|
||||
|
||||
double m_lockedTempo;
|
||||
|
||||
double* m_tempoScratch;
|
||||
double* m_smoothRCF; // Smoothed Output of Comb Filterbank (m_tempoScratch)
|
||||
|
||||
// Processing Buffers
|
||||
double* m_rawDFFrame; // Original Detection Function Analysis Frame
|
||||
double* m_smoothDFFrame; // Smoothed Detection Function Analysis Frame
|
||||
double* m_frameACF; // AutoCorrelation of Smoothed Detection Function
|
||||
|
||||
//Low Pass Coefficients for DF Smoothing
|
||||
double* m_ACoeffs;
|
||||
double* m_BCoeffs;
|
||||
|
||||
// Objetcs/operators declaration
|
||||
Framer m_DFFramer;
|
||||
DFProcess* m_DFConditioning;
|
||||
Correlation m_correlator;
|
||||
// Config structure for DFProcess
|
||||
DFProcConfig m_DFPParams;
|
||||
|
||||
// also want to smooth m_tempoScratch
|
||||
DFProcess* m_RCFConditioning;
|
||||
// Config structure for RCFProcess
|
||||
DFProcConfig m_RCFPParams;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,487 @@
|
|||
/* -*- 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 2008-2009 Matthew Davies and QMUL.
|
||||
|
||||
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 "TempoTrackV2.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include "maths/MathUtilities.h"
|
||||
|
||||
#define EPS 0.0000008 // just some arbitrary small number
|
||||
|
||||
TempoTrackV2::TempoTrackV2(float rate, size_t increment) :
|
||||
m_rate(rate), m_increment(increment) { }
|
||||
TempoTrackV2::~TempoTrackV2() { }
|
||||
|
||||
void
|
||||
TempoTrackV2::filter_df(d_vec_t &df)
|
||||
{
|
||||
d_vec_t a(3);
|
||||
d_vec_t b(3);
|
||||
d_vec_t lp_df(df.size());
|
||||
|
||||
//equivalent in matlab to [b,a] = butter(2,0.4);
|
||||
a[0] = 1.0000;
|
||||
a[1] = -0.3695;
|
||||
a[2] = 0.1958;
|
||||
b[0] = 0.2066;
|
||||
b[1] = 0.4131;
|
||||
b[2] = 0.2066;
|
||||
|
||||
double inp1 = 0.;
|
||||
double inp2 = 0.;
|
||||
double out1 = 0.;
|
||||
double out2 = 0.;
|
||||
|
||||
|
||||
// forwards filtering
|
||||
for (unsigned int i = 0;i < df.size();i++)
|
||||
{
|
||||
lp_df[i] = b[0]*df[i] + b[1]*inp1 + b[2]*inp2 - a[1]*out1 - a[2]*out2;
|
||||
inp2 = inp1;
|
||||
inp1 = df[i];
|
||||
out2 = out1;
|
||||
out1 = lp_df[i];
|
||||
}
|
||||
|
||||
// copy forwards filtering to df...
|
||||
// but, time-reversed, ready for backwards filtering
|
||||
for (unsigned int i = 0;i < df.size();i++)
|
||||
{
|
||||
df[i] = lp_df[df.size()-i-1];
|
||||
}
|
||||
|
||||
for (unsigned int i = 0;i < df.size();i++)
|
||||
{
|
||||
lp_df[i] = 0.;
|
||||
}
|
||||
|
||||
inp1 = 0.; inp2 = 0.;
|
||||
out1 = 0.; out2 = 0.;
|
||||
|
||||
// backwards filetering on time-reversed df
|
||||
for (unsigned int i = 0;i < df.size();i++)
|
||||
{
|
||||
lp_df[i] = b[0]*df[i] + b[1]*inp1 + b[2]*inp2 - a[1]*out1 - a[2]*out2;
|
||||
inp2 = inp1;
|
||||
inp1 = df[i];
|
||||
out2 = out1;
|
||||
out1 = lp_df[i];
|
||||
}
|
||||
|
||||
// write the re-reversed (i.e. forward) version back to df
|
||||
for (unsigned int i = 0;i < df.size();i++)
|
||||
{
|
||||
df[i] = lp_df[df.size()-i-1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TempoTrackV2::calculateBeatPeriod(const vector<double> &df,
|
||||
vector<double> &beat_period,
|
||||
vector<double> &tempi)
|
||||
{
|
||||
// to follow matlab.. split into 512 sample frames with a 128 hop size
|
||||
// calculate the acf,
|
||||
// then the rcf.. and then stick the rcfs as columns of a matrix
|
||||
// then call viterbi decoding with weight vector and transition matrix
|
||||
// and get best path
|
||||
|
||||
unsigned int wv_len = 128;
|
||||
double rayparam = 43.;
|
||||
|
||||
// make rayleigh weighting curve
|
||||
d_vec_t wv(wv_len);
|
||||
for (unsigned int i=0; i<wv.size(); i++)
|
||||
{
|
||||
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)
|
||||
unsigned int winlen = 512;
|
||||
unsigned int step = 128;
|
||||
|
||||
// matrix to store output of comb filter bank, increment column of matrix at each frame
|
||||
d_mat_t rcfmat;
|
||||
int col_counter = -1;
|
||||
|
||||
// main loop for beat period calculation
|
||||
for (unsigned int i=0; i+winlen<df.size(); i+=step)
|
||||
{
|
||||
// get dfframe
|
||||
d_vec_t dfframe(winlen);
|
||||
for (unsigned int k=0; k<winlen; k++)
|
||||
{
|
||||
dfframe[k] = df[i+k];
|
||||
}
|
||||
// get rcf vector for current frame
|
||||
d_vec_t rcf(wv_len);
|
||||
get_rcf(dfframe,wv,rcf);
|
||||
|
||||
rcfmat.push_back( d_vec_t() ); // adds a new column
|
||||
col_counter++;
|
||||
for (unsigned int j=0; j<rcf.size(); j++)
|
||||
{
|
||||
rcfmat[col_counter].push_back( rcf[j] );
|
||||
}
|
||||
}
|
||||
|
||||
// now call viterbi decoding function
|
||||
viterbi_decode(rcfmat,wv,beat_period,tempi);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TempoTrackV2::get_rcf(const d_vec_t &dfframe_in, const d_vec_t &wv, d_vec_t &rcf)
|
||||
{
|
||||
// calculate autocorrelation function
|
||||
// then rcf
|
||||
// just hard code for now... don't really need separate functions to do this
|
||||
|
||||
// make acf
|
||||
|
||||
d_vec_t dfframe(dfframe_in);
|
||||
|
||||
MathUtilities::adaptiveThreshold(dfframe);
|
||||
|
||||
d_vec_t acf(dfframe.size());
|
||||
|
||||
|
||||
for (unsigned int lag=0; lag<dfframe.size(); lag++)
|
||||
{
|
||||
double sum = 0.;
|
||||
double tmp = 0.;
|
||||
|
||||
for (unsigned int n=0; n<(dfframe.size()-lag); n++)
|
||||
{
|
||||
tmp = dfframe[n] * dfframe[n+lag];
|
||||
sum += tmp;
|
||||
}
|
||||
acf[lag] = static_cast<double> (sum/ (dfframe.size()-lag));
|
||||
}
|
||||
|
||||
// now apply comb filtering
|
||||
int numelem = 4;
|
||||
|
||||
for (unsigned int i = 2;i < rcf.size();i++) // max beat period
|
||||
{
|
||||
for (int a = 1;a <= numelem;a++) // number of comb elements
|
||||
{
|
||||
for (int b = 1-a;b <= a-1;b++) // general state using normalisation of comb elements
|
||||
{
|
||||
rcf[i-1] += ( acf[(a*i+b)-1]*wv[i-1] ) / (2.*a-1.); // calculate value for comb filter row
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply adaptive threshold to rcf
|
||||
MathUtilities::adaptiveThreshold(rcf);
|
||||
|
||||
double rcfsum =0.;
|
||||
for (unsigned int i=0; i<rcf.size(); i++)
|
||||
{
|
||||
rcf[i] += EPS ;
|
||||
rcfsum += rcf[i];
|
||||
}
|
||||
|
||||
// normalise rcf to sum to unity
|
||||
for (unsigned int i=0; i<rcf.size(); i++)
|
||||
{
|
||||
rcf[i] /= (rcfsum + EPS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TempoTrackV2::viterbi_decode(const d_mat_t &rcfmat, const d_vec_t &wv, d_vec_t &beat_period, d_vec_t &tempi)
|
||||
{
|
||||
// following Kevin Murphy's Viterbi decoding to get best path of
|
||||
// beat periods through rfcmat
|
||||
|
||||
// make transition matrix
|
||||
d_mat_t tmat;
|
||||
for (unsigned int i=0;i<wv.size();i++)
|
||||
{
|
||||
tmat.push_back ( d_vec_t() ); // adds a new column
|
||||
for (unsigned int j=0; j<wv.size(); j++)
|
||||
{
|
||||
tmat[i].push_back(0.); // fill with zeros initially
|
||||
}
|
||||
}
|
||||
|
||||
// variance of Gaussians in transition matrix
|
||||
// formed of Gaussians on diagonal - implies slow tempo change
|
||||
double sigma = 8.;
|
||||
// don't want really short beat periods, or really long ones
|
||||
for (unsigned int i=20;i <wv.size()-20; i++)
|
||||
{
|
||||
for (unsigned int j=20; j<wv.size()-20; j++)
|
||||
{
|
||||
double mu = static_cast<double>(i);
|
||||
tmat[i][j] = exp( (-1.*pow((j-mu),2.)) / (2.*pow(sigma,2.)) );
|
||||
}
|
||||
}
|
||||
|
||||
// parameters for Viterbi decoding... this part is taken from
|
||||
// Murphy's matlab
|
||||
|
||||
d_mat_t delta;
|
||||
i_mat_t psi;
|
||||
for (unsigned int i=0;i <rcfmat.size(); i++)
|
||||
{
|
||||
delta.push_back( d_vec_t());
|
||||
psi.push_back( i_vec_t());
|
||||
for (unsigned int j=0; j<rcfmat[i].size(); j++)
|
||||
{
|
||||
delta[i].push_back(0.); // fill with zeros initially
|
||||
psi[i].push_back(0); // fill with zeros initially
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int T = delta.size();
|
||||
|
||||
if (T < 2) return; // can't do anything at all meaningful
|
||||
|
||||
unsigned int Q = delta[0].size();
|
||||
|
||||
// initialize first column of delta
|
||||
for (unsigned int j=0; j<Q; j++)
|
||||
{
|
||||
delta[0][j] = wv[j] * rcfmat[0][j];
|
||||
psi[0][j] = 0;
|
||||
}
|
||||
|
||||
double deltasum = 0.;
|
||||
for (unsigned int i=0; i<Q; i++)
|
||||
{
|
||||
deltasum += delta[0][i];
|
||||
}
|
||||
for (unsigned int i=0; i<Q; i++)
|
||||
{
|
||||
delta[0][i] /= (deltasum + EPS);
|
||||
}
|
||||
|
||||
|
||||
for (unsigned int t=1; t<T; t++)
|
||||
{
|
||||
d_vec_t tmp_vec(Q);
|
||||
|
||||
for (unsigned int j=0; j<Q; j++)
|
||||
{
|
||||
for (unsigned int i=0; i<Q; i++)
|
||||
{
|
||||
tmp_vec[i] = delta[t-1][i] * tmat[j][i];
|
||||
}
|
||||
|
||||
delta[t][j] = get_max_val(tmp_vec);
|
||||
|
||||
psi[t][j] = get_max_ind(tmp_vec);
|
||||
|
||||
delta[t][j] *= rcfmat[t][j];
|
||||
}
|
||||
|
||||
// normalise current delta column
|
||||
double deltasum = 0.;
|
||||
for (unsigned int i=0; i<Q; i++)
|
||||
{
|
||||
deltasum += delta[t][i];
|
||||
}
|
||||
for (unsigned int i=0; i<Q; i++)
|
||||
{
|
||||
delta[t][i] /= (deltasum + EPS);
|
||||
}
|
||||
}
|
||||
|
||||
i_vec_t bestpath(T);
|
||||
d_vec_t tmp_vec(Q);
|
||||
for (unsigned int i=0; i<Q; i++)
|
||||
{
|
||||
tmp_vec[i] = delta[T-1][i];
|
||||
}
|
||||
|
||||
// find starting point - best beat period for "last" frame
|
||||
bestpath[T-1] = get_max_ind(tmp_vec);
|
||||
|
||||
// backtrace through index of maximum values in psi
|
||||
for (unsigned int t=T-2; t>0 ;t--)
|
||||
{
|
||||
bestpath[t] = psi[t+1][bestpath[t+1]];
|
||||
}
|
||||
|
||||
// weird but necessary hack -- couldn't get above loop to terminate at t >= 0
|
||||
bestpath[0] = psi[1][bestpath[1]];
|
||||
|
||||
unsigned int lastind = 0;
|
||||
for (unsigned int i=0; i<T; i++)
|
||||
{
|
||||
unsigned int step = 128;
|
||||
for (unsigned int j=0; j<step; j++)
|
||||
{
|
||||
lastind = i*step+j;
|
||||
beat_period[lastind] = bestpath[i];
|
||||
}
|
||||
// std::cerr << "bestpath[" << i << "] = " << bestpath[i] << " (used for beat_periods " << i*step << " to " << i*step+step-1 << ")" << std::endl;
|
||||
}
|
||||
|
||||
//fill in the last values...
|
||||
for (unsigned int i=lastind; i<beat_period.size(); i++)
|
||||
{
|
||||
beat_period[i] = beat_period[lastind];
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < beat_period.size(); i++)
|
||||
{
|
||||
tempi.push_back((60. * m_rate / m_increment)/beat_period[i]);
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
TempoTrackV2::get_max_val(const d_vec_t &df)
|
||||
{
|
||||
double maxval = 0.;
|
||||
for (unsigned int i=0; i<df.size(); i++)
|
||||
{
|
||||
if (maxval < df[i])
|
||||
{
|
||||
maxval = df[i];
|
||||
}
|
||||
}
|
||||
|
||||
return maxval;
|
||||
}
|
||||
|
||||
int
|
||||
TempoTrackV2::get_max_ind(const d_vec_t &df)
|
||||
{
|
||||
double maxval = 0.;
|
||||
int ind = 0;
|
||||
for (unsigned int i=0; i<df.size(); i++)
|
||||
{
|
||||
if (maxval < df[i])
|
||||
{
|
||||
maxval = df[i];
|
||||
ind = i;
|
||||
}
|
||||
}
|
||||
|
||||
return ind;
|
||||
}
|
||||
|
||||
void
|
||||
TempoTrackV2::normalise_vec(d_vec_t &df)
|
||||
{
|
||||
double sum = 0.;
|
||||
for (unsigned int i=0; i<df.size(); i++)
|
||||
{
|
||||
sum += df[i];
|
||||
}
|
||||
|
||||
for (unsigned int i=0; i<df.size(); i++)
|
||||
{
|
||||
df[i]/= (sum + EPS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TempoTrackV2::calculateBeats(const vector<double> &df,
|
||||
const vector<double> &beat_period,
|
||||
vector<double> &beats)
|
||||
{
|
||||
if (df.empty() || beat_period.empty()) return;
|
||||
|
||||
d_vec_t cumscore(df.size()); // store cumulative score
|
||||
i_vec_t backlink(df.size()); // backlink (stores best beat locations at each time instant)
|
||||
d_vec_t localscore(df.size()); // localscore, for now this is the same as the detection function
|
||||
|
||||
for (unsigned int i=0; i<df.size(); i++)
|
||||
{
|
||||
localscore[i] = df[i];
|
||||
backlink[i] = -1;
|
||||
}
|
||||
|
||||
double tightness = 4.;
|
||||
double alpha = 0.9;
|
||||
|
||||
// main loop
|
||||
for (unsigned int i=0; i<localscore.size(); i++)
|
||||
{
|
||||
int prange_min = -2*beat_period[i];
|
||||
int prange_max = round(-0.5*beat_period[i]);
|
||||
|
||||
// transition range
|
||||
d_vec_t txwt (prange_max - prange_min + 1);
|
||||
d_vec_t scorecands (txwt.size());
|
||||
|
||||
for (unsigned int j=0;j<txwt.size();j++)
|
||||
{
|
||||
double mu = static_cast<double> (beat_period[i]);
|
||||
txwt[j] = exp( -0.5*pow(tightness * log((round(2*mu)-j)/mu),2));
|
||||
|
||||
// IF IN THE ALLOWED RANGE, THEN LOOK AT CUMSCORE[I+PRANGE_MIN+J
|
||||
// ELSE LEAVE AT DEFAULT VALUE FROM INITIALISATION: D_VEC_T SCORECANDS (TXWT.SIZE());
|
||||
|
||||
int cscore_ind = i+prange_min+j;
|
||||
if (cscore_ind >= 0)
|
||||
{
|
||||
scorecands[j] = txwt[j] * cumscore[cscore_ind];
|
||||
}
|
||||
}
|
||||
|
||||
// find max value and index of maximum value
|
||||
double vv = get_max_val(scorecands);
|
||||
int xx = get_max_ind(scorecands);
|
||||
|
||||
cumscore[i] = alpha*vv + (1.-alpha)*localscore[i];
|
||||
backlink[i] = i+prange_min+xx;
|
||||
|
||||
// std::cerr << "backlink[" << i << "] <= " << backlink[i] << std::endl;
|
||||
}
|
||||
|
||||
// STARTING POINT, I.E. LAST BEAT.. PICK A STRONG POINT IN cumscore VECTOR
|
||||
d_vec_t tmp_vec;
|
||||
for (unsigned int i=cumscore.size() - beat_period[beat_period.size()-1] ; i<cumscore.size(); i++)
|
||||
{
|
||||
tmp_vec.push_back(cumscore[i]);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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
|
||||
i_vec_t ibeats;
|
||||
ibeats.push_back(startpoint);
|
||||
// std::cerr << "startpoint = " << startpoint << std::endl;
|
||||
while (backlink[ibeats.back()] > 0)
|
||||
{
|
||||
// std::cerr << "backlink[" << ibeats.back() << "] = " << backlink[ibeats.back()] << std::endl;
|
||||
int b = ibeats.back();
|
||||
if (backlink[b] == b) break; // shouldn't happen... haha
|
||||
ibeats.push_back(backlink[b]);
|
||||
}
|
||||
|
||||
// REVERSE SEQUENCE OF IBEATS AND STORE AS BEATS
|
||||
for (unsigned int i=0; i<ibeats.size(); i++)
|
||||
{
|
||||
beats.push_back( static_cast<double>(ibeats[ibeats.size()-i-1]) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/* -*- 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 2008-2009 Matthew Davies and QMUL.
|
||||
|
||||
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 TEMPOTRACKV2_H
|
||||
#define TEMPOTRACKV2_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
using std::vector;
|
||||
|
||||
//!!! Question: how far is this actually sample rate dependent? I
|
||||
// think it does produce plausible results for e.g. 48000 as well as
|
||||
// 44100, but surely the fixed window sizes and comb filtering will
|
||||
// make it prefer double or half time when run at e.g. 96000?
|
||||
|
||||
class TempoTrackV2
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct a tempo tracker that will operate on beat detection
|
||||
* function data calculated from audio at the given sample rate
|
||||
* with the given frame increment.
|
||||
*
|
||||
* Currently the sample rate and increment are used only for the
|
||||
* conversion from beat frame location to bpm in the tempo array.
|
||||
*/
|
||||
TempoTrackV2(float sampleRate, size_t dfIncrement);
|
||||
~TempoTrackV2();
|
||||
|
||||
// Returned beat periods are given in df increment units; tempi in bpm
|
||||
void calculateBeatPeriod(const vector<double> &df,
|
||||
vector<double> &beatPeriod,
|
||||
vector<double> &tempi);
|
||||
|
||||
// Returned beat positions are given in df increment units
|
||||
void calculateBeats(const vector<double> &df,
|
||||
const vector<double> &beatPeriod,
|
||||
vector<double> &beats);
|
||||
|
||||
private:
|
||||
typedef vector<int> i_vec_t;
|
||||
typedef vector<vector<int> > i_mat_t;
|
||||
typedef vector<double> d_vec_t;
|
||||
typedef vector<vector<double> > d_mat_t;
|
||||
|
||||
float m_rate;
|
||||
size_t m_increment;
|
||||
|
||||
void adapt_thresh(d_vec_t &df);
|
||||
double mean_array(const d_vec_t &dfin, int start, int end);
|
||||
void filter_df(d_vec_t &df);
|
||||
void get_rcf(const d_vec_t &dfframe, const d_vec_t &wv, d_vec_t &rcf);
|
||||
void viterbi_decode(const d_mat_t &rcfmat, const d_vec_t &wv,
|
||||
d_vec_t &bp, d_vec_t &tempi);
|
||||
double get_max_val(const d_vec_t &df);
|
||||
int get_max_ind(const d_vec_t &df);
|
||||
void normalise_vec(d_vec_t &df);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,144 @@
|
|||
/* -*- 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 2006 Martin Gasser.
|
||||
|
||||
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 "ChangeDetectionFunction.h"
|
||||
|
||||
#ifndef PI
|
||||
#define PI (3.14159265358979232846)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
ChangeDetectionFunction::ChangeDetectionFunction(ChangeDFConfig config) :
|
||||
m_dFilterSigma(0.0), m_iFilterWidth(0)
|
||||
{
|
||||
setFilterWidth(config.smoothingWidth);
|
||||
}
|
||||
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
dSmoothedValue += m_vaGaussian[iSkipLower + j++] * rCV[iPC];
|
||||
}
|
||||
|
||||
smoothedVector[iPC] = dSmoothedValue;
|
||||
}
|
||||
|
||||
smoothedTCSGram.addTCSVector(smoothedVector);
|
||||
}
|
||||
|
||||
for (int iPosition = 0; iPosition < rTCSGram.getSize(); iPosition++)
|
||||
{
|
||||
/*
|
||||
TODO: calculate a confidence measure for the current estimation
|
||||
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)
|
||||
{
|
||||
smoothedTCSGram.getTCSVector(iPosition-iWindow, previousTCS);
|
||||
// std::cout << previousTCS.magnitude() << std::endl;
|
||||
iWindow++;
|
||||
}
|
||||
|
||||
iWindow = 1;
|
||||
|
||||
// while (nextTCS.magnitude() < 0.1 && (iPosition+iWindow) < (rTCSGram.getSize()-1) )
|
||||
{
|
||||
smoothedTCSGram.getTCSVector(iPosition+iWindow, nextTCS);
|
||||
iWindow++;
|
||||
}
|
||||
|
||||
double distance = 0.0;
|
||||
// Euclidean distance
|
||||
for (size_t j = 0; j < 6; j++)
|
||||
{
|
||||
distance += std::pow(nextTCS[j] - previousTCS[j], 2.0);
|
||||
}
|
||||
|
||||
retVal[iPosition] = std::pow(distance, 0.5);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- 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 2006 Martin Gasser.
|
||||
|
||||
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 _CHANGEDETECTIONFUNCTION_
|
||||
#define _CHANGEDETECTIONFUNCTION_
|
||||
|
||||
//#define DEBUG_CHANGE_DETECTION_FUNCTION 1
|
||||
|
||||
#include "TCSgram.h"
|
||||
|
||||
#include <valarray>
|
||||
using std::valarray;
|
||||
|
||||
typedef valarray<double> ChangeDistance;
|
||||
|
||||
struct ChangeDFConfig
|
||||
{
|
||||
int smoothingWidth;
|
||||
};
|
||||
|
||||
class ChangeDetectionFunction
|
||||
{
|
||||
public:
|
||||
ChangeDetectionFunction(ChangeDFConfig);
|
||||
~ChangeDetectionFunction();
|
||||
ChangeDistance process(const TCSGram& rTCSGram);
|
||||
private:
|
||||
void setFilterWidth(const int iWidth);
|
||||
|
||||
private:
|
||||
valarray<double> m_vaGaussian;
|
||||
double m_dFilterSigma;
|
||||
int m_iFilterWidth;
|
||||
};
|
||||
|
||||
#endif // _CHANGDETECTIONFUNCTION_
|
|
@ -0,0 +1,77 @@
|
|||
/* -*- 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 2006 Martin Gasser.
|
||||
|
||||
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 "TCSgram.h"
|
||||
|
||||
#include <valarray>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#include "maths/MathUtilities.h"
|
||||
|
||||
TCSGram::TCSGram() :
|
||||
m_uNumBins(6)
|
||||
{
|
||||
}
|
||||
|
||||
TCSGram::~TCSGram()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void TCSGram::getTCSVector(int iPosition, TCSVector& rTCSVector) const
|
||||
{
|
||||
if (iPosition < 0)
|
||||
rTCSVector = TCSVector();
|
||||
else if (iPosition >= m_VectorList.size())
|
||||
rTCSVector = TCSVector();
|
||||
else
|
||||
rTCSVector = m_VectorList[iPosition].second;
|
||||
}
|
||||
|
||||
long TCSGram::getTime(size_t uPosition) const
|
||||
{
|
||||
return m_VectorList[uPosition].first;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
p.first = lMilliSeconds;
|
||||
p.second = rTCSVector;
|
||||
|
||||
m_VectorList.push_back(p);
|
||||
}
|
||||
|
||||
long TCSGram::getDuration() const
|
||||
{
|
||||
size_t uSize = m_VectorList.size();
|
||||
return static_cast<long>(uSize*m_dFrameDurationMS);
|
||||
}
|
||||
|
||||
void TCSGram::printDebug()
|
||||
{
|
||||
vectorlist_t::iterator vectorIterator = m_VectorList.begin();
|
||||
|
||||
while (vectorIterator != m_VectorList.end())
|
||||
{
|
||||
vectorIterator->second.printDebug();
|
||||
vectorIterator++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* -*- 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 2006 Martin Gasser.
|
||||
|
||||
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 _TCSGram_
|
||||
#define _TCSGram_
|
||||
|
||||
#include <vector>
|
||||
#include <valarray>
|
||||
#include <utility>
|
||||
|
||||
#include "TonalEstimator.h"
|
||||
|
||||
typedef std::vector<std::pair<long, TCSVector> > vectorlist_t;
|
||||
|
||||
class TCSGram
|
||||
{
|
||||
public:
|
||||
TCSGram();
|
||||
~TCSGram();
|
||||
void getTCSVector(int, TCSVector&) const;
|
||||
void addTCSVector(const TCSVector&);
|
||||
long getTime(size_t) const;
|
||||
long getDuration() const;
|
||||
void printDebug();
|
||||
int getSize() const { return m_VectorList.size(); }
|
||||
void reserve(size_t uSize) { m_VectorList.reserve(uSize); }
|
||||
void clear() { m_VectorList.clear(); }
|
||||
void setFrameDuration(const double dFrameDurationMS) { m_dFrameDurationMS = dFrameDurationMS; }
|
||||
void setNumBins(const unsigned int uNumBins) { m_uNumBins = uNumBins; }
|
||||
void normalize();
|
||||
protected:
|
||||
vectorlist_t m_VectorList;
|
||||
unsigned int m_uNumBins;
|
||||
double m_dFrameDurationMS;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
/* -*- 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 2006 Martin Gasser.
|
||||
|
||||
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 "TonalEstimator.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef PI
|
||||
#define PI (3.14159265358979232846)
|
||||
#endif
|
||||
|
||||
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);
|
||||
for (int iP = 0; iP < 12; iP++)
|
||||
{
|
||||
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);
|
||||
for (int iP = 0; iP < 12; iP++)
|
||||
{
|
||||
m_Basis[i][iP] = 0.6 * std::cos( (2.0 / 3.0) * iP * PI);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
|
||||
// circle of minor thirds
|
||||
m_Basis[i].resize(12);
|
||||
for (int iP = 0; iP < 12; iP++)
|
||||
{
|
||||
m_Basis[i][iP] = 1.1 * std::sin( (3.0 / 2.0) * iP * PI);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
m_Basis[i].resize(12);
|
||||
for (int iP = 0; iP < 12; iP++)
|
||||
{
|
||||
m_Basis[i][iP] = 1.1 * std::cos( (3.0 / 2.0) * iP * PI);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TonalEstimator::~TonalEstimator()
|
||||
{
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
vaRetVal[i] += m_Basis[i][iP] * rVector[iP];
|
||||
}
|
||||
}
|
||||
|
||||
return vaRetVal;
|
||||
}
|
|
@ -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 file copyright 2006 Martin Gasser.
|
||||
|
||||
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 _TONALESTIMATOR_
|
||||
#define _TONALESTIMATOR_
|
||||
|
||||
|
||||
#include <valarray>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
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++) ;
|
||||
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (size_t i = 0; i < 12; ++i) (*this)[i] = 0.0;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
class TCSVector : public std::valarray<double>
|
||||
{
|
||||
public:
|
||||
TCSVector() : std::valarray<double>()
|
||||
{ resize(6, 0.0f); }
|
||||
|
||||
virtual ~TCSVector() {};
|
||||
|
||||
void printDebug()
|
||||
{
|
||||
for (int i = 0; i < size(); i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TonalEstimator
|
||||
{
|
||||
public:
|
||||
TonalEstimator();
|
||||
virtual ~TonalEstimator();
|
||||
TCSVector transform2TCS(const ChromaVector& rVector);
|
||||
protected:
|
||||
std::valarray< std::valarray<double> > m_Basis;
|
||||
};
|
||||
|
||||
#endif // _TONALESTIMATOR_
|
|
@ -0,0 +1,181 @@
|
|||
/* -*- 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 is based on Don Cross's public domain FFT implementation.
|
||||
*/
|
||||
|
||||
#include "FFT.h"
|
||||
|
||||
#include "maths/MathUtilities.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
FFT::FFT(unsigned int n) :
|
||||
m_n(n),
|
||||
m_private(0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
FFT::~FFT()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FFTReal::FFTReal(unsigned int n) :
|
||||
m_n(n),
|
||||
m_private_real(0)
|
||||
{
|
||||
m_private_real = new FFT(m_n);
|
||||
}
|
||||
|
||||
FFTReal::~FFTReal()
|
||||
{
|
||||
delete (FFT *)m_private_real;
|
||||
}
|
||||
|
||||
void
|
||||
FFTReal::process(bool inverse,
|
||||
const double *realIn,
|
||||
double *realOut, double *imagOut)
|
||||
{
|
||||
((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;
|
||||
}
|
||||
|
||||
void
|
||||
FFT::process(bool p_bInverseTransform,
|
||||
const double *p_lpRealIn, const double *p_lpImagIn,
|
||||
double *p_lpRealOut, double *p_lpImagOut)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#ifndef FFT_H
|
||||
#define FFT_H
|
||||
|
||||
class FFT
|
||||
{
|
||||
public:
|
||||
FFT(unsigned int nsamples);
|
||||
virtual ~FFT();
|
||||
|
||||
void process(bool inverse,
|
||||
const double *realIn, const double *imagIn,
|
||||
double *realOut, double *imagOut);
|
||||
|
||||
private:
|
||||
unsigned int m_n;
|
||||
void *m_private;
|
||||
};
|
||||
|
||||
class FFTReal
|
||||
{
|
||||
public:
|
||||
FFTReal(unsigned int nsamples);
|
||||
~FFTReal();
|
||||
|
||||
void process(bool inverse,
|
||||
const double *realIn,
|
||||
double *realOut, double *imagOut);
|
||||
|
||||
private:
|
||||
unsigned int m_n;
|
||||
void *m_private_real;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,80 @@
|
|||
/* -*- 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 2009 Thomas Wilmering.
|
||||
|
||||
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 _WAVELET_H_
|
||||
#define _WAVELET_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Wavelet
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
Haar = 0,
|
||||
Daubechies_2,
|
||||
Daubechies_3,
|
||||
Daubechies_4,
|
||||
Daubechies_5,
|
||||
Daubechies_6,
|
||||
Daubechies_7,
|
||||
Daubechies_8,
|
||||
Daubechies_9,
|
||||
Daubechies_10,
|
||||
Daubechies_20,
|
||||
Daubechies_40,
|
||||
Symlet_2,
|
||||
Symlet_3,
|
||||
Symlet_4,
|
||||
Symlet_5,
|
||||
Symlet_6,
|
||||
Symlet_7,
|
||||
Symlet_8,
|
||||
Symlet_9,
|
||||
Symlet_10,
|
||||
Symlet_20,
|
||||
Symlet_30,
|
||||
Coiflet_1,
|
||||
Coiflet_2,
|
||||
Coiflet_3,
|
||||
Coiflet_4,
|
||||
Coiflet_5,
|
||||
Biorthogonal_1_3,
|
||||
Biorthogonal_1_5,
|
||||
Biorthogonal_2_2,
|
||||
Biorthogonal_2_4,
|
||||
Biorthogonal_2_6,
|
||||
Biorthogonal_2_8,
|
||||
Biorthogonal_3_1,
|
||||
Biorthogonal_3_3,
|
||||
Biorthogonal_3_5,
|
||||
Biorthogonal_3_7,
|
||||
Biorthogonal_3_9,
|
||||
Biorthogonal_4_4,
|
||||
Biorthogonal_5_5,
|
||||
Biorthogonal_6_8,
|
||||
Meyer,
|
||||
|
||||
LastType = Meyer
|
||||
};
|
||||
|
||||
static std::string getWaveletName(Type);
|
||||
|
||||
static void createDecompositionFilters(Type,
|
||||
std::vector<float> &lpd,
|
||||
std::vector<float> &hpd);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,837 @@
|
|||
/*
|
||||
* hmm.c
|
||||
*
|
||||
* Created by Mark Levy on 12/02/2006.
|
||||
* Copyright 2006 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 <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <float.h>
|
||||
#include <time.h> /* to seed random number generator */
|
||||
|
||||
#include <clapack.h> /* LAPACK for matrix inversion */
|
||||
|
||||
#include "maths/nan-inf.h"
|
||||
|
||||
#ifdef ATLAS_ORDER
|
||||
#define HAVE_ATLAS 1
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATLAS
|
||||
// Using ATLAS C interface to LAPACK
|
||||
#define dgetrf_(m, n, a, lda, ipiv, info) \
|
||||
clapack_dgetrf(CblasColMajor, *m, *n, a, *lda, ipiv)
|
||||
#define dgetri_(n, a, lda, ipiv, work, lwork, info) \
|
||||
clapack_dgetri(CblasColMajor, *n, a, *lda, ipiv)
|
||||
#endif
|
||||
|
||||
#ifdef _MAC_OS_X
|
||||
#include <vecLib/cblas.h>
|
||||
#else
|
||||
#include <cblas.h> /* BLAS for matrix multiplication */
|
||||
#endif
|
||||
|
||||
#include "hmm.h"
|
||||
|
||||
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->p0 = (double*) malloc(N*sizeof(double));
|
||||
model->a = (double**) malloc(N*sizeof(double*));
|
||||
model->mu = (double**) malloc(N*sizeof(double*));
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
model->a[i] = (double*) malloc(N*sizeof(double));
|
||||
model->mu[i] = (double*) malloc(L*sizeof(double));
|
||||
}
|
||||
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++)
|
||||
{
|
||||
global_mean[d] = 0;
|
||||
for (t = 0; t < T; t++)
|
||||
global_mean[d] += x[t][d];
|
||||
global_mean[d] /= T;
|
||||
}
|
||||
|
||||
/* calculate global diagonal covariance */
|
||||
for (d = 0; d < L; d++)
|
||||
{
|
||||
for (e = 0; e < L; e++)
|
||||
model->cov[d][e] = 0;
|
||||
for (t = 0; t < T; t++)
|
||||
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++)
|
||||
{
|
||||
for (d = 0; d < L; d++)
|
||||
{
|
||||
/* add some random noise related to covariance */
|
||||
/* 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++)
|
||||
{
|
||||
model->p0[i] = 1 + rand() / (double) RAND_MAX;
|
||||
s += model->p0[i];
|
||||
ss = 0;
|
||||
for (j = 0; j < N; j++)
|
||||
{
|
||||
model->a[i][j] = 1 + rand() / (double) RAND_MAX;
|
||||
ss += model->a[i][j];
|
||||
}
|
||||
for (j = 0; j < N; j++)
|
||||
{
|
||||
model->a[i][j] /= ss;
|
||||
}
|
||||
}
|
||||
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]);
|
||||
free(model->mu[i]);
|
||||
}
|
||||
free(model->a);
|
||||
free(model->mu);
|
||||
for (i = 0; i < model->L; i++)
|
||||
free(model->cov[i]);
|
||||
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 */
|
||||
double** gamma = (double**) malloc(T*sizeof(double*));
|
||||
double*** xi = (double***) malloc(T*sizeof(double**));
|
||||
for (t = 0; t < T; t++)
|
||||
{
|
||||
gamma[t] = (double*) malloc(N*sizeof(double));
|
||||
xi[t] = (double**) malloc(N*sizeof(double*));
|
||||
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));
|
||||
|
||||
/* 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 iter = 0;
|
||||
double loglik1, loglik2;
|
||||
int foundnan = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
/*
|
||||
if (allzero)
|
||||
{
|
||||
printf("all the b[t][i] were zero for t = %d, correcting...\n", t);
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
b[t][i] = 0.00001;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
/*
|
||||
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, "re-estimation...\n");
|
||||
fflush(stderr);
|
||||
*/
|
||||
if (ISNAN(loglik)) {
|
||||
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++)
|
||||
{
|
||||
for (j = 0; j < model->N; j++)
|
||||
printf("%f ", model->a[i][j]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n\n");
|
||||
*/
|
||||
//hmm_print(model);
|
||||
}
|
||||
|
||||
/* deallocate memory */
|
||||
for (t = 0; t < T; t++)
|
||||
{
|
||||
free(gamma[t]);
|
||||
free(b[t]);
|
||||
for (i = 0; i < N; i++)
|
||||
free(xi[t][i]);
|
||||
free(xi[t]);
|
||||
}
|
||||
free(gamma);
|
||||
free(xi);
|
||||
free(b);
|
||||
|
||||
for (i = 0; i < L; i++)
|
||||
free(icov[i]);
|
||||
free(icov);
|
||||
|
||||
free(gauss_y);
|
||||
free(gauss_z);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
/* re-estimate transition probs */
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
sum_gamma[i] = 0;
|
||||
for (t = 0; t < T-1; t++)
|
||||
sum_gamma[i] += gamma[t][i];
|
||||
}
|
||||
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
if (sum_gamma[i] == 0)
|
||||
{
|
||||
/* fprintf(stderr, "sum_gamma[%d] was zero...\n", i); */
|
||||
}
|
||||
//double s = 0;
|
||||
for (j = 0; j < N; j++)
|
||||
{
|
||||
a[i][j] = 0;
|
||||
if (sum_gamma[i] == 0.) continue;
|
||||
for (t = 0; t < T-1; t++)
|
||||
a[i][j] += xi[t][i][j];
|
||||
//s += a[i][j];
|
||||
a[i][j] /= sum_gamma[i];
|
||||
}
|
||||
/*
|
||||
for (j = 0; j < N; j++)
|
||||
{
|
||||
a[i][j] /= s;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/* 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];
|
||||
|
||||
/*
|
||||
for (d = 0; d < L; d++)
|
||||
{
|
||||
for (e = d; e < L; e++)
|
||||
{
|
||||
cov[d][e] = 0;
|
||||
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);
|
||||
for (j = 0; j < N; j++)
|
||||
for (i = 0; i < L; i++)
|
||||
if (ISNAN(mu[j][i]))
|
||||
printf("mu[%d][%d] was nan\n", j, i);
|
||||
for (t = 0; t < T; t++)
|
||||
for (j = 0; j < N; j++)
|
||||
if (ISNAN(gamma[t][j]))
|
||||
printf("gamma[%d][%d] was nan\n", t, j);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (d = 0; d < L; d++)
|
||||
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++)
|
||||
for (t = 0; t < T; 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; */
|
||||
|
||||
//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++)
|
||||
{
|
||||
for (d = 0; d < L; d++)
|
||||
{
|
||||
mu[j][d] = 0;
|
||||
for (t = 0; t < T; t++)
|
||||
mu[j][d] += gamma[t][j] * x[t][d];
|
||||
mu[j][d] /= sum_gamma[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* deallocate memory */
|
||||
free(sum_gamma);
|
||||
free(yy);
|
||||
free(yy2);
|
||||
free(u);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* 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++)
|
||||
{
|
||||
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];
|
||||
|
||||
//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++)
|
||||
{
|
||||
alpha[t][j] = 0;
|
||||
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)
|
||||
{
|
||||
printf("c[%d] = 0, going to blow up so exiting\n", t);
|
||||
for (i = 0; i < N; i++)
|
||||
if (b[t][i] == 0)
|
||||
fprintf(stderr, "b[%d][%d] was zero\n", t, i);
|
||||
fprintf(stderr, "x[t] was \n");
|
||||
for (i = 0; i < L; i++)
|
||||
fprintf(stderr, "%f ", x[t][i]);
|
||||
fprintf(stderr, "\n\n");
|
||||
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;
|
||||
t = T - 1;
|
||||
while (1)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
for (i = 0; i < N; i++)
|
||||
printf("%4.4e\t\t", alpha[t][i]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n\n");printf("beta:\n");
|
||||
for (t = 0; t < T; t++)
|
||||
{
|
||||
for (i = 0; i < N; i++)
|
||||
printf("%4.4e\t\t", beta[t][i]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n\n");
|
||||
*/
|
||||
|
||||
/* calculate posterior probs */
|
||||
double tot;
|
||||
for (t = 0; t < T; t++)
|
||||
{
|
||||
tot = 0;
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
gamma[t][i] = alpha[t][i] * beta[t][i];
|
||||
tot += gamma[t][i];
|
||||
}
|
||||
for (i = 0; i < N; 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;
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
for (j = 0; j < N; j++)
|
||||
{
|
||||
xi[t][i][j] = alpha[t][i] * a[i][j] * b[t+1][j] * beta[t+1][j];
|
||||
tot += xi[t][i][j];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < N; i++)
|
||||
for (j = 0; j < N; j++)
|
||||
xi[t][i][j] /= tot;
|
||||
}
|
||||
|
||||
/*
|
||||
// CHECK - fine
|
||||
// gamma[t][i] = \sum_j{xi[t][i][j]}
|
||||
tot = 0;
|
||||
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]);
|
||||
free(beta[t]);
|
||||
}
|
||||
free(alpha);
|
||||
free(beta);
|
||||
free(c);
|
||||
}
|
||||
|
||||
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*));
|
||||
for (t = 0; t < T; t++)
|
||||
{
|
||||
logb[t] = (double*) malloc(N*sizeof(double));
|
||||
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));
|
||||
|
||||
/* 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++)
|
||||
{
|
||||
max = -1000000;
|
||||
havemax = 0;
|
||||
|
||||
psi[t][j] = 0;
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
if (phi[t-1][i] + log(a[i][j]) > max || !havemax)
|
||||
{
|
||||
max = phi[t-1][i] + log(a[i][j]);
|
||||
phi[t][j] = max + logb[t][j];
|
||||
psi[t][j] = i;
|
||||
havemax = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* find maximising state at time T-1 */
|
||||
max = phi[T-1][0];
|
||||
q[T-1] = 0;
|
||||
for (i = 1; i < N; i++)
|
||||
{
|
||||
if (phi[T-1][i] > max)
|
||||
{
|
||||
max = phi[T-1][i];
|
||||
q[T-1] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* track back */
|
||||
t = T - 2;
|
||||
while (t >= 0)
|
||||
{
|
||||
q[t] = psi[t+1][q[t+1]];
|
||||
t--;
|
||||
}
|
||||
|
||||
/* de-allocate memory */
|
||||
for (i = 0; i < L; i++)
|
||||
free(icov[i]);
|
||||
free(icov);
|
||||
for (t = 0; t < T; t++)
|
||||
{
|
||||
free(logb[t]);
|
||||
free(phi[t]);
|
||||
free(psi[t]);
|
||||
}
|
||||
free(logb);
|
||||
free(phi);
|
||||
free(psi);
|
||||
|
||||
free(gauss_y);
|
||||
free(gauss_z);
|
||||
}
|
||||
|
||||
/* invert matrix and calculate determinant using LAPACK */
|
||||
void invert(double** cov, int L, double** icov, double* detcov)
|
||||
{
|
||||
/* copy square matrix into a vector in column-major order */
|
||||
double* a = (double*) malloc(L*L*sizeof(double));
|
||||
int i, j;
|
||||
for(j=0; j < L; j++)
|
||||
for (i=0; i < L; i++)
|
||||
a[j*L+i] = cov[i][j];
|
||||
|
||||
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)
|
||||
{
|
||||
fprintf(stderr, "Covariance matrix was singular, couldn't invert\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* find determinant */
|
||||
double det = 1;
|
||||
for(i = 0; i < L; i++)
|
||||
det *= a[i*L+i];
|
||||
// TODO: get this to work!!! If detcov < 0 then cov is bad anyway...
|
||||
/*
|
||||
int sign = 1;
|
||||
for (i = 0; i < L; i++)
|
||||
if (ipiv[i] != i)
|
||||
sign = -sign;
|
||||
det *= sign;
|
||||
*/
|
||||
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;
|
||||
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
|
||||
free(work);
|
||||
#endif
|
||||
free(a);
|
||||
}
|
||||
|
||||
/* probability of multivariate Gaussian given mean, inverse and determinant of covariance */
|
||||
double gauss(double* x, int L, double* mu, double** icov, double detcov, double* y, double* z)
|
||||
{
|
||||
int i, j;
|
||||
double s = 0;
|
||||
for (i = 0; i < L; i++)
|
||||
y[i] = x[i] - mu[i];
|
||||
for (i = 0; i < L; i++)
|
||||
{
|
||||
//z[i] = 0;
|
||||
//for (j = 0; j < L; j++)
|
||||
// z[i] += icov[i][j] * y[j];
|
||||
z[i] = cblas_ddot(L, &icov[i][0], 1, y, 1);
|
||||
}
|
||||
s = cblas_ddot(L, z, 1, y, 1);
|
||||
//for (i = 0; i < L; i++)
|
||||
// s += z[i] * y[i];
|
||||
|
||||
return exp(-s/2.0) / (pow(2*PI, L/2.0) * sqrt(detcov));
|
||||
}
|
||||
|
||||
/* log probability of multivariate Gaussian given mean, inverse and determinant of covariance */
|
||||
double loggauss(double* x, int L, double* mu, double** icov, double detcov, double* y, double* z)
|
||||
{
|
||||
int i, j;
|
||||
double s = 0;
|
||||
double ret;
|
||||
for (i = 0; i < L; i++)
|
||||
y[i] = x[i] - mu[i];
|
||||
for (i = 0; i < L; i++)
|
||||
{
|
||||
//z[i] = 0;
|
||||
//for (j = 0; j < L; j++)
|
||||
// z[i] += icov[i][j] * y[j];
|
||||
z[i] = cblas_ddot(L, &icov[i][0], 1, y, 1);
|
||||
}
|
||||
s = cblas_ddot(L, z, 1, y, 1);
|
||||
//for (i = 0; i < L; i++)
|
||||
// s += z[i] * y[i];
|
||||
|
||||
ret = -0.5 * (s + L * log(2*PI) + log(detcov));
|
||||
|
||||
/*
|
||||
// TEST
|
||||
if (ISINF(ret) > 0)
|
||||
printf("loggauss returning infinity\n");
|
||||
if (ISINF(ret) < 0)
|
||||
printf("loggauss returning -infinity\n");
|
||||
if (ISNAN(ret))
|
||||
printf("loggauss returning nan\n");
|
||||
*/
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void hmm_print(model_t* model)
|
||||
{
|
||||
int i, j;
|
||||
printf("p0:\n");
|
||||
for (i = 0; i < model->N; i++)
|
||||
printf("%f ", model->p0[i]);
|
||||
printf("\n\n");
|
||||
printf("a:\n");
|
||||
for (i = 0; i < model->N; i++)
|
||||
{
|
||||
for (j = 0; j < model->N; j++)
|
||||
printf("%f ", model->a[i][j]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n\n");
|
||||
printf("mu:\n");
|
||||
for (i = 0; i < model->N; i++)
|
||||
{
|
||||
for (j = 0; j < model->L; j++)
|
||||
printf("%f ", model->mu[i][j]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n\n");
|
||||
printf("cov:\n");
|
||||
for (i = 0; i < model->L; i++)
|
||||
{
|
||||
for (j = 0; j < model->L; j++)
|
||||
printf("%f ", model->cov[i][j]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef _HMM_H
|
||||
#define _HMM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* hmm.h
|
||||
*
|
||||
* Created by Mark Levy on 12/02/2006.
|
||||
* Copyright 2006 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 PI
|
||||
#define PI 3.14159265358979323846264338327950288
|
||||
#endif
|
||||
|
||||
typedef struct _model_t {
|
||||
int N; /* number of states */
|
||||
double* p0; /* initial probs */
|
||||
double** a; /* transition probs */
|
||||
int L; /* dimensionality of data */
|
||||
double** mu; /* state means */
|
||||
double** cov; /* covariance, tied between all states */
|
||||
} 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,
|
||||
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 */
|
||||
model_t* hmm_init(double** x, int T, int L, int N);
|
||||
void hmm_close(model_t* model);
|
||||
void invert(double** cov, int L, double** icov, double* detcov); /* uses LAPACK (included with Mac OSX) */
|
||||
double gauss(double* x, int L, double* mu, double** icov, double detcov, double* y, double* z);
|
||||
double loggauss(double* x, int L, double* mu, double** icov, double detcov, double* y, double* z);
|
||||
void hmm_print(model_t* model);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,578 @@
|
|||
#ifndef CBLAS_H
|
||||
#define CBLAS_H
|
||||
#include <stddef.h>
|
||||
|
||||
/* Allow the use in C++ code. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated and derived types
|
||||
*/
|
||||
#define CBLAS_INDEX size_t /* this may vary between platforms */
|
||||
|
||||
enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102};
|
||||
enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113};
|
||||
enum CBLAS_UPLO {CblasUpper=121, CblasLower=122};
|
||||
enum CBLAS_DIAG {CblasNonUnit=131, CblasUnit=132};
|
||||
enum CBLAS_SIDE {CblasLeft=141, CblasRight=142};
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* Prototypes for level 1 BLAS functions (complex are recast as routines)
|
||||
* ===========================================================================
|
||||
*/
|
||||
float cblas_sdsdot(const int N, const float alpha, const float *X,
|
||||
const int incX, const float *Y, const int incY);
|
||||
double cblas_dsdot(const int N, const float *X, const int incX, const float *Y,
|
||||
const int incY);
|
||||
float cblas_sdot(const int N, const float *X, const int incX,
|
||||
const float *Y, const int incY);
|
||||
double cblas_ddot(const int N, const double *X, const int incX,
|
||||
const double *Y, const int incY);
|
||||
|
||||
/*
|
||||
* Functions having prefixes Z and C only
|
||||
*/
|
||||
void cblas_cdotu_sub(const int N, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *dotu);
|
||||
void cblas_cdotc_sub(const int N, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *dotc);
|
||||
|
||||
void cblas_zdotu_sub(const int N, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *dotu);
|
||||
void cblas_zdotc_sub(const int N, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *dotc);
|
||||
|
||||
|
||||
/*
|
||||
* Functions having prefixes S D SC DZ
|
||||
*/
|
||||
float cblas_snrm2(const int N, const float *X, const int incX);
|
||||
float cblas_sasum(const int N, const float *X, const int incX);
|
||||
|
||||
double cblas_dnrm2(const int N, const double *X, const int incX);
|
||||
double cblas_dasum(const int N, const double *X, const int incX);
|
||||
|
||||
float cblas_scnrm2(const int N, const void *X, const int incX);
|
||||
float cblas_scasum(const int N, const void *X, const int incX);
|
||||
|
||||
double cblas_dznrm2(const int N, const void *X, const int incX);
|
||||
double cblas_dzasum(const int N, const void *X, const int incX);
|
||||
|
||||
|
||||
/*
|
||||
* Functions having standard 4 prefixes (S D C Z)
|
||||
*/
|
||||
CBLAS_INDEX cblas_isamax(const int N, const float *X, const int incX);
|
||||
CBLAS_INDEX cblas_idamax(const int N, const double *X, const int incX);
|
||||
CBLAS_INDEX cblas_icamax(const int N, const void *X, const int incX);
|
||||
CBLAS_INDEX cblas_izamax(const int N, const void *X, const int incX);
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* Prototypes for level 1 BLAS routines
|
||||
* ===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Routines with standard 4 prefixes (s, d, c, z)
|
||||
*/
|
||||
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,
|
||||
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,
|
||||
double *Y, const int incY);
|
||||
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 *Y, const int incY);
|
||||
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 *Y, const int incY);
|
||||
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);
|
||||
void cblas_srotmg(float *d1, float *d2, float *b1, const float b2, float *P);
|
||||
void cblas_srot(const int N, float *X, const int incX,
|
||||
float *Y, const int incY, const float c, const float s);
|
||||
void cblas_srotm(const int N, float *X, const int incX,
|
||||
float *Y, const int incY, const float *P);
|
||||
|
||||
void cblas_drotg(double *a, double *b, double *c, double *s);
|
||||
void cblas_drotmg(double *d1, double *d2, double *b1, const double b2, double *P);
|
||||
void cblas_drot(const int N, double *X, const int incX,
|
||||
double *Y, const int incY, const double c, const double s);
|
||||
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);
|
||||
void cblas_dscal(const int N, const double alpha, double *X, const int incX);
|
||||
void cblas_cscal(const int N, const void *alpha, void *X, const int incX);
|
||||
void cblas_zscal(const int N, const void *alpha, void *X, const int incX);
|
||||
void cblas_csscal(const int N, const float alpha, void *X, const int incX);
|
||||
void cblas_zdscal(const int N, const double alpha, void *X, const int incX);
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* Prototypes for level 2 BLAS
|
||||
* ===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Routines with standard 4 prefixes (S, D, C, Z)
|
||||
*/
|
||||
void cblas_sgemv(const enum CBLAS_ORDER order,
|
||||
const enum CBLAS_TRANSPOSE TransA, const int M, const int N,
|
||||
const float alpha, const float *A, const int lda,
|
||||
const float *X, const int incX, const float beta,
|
||||
float *Y, const int incY);
|
||||
void cblas_sgbmv(const enum CBLAS_ORDER order,
|
||||
const enum CBLAS_TRANSPOSE TransA, const int M, const int N,
|
||||
const int KL, const int KU, const float alpha,
|
||||
const float *A, const int lda, const float *X,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
const int N, const float *Ap, float *X, const int incX);
|
||||
void cblas_strsv(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, float *X,
|
||||
const int incX);
|
||||
void cblas_stbsv(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,
|
||||
float *X, const int incX);
|
||||
void cblas_stpsv(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 *Ap, float *X, const int incX);
|
||||
|
||||
void cblas_dgemv(const enum CBLAS_ORDER order,
|
||||
const enum CBLAS_TRANSPOSE TransA, const int M, const int N,
|
||||
const double alpha, const double *A, const int lda,
|
||||
const double *X, const int incX, const double beta,
|
||||
double *Y, const int incY);
|
||||
void cblas_dgbmv(const enum CBLAS_ORDER order,
|
||||
const enum CBLAS_TRANSPOSE TransA, const int M, const int N,
|
||||
const int KL, const int KU, const double alpha,
|
||||
const double *A, const int lda, const double *X,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
const int N, const double *Ap, double *X, const int incX);
|
||||
void cblas_dtrsv(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, double *X,
|
||||
const int incX);
|
||||
void cblas_dtbsv(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,
|
||||
double *X, const int incX);
|
||||
void cblas_dtpsv(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 *Ap, double *X, const int incX);
|
||||
|
||||
void cblas_cgemv(const enum CBLAS_ORDER order,
|
||||
const enum CBLAS_TRANSPOSE TransA, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *X, const int incX, const void *beta,
|
||||
void *Y, const int incY);
|
||||
void cblas_cgbmv(const enum CBLAS_ORDER order,
|
||||
const enum CBLAS_TRANSPOSE TransA, const int M, const int N,
|
||||
const int KL, const int KU, const void *alpha,
|
||||
const void *A, const int lda, const void *X,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
const int N, const void *Ap, void *X, const int incX);
|
||||
void cblas_ctrsv(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, void *X,
|
||||
const int incX);
|
||||
void cblas_ctbsv(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,
|
||||
void *X, const int incX);
|
||||
void cblas_ctpsv(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 *Ap, void *X, const int incX);
|
||||
|
||||
void cblas_zgemv(const enum CBLAS_ORDER order,
|
||||
const enum CBLAS_TRANSPOSE TransA, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *X, const int incX, const void *beta,
|
||||
void *Y, const int incY);
|
||||
void cblas_zgbmv(const enum CBLAS_ORDER order,
|
||||
const enum CBLAS_TRANSPOSE TransA, const int M, const int N,
|
||||
const int KL, const int KU, const void *alpha,
|
||||
const void *A, const int lda, const void *X,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
const int N, const void *Ap, void *X, const int incX);
|
||||
void cblas_ztrsv(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, void *X,
|
||||
const int incX);
|
||||
void cblas_ztbsv(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,
|
||||
void *X, const int incX);
|
||||
void cblas_ztpsv(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 *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,
|
||||
const int N, const float alpha, const float *A,
|
||||
const int lda, const float *X, const int incX,
|
||||
const float beta, float *Y, const int incY);
|
||||
void cblas_ssbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const int K, const float alpha, const float *A,
|
||||
const int lda, const float *X, const int incX,
|
||||
const float beta, float *Y, const int incY);
|
||||
void cblas_sspmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const float alpha, const float *Ap,
|
||||
const float *X, const int incX,
|
||||
const float beta, float *Y, const int incY);
|
||||
void cblas_sger(const enum CBLAS_ORDER order, const int M, const int N,
|
||||
const float alpha, const float *X, const int incX,
|
||||
const float *Y, const int incY, float *A, const int lda);
|
||||
void cblas_ssyr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const float alpha, const float *X,
|
||||
const int incX, float *A, const int lda);
|
||||
void cblas_sspr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const float alpha, const float *X,
|
||||
const int incX, float *Ap);
|
||||
void cblas_ssyr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const float alpha, const float *X,
|
||||
const int incX, const float *Y, const int incY, float *A,
|
||||
const int lda);
|
||||
void cblas_sspr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const float alpha, const float *X,
|
||||
const int incX, const float *Y, const int incY, float *A);
|
||||
|
||||
void cblas_dsymv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const double alpha, const double *A,
|
||||
const int lda, const double *X, const int incX,
|
||||
const double beta, double *Y, const int incY);
|
||||
void cblas_dsbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const int K, const double alpha, const double *A,
|
||||
const int lda, const double *X, const int incX,
|
||||
const double beta, double *Y, const int incY);
|
||||
void cblas_dspmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const double alpha, const double *Ap,
|
||||
const double *X, const int incX,
|
||||
const double beta, double *Y, const int incY);
|
||||
void cblas_dger(const enum CBLAS_ORDER order, const int M, const int N,
|
||||
const double alpha, const double *X, const int incX,
|
||||
const double *Y, const int incY, double *A, const int lda);
|
||||
void cblas_dsyr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const double alpha, const double *X,
|
||||
const int incX, double *A, const int lda);
|
||||
void cblas_dspr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const double alpha, const double *X,
|
||||
const int incX, double *Ap);
|
||||
void cblas_dsyr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const double alpha, const double *X,
|
||||
const int incX, const double *Y, const int incY, double *A,
|
||||
const int lda);
|
||||
void cblas_dspr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const double alpha, const double *X,
|
||||
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,
|
||||
const int N, const void *alpha, const void *A,
|
||||
const int lda, const void *X, const int incX,
|
||||
const void *beta, void *Y, const int incY);
|
||||
void cblas_chbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const int K, const void *alpha, const void *A,
|
||||
const int lda, const void *X, const int incX,
|
||||
const void *beta, void *Y, const int incY);
|
||||
void cblas_chpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const void *alpha, const void *Ap,
|
||||
const void *X, const int incX,
|
||||
const void *beta, void *Y, const int incY);
|
||||
void cblas_cgeru(const enum CBLAS_ORDER order, const int M, const int N,
|
||||
const void *alpha, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *A, const int lda);
|
||||
void cblas_cgerc(const enum CBLAS_ORDER order, const int M, const int N,
|
||||
const void *alpha, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *A, const int lda);
|
||||
void cblas_cher(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const float alpha, const void *X, const int incX,
|
||||
void *A, const int lda);
|
||||
void cblas_chpr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const float alpha, const void *X,
|
||||
const int incX, void *A);
|
||||
void cblas_cher2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N,
|
||||
const void *alpha, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *A, const int lda);
|
||||
void cblas_chpr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N,
|
||||
const void *alpha, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *Ap);
|
||||
|
||||
void cblas_zhemv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const void *alpha, const void *A,
|
||||
const int lda, const void *X, const int incX,
|
||||
const void *beta, void *Y, const int incY);
|
||||
void cblas_zhbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const int K, const void *alpha, const void *A,
|
||||
const int lda, const void *X, const int incX,
|
||||
const void *beta, void *Y, const int incY);
|
||||
void cblas_zhpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const void *alpha, const void *Ap,
|
||||
const void *X, const int incX,
|
||||
const void *beta, void *Y, const int incY);
|
||||
void cblas_zgeru(const enum CBLAS_ORDER order, const int M, const int N,
|
||||
const void *alpha, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *A, const int lda);
|
||||
void cblas_zgerc(const enum CBLAS_ORDER order, const int M, const int N,
|
||||
const void *alpha, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *A, const int lda);
|
||||
void cblas_zher(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const double alpha, const void *X, const int incX,
|
||||
void *A, const int lda);
|
||||
void cblas_zhpr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo,
|
||||
const int N, const double alpha, const void *X,
|
||||
const int incX, void *A);
|
||||
void cblas_zher2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N,
|
||||
const void *alpha, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *A, const int lda);
|
||||
void cblas_zhpr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N,
|
||||
const void *alpha, const void *X, const int incX,
|
||||
const void *Y, const int incY, void *Ap);
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* Prototypes for level 3 BLAS
|
||||
* ===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Routines with standard 4 prefixes (S, D, C, Z)
|
||||
*/
|
||||
void cblas_sgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_TRANSPOSE TransB, const int M, const int N,
|
||||
const int K, const float alpha, const float *A,
|
||||
const int lda, const float *B, const int ldb,
|
||||
const float beta, float *C, const int ldc);
|
||||
void cblas_ssymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const int M, const int N,
|
||||
const float alpha, const float *A, const int lda,
|
||||
const float *B, const int ldb, const float beta,
|
||||
float *C, const int ldc);
|
||||
void cblas_ssyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const float alpha, const float *A, const int lda,
|
||||
const float beta, float *C, const int ldc);
|
||||
void cblas_ssyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const float alpha, const float *A, const int lda,
|
||||
const float *B, const int ldb, const float beta,
|
||||
float *C, const int ldc);
|
||||
void cblas_strmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_DIAG Diag, const int M, const int N,
|
||||
const float alpha, const float *A, const int lda,
|
||||
float *B, const int ldb);
|
||||
void cblas_strsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_DIAG Diag, const int M, const int N,
|
||||
const float alpha, const float *A, const int lda,
|
||||
float *B, const int ldb);
|
||||
|
||||
void cblas_dgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_TRANSPOSE TransB, const int M, const int N,
|
||||
const int K, const double alpha, const double *A,
|
||||
const int lda, const double *B, const int ldb,
|
||||
const double beta, double *C, const int ldc);
|
||||
void cblas_dsymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const int M, const int N,
|
||||
const double alpha, const double *A, const int lda,
|
||||
const double *B, const int ldb, const double beta,
|
||||
double *C, const int ldc);
|
||||
void cblas_dsyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const double alpha, const double *A, const int lda,
|
||||
const double beta, double *C, const int ldc);
|
||||
void cblas_dsyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const double alpha, const double *A, const int lda,
|
||||
const double *B, const int ldb, const double beta,
|
||||
double *C, const int ldc);
|
||||
void cblas_dtrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_DIAG Diag, const int M, const int N,
|
||||
const double alpha, const double *A, const int lda,
|
||||
double *B, const int ldb);
|
||||
void cblas_dtrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_DIAG Diag, const int M, const int N,
|
||||
const double alpha, const double *A, const int lda,
|
||||
double *B, const int ldb);
|
||||
|
||||
void cblas_cgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_TRANSPOSE TransB, const int M, const int N,
|
||||
const int K, const void *alpha, const void *A,
|
||||
const int lda, const void *B, const int ldb,
|
||||
const void *beta, void *C, const int ldc);
|
||||
void cblas_csymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *B, const int ldb, const void *beta,
|
||||
void *C, const int ldc);
|
||||
void cblas_csyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *beta, void *C, const int ldc);
|
||||
void cblas_csyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *B, const int ldb, const void *beta,
|
||||
void *C, const int ldc);
|
||||
void cblas_ctrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_DIAG Diag, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
void *B, const int ldb);
|
||||
void cblas_ctrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_DIAG Diag, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
void *B, const int ldb);
|
||||
|
||||
void cblas_zgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_TRANSPOSE TransB, const int M, const int N,
|
||||
const int K, const void *alpha, const void *A,
|
||||
const int lda, const void *B, const int ldb,
|
||||
const void *beta, void *C, const int ldc);
|
||||
void cblas_zsymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *B, const int ldb, const void *beta,
|
||||
void *C, const int ldc);
|
||||
void cblas_zsyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *beta, void *C, const int ldc);
|
||||
void cblas_zsyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *B, const int ldb, const void *beta,
|
||||
void *C, const int ldc);
|
||||
void cblas_ztrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_DIAG Diag, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
void *B, const int ldb);
|
||||
void cblas_ztrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA,
|
||||
const enum CBLAS_DIAG Diag, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
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,
|
||||
const enum CBLAS_UPLO Uplo, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *B, const int ldb, const void *beta,
|
||||
void *C, const int ldc);
|
||||
void cblas_cherk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const float alpha, const void *A, const int lda,
|
||||
const float beta, void *C, const int ldc);
|
||||
void cblas_cher2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *B, const int ldb, const float beta,
|
||||
void *C, const int ldc);
|
||||
|
||||
void cblas_zhemm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
|
||||
const enum CBLAS_UPLO Uplo, const int M, const int N,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *B, const int ldb, const void *beta,
|
||||
void *C, const int ldc);
|
||||
void cblas_zherk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const double alpha, const void *A, const int lda,
|
||||
const double beta, void *C, const int ldc);
|
||||
void cblas_zher2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
|
||||
const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
|
||||
const void *alpha, const void *A, const int lda,
|
||||
const void *B, const int ldb, const double beta,
|
||||
void *C, const int ldc);
|
||||
|
||||
void cblas_xerbla(int p, const char *rout, const char *form, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "Correlation.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Correlation::Correlation()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Correlation::~Correlation()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Correlation::doAutoUnBiased(double *src, double *dst, unsigned int length)
|
||||
{
|
||||
double tmp = 0.0;
|
||||
double outVal = 0.0;
|
||||
|
||||
unsigned int i,j;
|
||||
|
||||
for( i = 0; i < length; i++)
|
||||
{
|
||||
for( j = i; j < length; j++)
|
||||
{
|
||||
tmp += src[ j-i ] * src[ j ];
|
||||
}
|
||||
|
||||
|
||||
outVal = tmp / ( length - i );
|
||||
|
||||
if( outVal <= 0 )
|
||||
dst[ i ] = EPS;
|
||||
else
|
||||
dst[ i ] = outVal;
|
||||
|
||||
tmp = 0.0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef CORRELATION_H
|
||||
#define CORRELATION_H
|
||||
|
||||
#define EPS 2.2204e-016
|
||||
|
||||
class Correlation
|
||||
{
|
||||
public:
|
||||
void doAutoUnBiased( double* src, double* dst, unsigned int length );
|
||||
Correlation();
|
||||
virtual ~Correlation();
|
||||
|
||||
};
|
||||
|
||||
#endif //
|
|
@ -0,0 +1,47 @@
|
|||
/* -*- 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 2008 Kurt Jacobson.
|
||||
|
||||
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 "CosineDistance.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
using std::cerr;
|
||||
|
||||
double CosineDistance::distance(const vector<double> &v1,
|
||||
const vector<double> &v2)
|
||||
{
|
||||
dist = 1.0; dDenTot = 0; dDen1 = 0; dDen2 = 0; dSum1 =0;
|
||||
double small = 1e-20;
|
||||
|
||||
//check if v1, v2 same size
|
||||
if (v1.size() != v2.size())
|
||||
{
|
||||
cerr << "CosineDistance::distance: ERROR: vectors not the same size\n";
|
||||
return 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i=0; i<v1.size(); i++)
|
||||
{
|
||||
dSum1 += v1[i]*v2[i];
|
||||
dDen1 += v1[i]*v1[i];
|
||||
dDen2 += v2[i]*v2[i];
|
||||
}
|
||||
dDenTot = sqrt(fabs(dDen1*dDen2)) + small;
|
||||
dist = 1-((dSum1)/dDenTot);
|
||||
return dist;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* -*- 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 2008 Kurt Jacobson.
|
||||
|
||||
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 COSINEDISTANCE_H
|
||||
#define COSINEDISTANCE_H
|
||||
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
using std::vector;
|
||||
|
||||
class CosineDistance
|
||||
{
|
||||
public:
|
||||
CosineDistance() { }
|
||||
~CosineDistance() { }
|
||||
|
||||
double distance(const vector<double> &v1, const vector<double> &v2);
|
||||
|
||||
protected:
|
||||
double dist, dDenTot, dDen1, dDen2, dSum1;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -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 file copyright 2008 QMUL
|
||||
|
||||
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 "KLDivergence.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
double KLDivergence::distanceGaussian(const vector<double> &m1,
|
||||
const vector<double> &v1,
|
||||
const vector<double> &m2,
|
||||
const vector<double> &v2)
|
||||
{
|
||||
int sz = m1.size();
|
||||
|
||||
double d = -2.0 * sz;
|
||||
double small = 1e-20;
|
||||
|
||||
for (int k = 0; k < sz; ++k) {
|
||||
|
||||
double kv1 = v1[k] + small;
|
||||
double kv2 = v2[k] + small;
|
||||
double km = (m1[k] - m2[k]) + small;
|
||||
|
||||
d += kv1 / kv2 + kv2 / kv1;
|
||||
d += km * (1.0 / kv1 + 1.0 / kv2) * km;
|
||||
}
|
||||
|
||||
d /= 2.0;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
double KLDivergence::distanceDistribution(const vector<double> &d1,
|
||||
const vector<double> &d2,
|
||||
bool symmetrised)
|
||||
{
|
||||
int sz = d1.size();
|
||||
|
||||
double d = 0;
|
||||
double small = 1e-20;
|
||||
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
d += d1[i] * log10((d1[i] + small) / (d2[i] + small));
|
||||
}
|
||||
|
||||
if (symmetrised) {
|
||||
d += distanceDistribution(d2, d1, false);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- 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 2008 QMUL.
|
||||
|
||||
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 KLDIVERGENCE_H
|
||||
#define KLDIVERGENCE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
using std::vector;
|
||||
|
||||
/**
|
||||
* Helper methods for calculating Kullback-Leibler divergences.
|
||||
*/
|
||||
class KLDivergence
|
||||
{
|
||||
public:
|
||||
KLDivergence() { }
|
||||
~KLDivergence() { }
|
||||
|
||||
/**
|
||||
* Calculate a symmetrised Kullback-Leibler divergence of Gaussian
|
||||
* models based on mean and variance vectors. All input vectors
|
||||
* must be of equal size.
|
||||
*/
|
||||
double distanceGaussian(const vector<double> &means1,
|
||||
const vector<double> &variances1,
|
||||
const vector<double> &means2,
|
||||
const vector<double> &variances2);
|
||||
|
||||
/**
|
||||
* Calculate a Kullback-Leibler divergence of two probability
|
||||
* distributions. Input vectors must be of equal size. If
|
||||
* symmetrised is true, the result will be the symmetrised
|
||||
* distance (equal to KL(d1, d2) + KL(d2, d1)).
|
||||
*/
|
||||
double distanceDistribution(const vector<double> &d1,
|
||||
const vector<double> &d2,
|
||||
bool symmetrised);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef MATHALIASES_H
|
||||
#define MATHALIASES_H
|
||||
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
|
||||
using namespace std;
|
||||
typedef complex<double> ComplexData;
|
||||
|
||||
|
||||
#ifndef PI
|
||||
#define PI (3.14159265358979232846)
|
||||
#endif
|
||||
|
||||
#define TWO_PI (2. * PI)
|
||||
|
||||
#define EPS 2.2204e-016
|
||||
|
||||
/* aliases to math.h functions */
|
||||
#define EXP exp
|
||||
#define COS cos
|
||||
#define SIN sin
|
||||
#define ABS fabs
|
||||
#define POW powf
|
||||
#define SQRT sqrtf
|
||||
#define LOG10 log10f
|
||||
#define LOG logf
|
||||
#define FLOOR floorf
|
||||
#define TRUNC truncf
|
||||
|
||||
/* aliases to complex.h functions */
|
||||
/** sample = EXPC(complex) */
|
||||
#define EXPC cexpf
|
||||
/** complex = CEXPC(complex) */
|
||||
#define CEXPC cexp
|
||||
/** sample = ARGC(complex) */
|
||||
#define ARGC cargf
|
||||
/** sample = ABSC(complex) norm */
|
||||
#define ABSC cabsf
|
||||
/** sample = REAL(complex) */
|
||||
#define REAL crealf
|
||||
/** sample = IMAG(complex) */
|
||||
#define IMAG cimagf
|
||||
|
||||
#endif
|
|
@ -0,0 +1,401 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#include "MathUtilities.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
double MathUtilities::mod(double x, double y)
|
||||
{
|
||||
double a = floor( x / y );
|
||||
|
||||
double b = x - ( y * a );
|
||||
return b;
|
||||
}
|
||||
|
||||
double MathUtilities::princarg(double ang)
|
||||
{
|
||||
double ValOut;
|
||||
|
||||
ValOut = mod( ang + M_PI, -2 * M_PI ) + M_PI;
|
||||
|
||||
return ValOut;
|
||||
}
|
||||
|
||||
void MathUtilities::getAlphaNorm(const double *data, unsigned int len, unsigned int alpha, double* ANorm)
|
||||
{
|
||||
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;
|
||||
a = ::pow( a, ( 1.0 / (double) alpha ) );
|
||||
|
||||
*ANorm = a;
|
||||
}
|
||||
|
||||
double MathUtilities::getAlphaNorm( const std::vector <double> &data, unsigned int alpha )
|
||||
{
|
||||
unsigned int 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;
|
||||
a = ::pow( a, ( 1.0 / (double) alpha ) );
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
double MathUtilities::round(double x)
|
||||
{
|
||||
double val = (double)floor(x + 0.5);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
double MathUtilities::median(const double *src, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
double tmp = 0.0;
|
||||
double tempMedian;
|
||||
double medianVal;
|
||||
|
||||
double* scratch = new double[ len ];//Vector < double > sortedX = Vector < double > ( size );
|
||||
|
||||
for ( i = 0; i < len; i++ )
|
||||
{
|
||||
scratch[i] = src[i];
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned int i ;
|
||||
double retVal =0.0;
|
||||
|
||||
for( i = 0; i < len; i++)
|
||||
{
|
||||
retVal += src[ i ];
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
double MathUtilities::mean(const double *src, unsigned int len)
|
||||
{
|
||||
double retVal =0.0;
|
||||
|
||||
double s = sum( src, len );
|
||||
|
||||
retVal = s / (double)len;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
double MathUtilities::mean(const std::vector<double> &src,
|
||||
unsigned int start,
|
||||
unsigned int count)
|
||||
{
|
||||
double sum = 0.;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
sum += src[start + i];
|
||||
}
|
||||
|
||||
return sum / count;
|
||||
}
|
||||
|
||||
void MathUtilities::getFrameMinMax(const double *data, unsigned int len, double *min, double *max)
|
||||
{
|
||||
unsigned int i;
|
||||
double temp = 0.0;
|
||||
double a=0.0;
|
||||
|
||||
if (len == 0) {
|
||||
*min = *max = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
*min = data[0];
|
||||
*max = data[0];
|
||||
|
||||
for( i = 0; i < len; i++)
|
||||
{
|
||||
temp = data[ i ];
|
||||
|
||||
if( temp < *min )
|
||||
{
|
||||
*min = temp ;
|
||||
}
|
||||
if( temp > *max )
|
||||
{
|
||||
*max = temp ;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
temp = pData[ i ];
|
||||
|
||||
if( temp > max )
|
||||
{
|
||||
max = temp ;
|
||||
index = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (pMax) *pMax = max;
|
||||
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
temp = data[ i ];
|
||||
|
||||
if( temp > max )
|
||||
{
|
||||
max = temp ;
|
||||
index = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (pMax) *pMax = max;
|
||||
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void MathUtilities::circShift( double* pData, int length, int shift)
|
||||
{
|
||||
shift = shift % length;
|
||||
double temp;
|
||||
int i,n;
|
||||
|
||||
for( i = 0; i < shift; i++)
|
||||
{
|
||||
temp=*(pData + length - 1);
|
||||
|
||||
for( n = length-2; n >= 0; n--)
|
||||
{
|
||||
*(pData+n+1)=*(pData+n);
|
||||
}
|
||||
|
||||
*pData = temp;
|
||||
}
|
||||
}
|
||||
|
||||
int MathUtilities::compareInt (const void * a, const void * b)
|
||||
{
|
||||
return ( *(int*)a - *(int*)b );
|
||||
}
|
||||
|
||||
void MathUtilities::normalise(double *data, int length, NormaliseType type)
|
||||
{
|
||||
switch (type) {
|
||||
|
||||
case NormaliseNone: return;
|
||||
|
||||
case NormaliseUnitSum:
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
sum += data[i];
|
||||
}
|
||||
if (sum != 0.0) {
|
||||
for (int i = 0; i < length; ++i) {
|
||||
data[i] /= sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NormaliseUnitMax:
|
||||
{
|
||||
double max = 0.0;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
if (fabs(data[i]) > max) {
|
||||
max = fabs(data[i]);
|
||||
}
|
||||
}
|
||||
if (max != 0.0) {
|
||||
for (int i = 0; i < length; ++i) {
|
||||
data[i] /= max;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void MathUtilities::normalise(std::vector<double> &data, NormaliseType type)
|
||||
{
|
||||
switch (type) {
|
||||
|
||||
case NormaliseNone: return;
|
||||
|
||||
case NormaliseUnitSum:
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < data.size(); ++i) sum += data[i];
|
||||
if (sum != 0.0) {
|
||||
for (int i = 0; i < data.size(); ++i) data[i] /= sum;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NormaliseUnitMax:
|
||||
{
|
||||
double max = 0.0;
|
||||
for (int i = 0; i < data.size(); ++i) {
|
||||
if (fabs(data[i]) > max) max = fabs(data[i]);
|
||||
}
|
||||
if (max != 0.0) {
|
||||
for (int i = 0; i < data.size(); ++i) data[i] /= max;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void MathUtilities::adaptiveThreshold(std::vector<double> &data)
|
||||
{
|
||||
int sz = int(data.size());
|
||||
if (sz == 0) return;
|
||||
|
||||
std::vector<double> smoothed(sz);
|
||||
|
||||
int p_pre = 8;
|
||||
int p_post = 7;
|
||||
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
|
||||
int first = std::max(0, i - p_pre);
|
||||
int last = std::min(sz - 1, i + p_post);
|
||||
|
||||
smoothed[i] = mean(data, first, last - first + 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
data[i] -= smoothed[i];
|
||||
if (data[i] < 0.0) data[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MathUtilities::isPowerOfTwo(int x)
|
||||
{
|
||||
if (x < 2) return false;
|
||||
if (x & (x-1)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
MathUtilities::nextPowerOfTwo(int x)
|
||||
{
|
||||
if (isPowerOfTwo(x)) return x;
|
||||
int n = 1;
|
||||
while (x) { x >>= 1; n <<= 1; }
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
MathUtilities::previousPowerOfTwo(int x)
|
||||
{
|
||||
if (isPowerOfTwo(x)) return x;
|
||||
int n = 1;
|
||||
x >>= 1;
|
||||
while (x) { x >>= 1; n <<= 1; }
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
MathUtilities::nearestPowerOfTwo(int x)
|
||||
{
|
||||
if (isPowerOfTwo(x)) return x;
|
||||
int n0 = previousPowerOfTwo(x), n1 = nearestPowerOfTwo(x);
|
||||
if (x - n0 < n1 - x) return n0;
|
||||
else return n1;
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/* -*- 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 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
|
||||
COPYING included with this distribution for more information.
|
||||
*/
|
||||
|
||||
#ifndef MATHUTILITIES_H
|
||||
#define MATHUTILITIES_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "nan-inf.h"
|
||||
|
||||
class MathUtilities
|
||||
{
|
||||
public:
|
||||
static double round( double x );
|
||||
|
||||
static void getFrameMinMax( const double* data, unsigned int len, double* min, double* max );
|
||||
|
||||
static double mean( const double* src, unsigned int len );
|
||||
static double mean( const std::vector<double> &data,
|
||||
unsigned int start, unsigned int count );
|
||||
static double sum( const double* src, unsigned int len );
|
||||
static double median( const double* src, unsigned int len );
|
||||
|
||||
static double princarg( double ang );
|
||||
static double mod( double x, double y);
|
||||
|
||||
static void getAlphaNorm(const double *data, unsigned int len, unsigned int alpha, double* ANorm);
|
||||
static double getAlphaNorm(const std::vector <double> &data, unsigned int alpha );
|
||||
|
||||
static void circShift( double* data, int length, int shift);
|
||||
|
||||
static int getMax( double* data, unsigned int length, double* max = 0 );
|
||||
static int getMax( const std::vector<double> &data, double* max = 0 );
|
||||
static int compareInt(const void * a, const void * b);
|
||||
|
||||
enum NormaliseType {
|
||||
NormaliseNone,
|
||||
NormaliseUnitSum,
|
||||
NormaliseUnitMax
|
||||
};
|
||||
|
||||
static void normalise(double *data, int length,
|
||||
NormaliseType n = NormaliseUnitMax);
|
||||
|
||||
static void normalise(std::vector<double> &data,
|
||||
NormaliseType n = NormaliseUnitMax);
|
||||
|
||||
// moving mean threshholding:
|
||||
static void adaptiveThreshold(std::vector<double> &data);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,407 @@
|
|||
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#ifndef PolyfitHPP
|
||||
#define PolyfitHPP
|
||||
//---------------------------------------------------------------------------
|
||||
// Least-squares curve fitting class for arbitrary data types
|
||||
/*
|
||||
|
||||
{ ******************************************
|
||||
**** Scientific Subroutine Library ****
|
||||
**** for C++ Builder ****
|
||||
******************************************
|
||||
|
||||
The following programs were written by Allen Miller and appear in the
|
||||
book entitled "Pascal Programs For Scientists And Engineers" which is
|
||||
published by Sybex, 1981.
|
||||
They were originally typed and submitted to MTPUG in Oct. 1982
|
||||
Juergen Loewner
|
||||
Hoher Heckenweg 3
|
||||
D-4400 Muenster
|
||||
They have had minor corrections and adaptations for Turbo Pascal by
|
||||
Jeff Weiss
|
||||
1572 Peacock Ave.
|
||||
Sunnyvale, CA 94087.
|
||||
|
||||
|
||||
2000 Oct 28 Updated for Delphi 4, open array parameters.
|
||||
This allows the routine to be generalised so that it is no longer
|
||||
hard-coded to make a specific order of best fit or work with a
|
||||
specific number of points.
|
||||
2001 Jan 07 Update Web site address
|
||||
|
||||
Copyright © David J Taylor, Edinburgh and others listed above
|
||||
Web site: www.satsignal.net
|
||||
E-mail: davidtaylor@writeme.com
|
||||
}*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Modified by CLandone for VC6 Aug 2004
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using std::vector;
|
||||
|
||||
class TPolyFit
|
||||
{
|
||||
typedef vector<vector<double> > Matrix;
|
||||
public:
|
||||
|
||||
static double PolyFit2 (const vector<double> &x, // does the work
|
||||
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
|
||||
vector<double> &g, // G = Y times X
|
||||
const int nrow, const int ncol);
|
||||
// Forms square coefficient matrix
|
||||
|
||||
static bool GaussJordan (Matrix &b, // square matrix of coefficients
|
||||
const vector<double> &y, // constant vector
|
||||
vector<double> &coef); // solution vector
|
||||
// returns false if matrix singular
|
||||
|
||||
static bool GaussJordan2(Matrix &b,
|
||||
const vector<double> &y,
|
||||
Matrix &w,
|
||||
vector<vector<int> > &index);
|
||||
};
|
||||
|
||||
// some utility functions
|
||||
|
||||
namespace NSUtility
|
||||
{
|
||||
inline void swap(double &a, double &b) {double t = a; a = b; b = t;}
|
||||
void zeroise(vector<double> &array, int n);
|
||||
void zeroise(vector<int> &array, int n);
|
||||
void zeroise(vector<vector<double> > &matrix, int m, int n);
|
||||
void zeroise(vector<vector<int> > &matrix, int m, int n);
|
||||
inline double sqr(const double &x) {return x * x;}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Implementation
|
||||
//---------------------------------------------------------------------------
|
||||
using namespace NSUtility;
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// main PolyFit routine
|
||||
|
||||
double TPolyFit::PolyFit2 (const vector<double> &x,
|
||||
const vector<double> &y,
|
||||
vector<double> &coefs)
|
||||
// nterms = coefs.size()
|
||||
// npoints = x.size()
|
||||
{
|
||||
int i, j;
|
||||
double xi, yi, yc, srs, sum_y, sum_y2;
|
||||
Matrix xmatr; // Data matrix
|
||||
Matrix a;
|
||||
vector<double> g; // Constant vector
|
||||
const int npoints(x.size());
|
||||
const int nterms(coefs.size());
|
||||
double correl_coef;
|
||||
zeroise(g, nterms);
|
||||
zeroise(a, nterms, nterms);
|
||||
zeroise(xmatr, npoints, nterms);
|
||||
if (nterms < 1) {
|
||||
std::cerr << "ERROR: PolyFit called with less than one term" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
if(npoints < 2) {
|
||||
std::cerr << "ERROR: PolyFit called with less than two points" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
if(npoints != y.size()) {
|
||||
std::cerr << "ERROR: PolyFit called with x and y of unequal size" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
for(i = 0; i < npoints; ++i)
|
||||
{
|
||||
// { setup x matrix }
|
||||
xi = x[i];
|
||||
xmatr[i][0] = 1.0; // { first column }
|
||||
for(j = 1; j < nterms; ++j)
|
||||
xmatr[i][j] = xmatr [i][j - 1] * xi;
|
||||
}
|
||||
Square (xmatr, y, a, g, npoints, nterms);
|
||||
if(!GaussJordan (a, g, coefs))
|
||||
return -1;
|
||||
sum_y = 0.0;
|
||||
sum_y2 = 0.0;
|
||||
srs = 0.0;
|
||||
for(i = 0; i < npoints; ++i)
|
||||
{
|
||||
yi = y[i];
|
||||
yc = 0.0;
|
||||
for(j = 0; j < nterms; ++j)
|
||||
yc += coefs [j] * xmatr [i][j];
|
||||
srs += sqr (yc - yi);
|
||||
sum_y += yi;
|
||||
sum_y2 += yi * yi;
|
||||
}
|
||||
|
||||
// If all Y values are the same, avoid dividing by zero
|
||||
correl_coef = sum_y2 - sqr (sum_y) / npoints;
|
||||
// Either return 0 or the correct value of correlation coefficient
|
||||
if (correl_coef != 0)
|
||||
correl_coef = srs / correl_coef;
|
||||
if (correl_coef >= 1)
|
||||
correl_coef = 0.0;
|
||||
else
|
||||
correl_coef = sqrt (1.0 - correl_coef);
|
||||
return correl_coef;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// Matrix multiplication routine
|
||||
// A = transpose X times X
|
||||
// G = Y times X
|
||||
|
||||
// Form square coefficient matrix
|
||||
|
||||
void TPolyFit::Square (const Matrix &x,
|
||||
const vector<double> &y,
|
||||
Matrix &a,
|
||||
vector<double> &g,
|
||||
const int nrow,
|
||||
const int ncol)
|
||||
{
|
||||
int i, k, l;
|
||||
for(k = 0; k < ncol; ++k)
|
||||
{
|
||||
for(l = 0; l < k + 1; ++l)
|
||||
{
|
||||
a [k][l] = 0.0;
|
||||
for(i = 0; i < nrow; ++i)
|
||||
{
|
||||
a[k][l] += x[i][l] * x [i][k];
|
||||
if(k != l)
|
||||
a[l][k] = a[k][l];
|
||||
}
|
||||
}
|
||||
g[k] = 0.0;
|
||||
for(i = 0; i < nrow; ++i)
|
||||
g[k] += y[i] * x[i][k];
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
bool TPolyFit::GaussJordan (Matrix &b,
|
||||
const vector<double> &y,
|
||||
vector<double> &coef)
|
||||
//b square matrix of coefficients
|
||||
//y constant vector
|
||||
//coef solution vector
|
||||
//ncol order of matrix got from b.size()
|
||||
|
||||
|
||||
{
|
||||
/*
|
||||
{ Gauss Jordan matrix inversion and solution }
|
||||
|
||||
{ B (n, n) coefficient matrix becomes inverse }
|
||||
{ Y (n) original constant vector }
|
||||
{ W (n, m) constant vector(s) become solution vector }
|
||||
{ DETERM is the determinant }
|
||||
{ ERROR = 1 if singular }
|
||||
{ INDEX (n, 3) }
|
||||
{ NV is number of constant vectors }
|
||||
*/
|
||||
|
||||
int ncol(b.size());
|
||||
int irow, icol;
|
||||
vector<vector<int> >index;
|
||||
Matrix w;
|
||||
|
||||
zeroise(w, ncol, ncol);
|
||||
zeroise(index, ncol, 3);
|
||||
|
||||
if(!GaussJordan2(b, y, w, index))
|
||||
return false;
|
||||
|
||||
// Interchange columns
|
||||
int m;
|
||||
for (int i = 0; i < ncol; ++i)
|
||||
{
|
||||
m = ncol - i - 1;
|
||||
if(index [m][0] != index [m][1])
|
||||
{
|
||||
irow = index [m][0];
|
||||
icol = index [m][1];
|
||||
for(int k = 0; k < ncol; ++k)
|
||||
swap (b[k][irow], b[k][icol]);
|
||||
}
|
||||
}
|
||||
|
||||
for(int k = 0; k < ncol; ++k)
|
||||
{
|
||||
if(index [k][2] != 0)
|
||||
{
|
||||
std::cerr << "ERROR: Error in PolyFit::GaussJordan: matrix is singular" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for( int i = 0; i < ncol; ++i)
|
||||
coef[i] = w[i][0];
|
||||
|
||||
|
||||
return true;
|
||||
} // end; { procedure GaussJordan }
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
bool TPolyFit::GaussJordan2(Matrix &b,
|
||||
const vector<double> &y,
|
||||
Matrix &w,
|
||||
vector<vector<int> > &index)
|
||||
{
|
||||
//GaussJordan2; // first half of GaussJordan
|
||||
// actual start of gaussj
|
||||
|
||||
double big, t;
|
||||
double pivot;
|
||||
double determ;
|
||||
int irow, icol;
|
||||
int ncol(b.size());
|
||||
int nv = 1; // single constant vector
|
||||
for(int i = 0; i < ncol; ++i)
|
||||
{
|
||||
w[i][0] = y[i]; // copy constant vector
|
||||
index[i][2] = -1;
|
||||
}
|
||||
determ = 1.0;
|
||||
for(int i = 0; i < ncol; ++i)
|
||||
{
|
||||
// Search for largest element
|
||||
big = 0.0;
|
||||
for(int j = 0; j < ncol; ++j)
|
||||
{
|
||||
if(index[j][2] != 0)
|
||||
{
|
||||
for(int k = 0; k < ncol; ++k)
|
||||
{
|
||||
if(index[k][2] > 0) {
|
||||
std::cerr << "ERROR: Error in PolyFit::GaussJordan2: matrix is singular" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(index[k][2] < 0 && fabs(b[j][k]) > big)
|
||||
{
|
||||
irow = j;
|
||||
icol = k;
|
||||
big = fabs(b[j][k]);
|
||||
}
|
||||
} // { k-loop }
|
||||
}
|
||||
} // { j-loop }
|
||||
index [icol][2] = index [icol][2] + 1;
|
||||
index [i][0] = irow;
|
||||
index [i][1] = icol;
|
||||
|
||||
// Interchange rows to put pivot on diagonal
|
||||
// GJ3
|
||||
if(irow != icol)
|
||||
{
|
||||
determ = -determ;
|
||||
for(int m = 0; m < ncol; ++m)
|
||||
swap (b [irow][m], b[icol][m]);
|
||||
if (nv > 0)
|
||||
for (int m = 0; m < nv; ++m)
|
||||
swap (w[irow][m], w[icol][m]);
|
||||
} // end GJ3
|
||||
|
||||
// divide pivot row by pivot column
|
||||
pivot = b[icol][icol];
|
||||
determ *= pivot;
|
||||
b[icol][icol] = 1.0;
|
||||
|
||||
for(int m = 0; m < ncol; ++m)
|
||||
b[icol][m] /= pivot;
|
||||
if(nv > 0)
|
||||
for(int m = 0; m < nv; ++m)
|
||||
w[icol][m] /= pivot;
|
||||
|
||||
// Reduce nonpivot rows
|
||||
for(int n = 0; n < ncol; ++n)
|
||||
{
|
||||
if(n != icol)
|
||||
{
|
||||
t = b[n][icol];
|
||||
b[n][icol] = 0.0;
|
||||
for(int m = 0; m < ncol; ++m)
|
||||
b[n][m] -= b[icol][m] * t;
|
||||
if(nv > 0)
|
||||
for(int m = 0; m < nv; ++m)
|
||||
w[n][m] -= w[icol][m] * t;
|
||||
}
|
||||
}
|
||||
} // { i-loop }
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
// Utility functions
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// fills a vector with zeros.
|
||||
void NSUtility::zeroise(vector<double> &array, int n)
|
||||
{
|
||||
array.clear();
|
||||
for(int j = 0; j < n; ++j)
|
||||
array.push_back(0);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// fills a vector with zeros.
|
||||
void NSUtility::zeroise(vector<int> &array, int n)
|
||||
{
|
||||
array.clear();
|
||||
for(int j = 0; j < n; ++j)
|
||||
array.push_back(0);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// fills a (m by n) matrix with zeros.
|
||||
void NSUtility::zeroise(vector<vector<double> > &matrix, int m, int n)
|
||||
{
|
||||
vector<double> zero;
|
||||
zeroise(zero, n);
|
||||
matrix.clear();
|
||||
for(int j = 0; j < m; ++j)
|
||||
matrix.push_back(zero);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// fills a (m by n) matrix with zeros.
|
||||
void NSUtility::zeroise(vector<vector<int> > &matrix, int m, int n)
|
||||
{
|
||||
vector<int> zero;
|
||||
zeroise(zero, n);
|
||||
matrix.clear();
|
||||
for(int j = 0; j < m; ++j)
|
||||
matrix.push_back(zero);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
#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
|
||||
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
3.0 3.0 3.0 3.0 3.0 3.0 35.0 45.0
|
||||
53.0 55.0 58.0 113.0 113.0 86.0 67.0 90.0
|
||||
3.5 3.5 4.0 4.0 4.5 4.5 46.0 59.0
|
||||
63.0 58.0 58.0 125.0 126.0 110.0 78.0 97.0
|
||||
4.0 4.0 4.5 4.5 5.0 5.0 48.0 60.0
|
||||
68.0 65.0 65.0 123.0 123.0 117.0 87.0 108.0
|
||||
5.0 5.0 5.0 5.5 5.5 5.5 46.0 63.0
|
||||
70.0 64.0 63.0 116.0 119.0 115.0 97.0 112.0
|
||||
6.0 6.0 6.0 6.0 6.5 6.5 51.0 69.0
|
||||
77.0 70.0 71.0 120.0 122.0 122.0 96.0 123.0
|
||||
11.0 11.0 11.0 11.0 11.0 11.0 64.0 75.0
|
||||
81.0 79.0 79.0 112.0 114.0 113.0 98.0 115.0
|
||||
20.0 20.0 20.0 20.0 20.0 20.0 76.0 86.0
|
||||
93.0 92.0 91.0 104.0 104.5 107.0 97.5 104.0
|
||||
30.0 30.0 30.0 30.0 30.1 30.2 84.0 96.0
|
||||
98.0 99.0 96.0 101.0 102.0 99.0 94.0 99.0
|
||||
30.0 33.4 36.8 40.0 43.0 45.6 100.0 106.0
|
||||
106.0 108.0 101.0 99.0 98.0 99.0 95.0 95.0
|
||||
42.0 44.0 46.0 48.0 50.0 51.0 109.0 111.0
|
||||
110.0 110.0 103.0 95.5 95.5 95.0 92.5 92.0
|
||||
60.0 61.7 63.5 65.5 67.3 69.2 122.0 124.0
|
||||
124.0 121.0 103.0 93.2 92.5 92.2 90.0 90.8
|
||||
70.0 70.1 70.2 70.3 70.4 70.5 137.0 132.0
|
||||
134.0 128.0 101.0 91.7 90.2 88.8 87.3 85.8
|
||||
78.0 77.6 77.2 76.8 76.4 76.0 167.0 159.0
|
||||
152.0 144.0 103.0 89.8 87.7 85.7 83.7 81.8
|
||||
98.9 97.8 96.7 95.5 94.3 93.2 183.0 172.0
|
||||
162.0 152.0 102.0 87.5 85.3 83.3 81.3 79.3
|
||||
160.0 157.0 155.0 152.0 149.0 147.0 186.0 175.0
|
||||
165.0 156.0 120.0 87.0 84.9 82.8 80.8 79.0
|
||||
272.0 266.0 260.0 254.0 248.0 242.0 192.0 182.0
|
||||
170.0 159.0 131.0 88.0 85.8 83.7 81.6 79.6
|
||||
382.0 372.0 362.0 352.0 343.0 333.0 205.0 192.0
|
||||
178.0 166.0 138.0 86.2 84.0 82.0 79.8 77.5
|
||||
770.0 740.0 710.0 680.0 650.0 618.0 226.0 207.0
|
||||
195.0 180.0 160.0 82.9 80.2 77.7 75.2 72.7
|
|
@ -0,0 +1,356 @@
|
|||
/*********************************/
|
||||
/* Principal Components Analysis */
|
||||
/*********************************/
|
||||
|
||||
/*********************************************************************/
|
||||
/* Principal Components Analysis or the Karhunen-Loeve expansion is a
|
||||
classical method for dimensionality reduction or exploratory data
|
||||
analysis. One reference among many is: F. Murtagh and A. Heck,
|
||||
Multivariate Data Analysis, Kluwer Academic, Dordrecht, 1987.
|
||||
|
||||
Author:
|
||||
F. Murtagh
|
||||
Phone: + 49 89 32006298 (work)
|
||||
+ 49 89 965307 (home)
|
||||
Earn/Bitnet: fionn@dgaeso51, fim@dgaipp1s, murtagh@stsci
|
||||
Span: esomc1::fionn
|
||||
Internet: murtagh@scivax.stsci.edu
|
||||
|
||||
F. Murtagh, Munich, 6 June 1989 */
|
||||
/*********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "pca.h"
|
||||
|
||||
#define SIGN(a, b) ( (b) < 0 ? -fabs(a) : fabs(a) )
|
||||
|
||||
/** Variance-covariance matrix: creation *****************************/
|
||||
|
||||
/* Create m * m covariance matrix from given n * m data matrix. */
|
||||
void covcol(double** data, int n, int m, double** symmat)
|
||||
{
|
||||
double *mean;
|
||||
int i, j, j1, j2;
|
||||
|
||||
/* Allocate storage for mean vector */
|
||||
|
||||
mean = (double*) malloc(m*sizeof(double));
|
||||
|
||||
/* Determine mean of column vectors of input data matrix */
|
||||
|
||||
for (j = 0; j < m; j++)
|
||||
{
|
||||
mean[j] = 0.0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
mean[j] += data[i][j];
|
||||
}
|
||||
mean[j] /= (double)n;
|
||||
}
|
||||
|
||||
/*
|
||||
printf("\nMeans of column vectors:\n");
|
||||
for (j = 0; j < m; j++) {
|
||||
printf("%12.1f",mean[j]); } printf("\n");
|
||||
*/
|
||||
|
||||
/* Center the column vectors. */
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
for (j = 0; j < m; j++)
|
||||
{
|
||||
data[i][j] -= mean[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the m * m covariance matrix. */
|
||||
for (j1 = 0; j1 < m; j1++)
|
||||
{
|
||||
for (j2 = j1; j2 < m; j2++)
|
||||
{
|
||||
symmat[j1][j2] = 0.0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
symmat[j1][j2] += data[i][j1] * data[i][j2];
|
||||
}
|
||||
symmat[j2][j1] = symmat[j1][j2];
|
||||
}
|
||||
}
|
||||
|
||||
free(mean);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/** Error handler **************************************************/
|
||||
|
||||
void erhand(char* err_msg)
|
||||
{
|
||||
fprintf(stderr,"Run-time error:\n");
|
||||
fprintf(stderr,"%s\n", err_msg);
|
||||
fprintf(stderr,"Exiting to system.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/** Reduce a real, symmetric matrix to a symmetric, tridiag. matrix. */
|
||||
|
||||
/* Householder reduction of matrix a to tridiagonal form.
|
||||
Algorithm: Martin et al., Num. Math. 11, 181-195, 1968.
|
||||
Ref: Smith et al., Matrix Eigensystem Routines -- EISPACK Guide
|
||||
Springer-Verlag, 1976, pp. 489-494.
|
||||
W H Press et al., Numerical Recipes in C, Cambridge U P,
|
||||
1988, pp. 373-374. */
|
||||
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;
|
||||
h = scale = 0.0;
|
||||
if (l > 0)
|
||||
{
|
||||
for (k = 0; k <= l; k++)
|
||||
scale += fabs(a[i][k]);
|
||||
if (scale == 0.0)
|
||||
e[i] = a[i][l];
|
||||
else
|
||||
{
|
||||
for (k = 0; k <= l; k++)
|
||||
{
|
||||
a[i][k] /= scale;
|
||||
h += a[i][k] * a[i][k];
|
||||
}
|
||||
f = a[i][l];
|
||||
g = f>0 ? -sqrt(h) : sqrt(h);
|
||||
e[i] = scale * g;
|
||||
h -= f * g;
|
||||
a[i][l] = f - g;
|
||||
f = 0.0;
|
||||
for (j = 0; j <= l; j++)
|
||||
{
|
||||
a[j][i] = a[i][j]/h;
|
||||
g = 0.0;
|
||||
for (k = 0; k <= j; k++)
|
||||
g += a[j][k] * a[i][k];
|
||||
for (k = j+1; k <= l; k++)
|
||||
g += a[k][j] * a[i][k];
|
||||
e[j] = g / h;
|
||||
f += e[j] * a[i][j];
|
||||
}
|
||||
hh = f / (h + h);
|
||||
for (j = 0; j <= l; j++)
|
||||
{
|
||||
f = a[i][j];
|
||||
e[j] = g = e[j] - hh * f;
|
||||
for (k = 0; k <= j; k++)
|
||||
a[j][k] -= (f * e[k] + g * a[i][k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
e[i] = a[i][l];
|
||||
d[i] = h;
|
||||
}
|
||||
d[0] = 0.0;
|
||||
e[0] = 0.0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
l = i - 1;
|
||||
if (d[i])
|
||||
{
|
||||
for (j = 0; j <= l; j++)
|
||||
{
|
||||
g = 0.0;
|
||||
for (k = 0; k <= l; k++)
|
||||
g += a[i][k] * a[k][j];
|
||||
for (k = 0; k <= l; k++)
|
||||
a[k][j] -= g * a[k][i];
|
||||
}
|
||||
}
|
||||
d[i] = a[i][i];
|
||||
a[i][i] = 1.0;
|
||||
for (j = 0; j <= l; j++)
|
||||
a[j][i] = a[i][j] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Tridiagonal QL algorithm -- Implicit **********************/
|
||||
|
||||
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;
|
||||
for (l = 0; l < n; l++)
|
||||
{
|
||||
iter = 0;
|
||||
do
|
||||
{
|
||||
for (m = l; m < n-1; m++)
|
||||
{
|
||||
dd = fabs(d[m]) + fabs(d[m+1]);
|
||||
if (fabs(e[m]) + dd == dd) break;
|
||||
}
|
||||
if (m != l)
|
||||
{
|
||||
if (iter++ == 30) erhand("No convergence in TLQI.");
|
||||
g = (d[l+1] - d[l]) / (2.0 * e[l]);
|
||||
r = sqrt((g * g) + 1.0);
|
||||
g = d[m] - d[l] + e[l] / (g + SIGN(r, g));
|
||||
s = c = 1.0;
|
||||
p = 0.0;
|
||||
for (i = m-1; i >= l; i--)
|
||||
{
|
||||
f = s * e[i];
|
||||
b = c * e[i];
|
||||
if (fabs(f) >= fabs(g))
|
||||
{
|
||||
c = g / f;
|
||||
r = sqrt((c * c) + 1.0);
|
||||
e[i+1] = f * r;
|
||||
c *= (s = 1.0/r);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = f / g;
|
||||
r = sqrt((s * s) + 1.0);
|
||||
e[i+1] = g * r;
|
||||
s *= (c = 1.0/r);
|
||||
}
|
||||
g = d[i+1] - p;
|
||||
r = (d[i] - g) * s + 2.0 * c * b;
|
||||
p = s * r;
|
||||
d[i+1] = g + p;
|
||||
g = c * r - b;
|
||||
for (k = 0; k < n; k++)
|
||||
{
|
||||
f = z[k][i+1];
|
||||
z[k][i+1] = s * z[k][i] + c * f;
|
||||
z[k][i] = c * z[k][i] - s * f;
|
||||
}
|
||||
}
|
||||
d[l] = d[l] - p;
|
||||
e[l] = g;
|
||||
e[m] = 0.0;
|
||||
}
|
||||
} while (m != l);
|
||||
}
|
||||
}
|
||||
|
||||
/* In place projection onto basis vectors */
|
||||
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);
|
||||
//for (i = 0; i < m; i++) {
|
||||
// for (j = 0; j < m; j++) {
|
||||
// symmat2[i][j] = symmat[i][j]; /* Needed below for col. projections */
|
||||
// }
|
||||
//}
|
||||
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. */
|
||||
|
||||
/*
|
||||
printf("\nEigenvalues:\n");
|
||||
for (j = m-1; j >= 0; j--) {
|
||||
printf("%18.5f\n", evals[j]); }
|
||||
printf("\n(Eigenvalues should be strictly positive; limited\n");
|
||||
printf("precision machine arithmetic may affect this.\n");
|
||||
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++) {
|
||||
for (i = 1; i <= 3; i++) {
|
||||
printf("%12.4f", symmat[j][m-i]); }
|
||||
printf("\n"); }
|
||||
*/
|
||||
|
||||
/* Form projections of row-points on prin. components. */
|
||||
/* Store in 'data', overwriting original data. */
|
||||
for (i = 0; i < n; i++) {
|
||||
for (j = 0; j < m; j++) {
|
||||
interm[j] = data[i][j]; } /* data[i][j] will be overwritten */
|
||||
for (k = 0; k < ncomponents; k++) {
|
||||
data[i][k] = 0.0;
|
||||
for (k2 = 0; k2 < m; k2++) {
|
||||
data[i][k] += interm[k2] * symmat[k2][m-k-1]; }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
printf("\nProjections of row-points on first 3 prin. comps.:\n");
|
||||
for (i = 0; i < n; i++) {
|
||||
for (j = 0; j < 3; j++) {
|
||||
printf("%12.4f", data[i][j]); }
|
||||
printf("\n"); }
|
||||
*/
|
||||
|
||||
/* Form projections of col.-points on first three prin. components. */
|
||||
/* Store in 'symmat2', overwriting what was stored in this. */
|
||||
//for (j = 0; j < m; j++) {
|
||||
// for (k = 0; k < m; k++) {
|
||||
// interm[k] = symmat2[j][k]; } /*symmat2[j][k] will be overwritten*/
|
||||
// for (i = 0; i < 3; i++) {
|
||||
// symmat2[j][i] = 0.0;
|
||||
// for (k2 = 0; k2 < m; k2++) {
|
||||
// symmat2[j][i] += interm[k2] * symmat[k2][m-i-1]; }
|
||||
// if (evals[m-i-1] > 0.0005) /* Guard against zero eigenvalue */
|
||||
// symmat2[j][i] /= sqrt(evals[m-i-1]); /* Rescale */
|
||||
// else
|
||||
// symmat2[j][i] = 0.0; /* Standard kludge */
|
||||
// }
|
||||
// }
|
||||
|
||||
/*
|
||||
printf("\nProjections of column-points on first 3 prin. comps.:\n");
|
||||
for (j = 0; j < m; j++) {
|
||||
for (k = 0; k < 3; k++) {
|
||||
printf("%12.4f", symmat2[j][k]); }
|
||||
printf("\n"); }
|
||||
*/
|
||||
|
||||
|
||||
for (i = 0; i < m; i++)
|
||||
free(symmat[i]);
|
||||
free(symmat);
|
||||
//FREE_ARRAY(symmat2,m);
|
||||
free(evals);
|
||||
free(interm);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef _PCA_H
|
||||
#define _PCA_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* pca.h
|
||||
*
|
||||
* Created by Mark Levy on 08/02/2006.
|
||||
* Copyright 2006 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.
|
||||
*
|
||||
*/
|
||||
|
||||
void pca_project(double** data, int n, int m, int ncomponents);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
|
||||
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
|
|
@ -0,0 +1,110 @@
|
|||
/* -*- 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 2009 QMUL.
|
||||
*/
|
||||
|
||||
#ifndef _ASYNCHRONOUS_TASK_H_
|
||||
#define _ASYNCHRONOUS_TASK_H_
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* AsynchronousTask provides a thread pattern implementation for
|
||||
* threads which are used to perform a series of similar operations in
|
||||
* parallel with other threads of the same type.
|
||||
*
|
||||
* For example, a thread used to calculate FFTs of a particular block
|
||||
* size in the context of a class that needs to calculate many block
|
||||
* sizes of FFT at once may be a candidate for an AsynchronousTask.
|
||||
*
|
||||
* The general use pattern is:
|
||||
*
|
||||
* caller -> request thread A calculate something
|
||||
* caller -> request thread B calculate something
|
||||
* caller -> request thread C calculate something
|
||||
* caller -> wait for threads A, B, and C
|
||||
*
|
||||
* Here threads A, B, and C may be AsynchronousTasks. An important
|
||||
* point is that the caller must be prepared to block when waiting for
|
||||
* these threads to complete (i.e. they are started asynchronously,
|
||||
* but testing for completion is synchronous).
|
||||
*/
|
||||
class AsynchronousTask : public Thread
|
||||
{
|
||||
public:
|
||||
AsynchronousTask() :
|
||||
m_todo("AsynchronousTask: task to perform"),
|
||||
m_done("AsynchronousTask: task complete"),
|
||||
m_inTask(false),
|
||||
m_finishing(false)
|
||||
{
|
||||
start();
|
||||
}
|
||||
virtual ~AsynchronousTask()
|
||||
{
|
||||
m_todo.lock();
|
||||
m_finishing = true;
|
||||
m_todo.signal();
|
||||
m_todo.unlock();
|
||||
wait();
|
||||
}
|
||||
|
||||
// Subclass must provide methods to request task and obtain
|
||||
// results, which the caller calls. The method that requests a
|
||||
// new task should set up any internal state and call startTask(),
|
||||
// which then calls back on the subclass implementation of
|
||||
// performTask from within its work thread. The method that
|
||||
// obtains results should call awaitTask() and then return any
|
||||
// results from internal state.
|
||||
|
||||
protected:
|
||||
void startTask() {
|
||||
m_done.lock();
|
||||
m_todo.lock();
|
||||
m_inTask = true;
|
||||
m_todo.signal();
|
||||
m_todo.unlock();
|
||||
}
|
||||
void awaitTask() {
|
||||
m_done.wait();
|
||||
m_done.unlock();
|
||||
}
|
||||
|
||||
virtual void performTask() = 0;
|
||||
|
||||
private:
|
||||
virtual void run() {
|
||||
m_todo.lock();
|
||||
while (1) {
|
||||
while (!m_inTask && !m_finishing) {
|
||||
m_todo.wait();
|
||||
}
|
||||
if (m_finishing) {
|
||||
m_done.lock();
|
||||
m_inTask = false;
|
||||
m_done.signal();
|
||||
m_done.unlock();
|
||||
break;
|
||||
}
|
||||
performTask();
|
||||
m_done.lock();
|
||||
m_inTask = false;
|
||||
m_done.signal();
|
||||
m_done.unlock();
|
||||
}
|
||||
m_todo.unlock();
|
||||
}
|
||||
|
||||
Condition m_todo;
|
||||
Condition m_done;
|
||||
bool m_inTask;
|
||||
bool m_finishing;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,189 @@
|
|||
/* -*- 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 is derived from the FSB Allocator by Juha Nieminen. The
|
||||
underlying method is unchanged, but the class has been refactored
|
||||
to permit multiple separate allocators (e.g. one per thread)
|
||||
rather than use a single global one (and to fit house style).
|
||||
|
||||
Copyright (c) 2008 Juha Nieminen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _BLOCK_ALLOCATOR_H_
|
||||
#define _BLOCK_ALLOCATOR_H_
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
/**
|
||||
* BlockAllocator is a simple allocator for fixed-size (usually small)
|
||||
* chunks of memory. The size of an element is specified in the
|
||||
* BlockAllocator constructor, and the functions allocate() and
|
||||
* deallocate() are used to obtain and release a single element at a
|
||||
* time.
|
||||
*
|
||||
* BlockAllocator may be an appropriate class to use in situations
|
||||
* involving a very large number of allocations and deallocations of
|
||||
* simple, identical objects across multiple threads (a hard situation
|
||||
* for a generic system malloc implementation to handle well). Retain
|
||||
* one BlockAllocator per thread (the class itself is not
|
||||
* thread-safe), and ensure that each thread uses its own allocator
|
||||
* exclusively.
|
||||
*
|
||||
* BlockAllocator is based on Juha Nieminen's more general
|
||||
* FSBAllocator.
|
||||
*/
|
||||
class BlockAllocator
|
||||
{
|
||||
public:
|
||||
typedef std::size_t data_t;
|
||||
|
||||
BlockAllocator(int elementSize) : m_sz(elementSize) { }
|
||||
|
||||
void *
|
||||
allocate()
|
||||
{
|
||||
if (m_freelist.empty()) {
|
||||
m_freelist.push_back(m_blocks.data.size());
|
||||
m_blocks.data.push_back(Block(this));
|
||||
}
|
||||
|
||||
const data_t index = m_freelist.back();
|
||||
Block &block = m_blocks.data[index];
|
||||
void *retval = block.allocate(index);
|
||||
if (block.isFull()) m_freelist.pop_back();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
deallocate(void *ptr)
|
||||
{
|
||||
if (!ptr) return;
|
||||
|
||||
data_t *unitPtr = (data_t *)ptr;
|
||||
const data_t blockIndex = unitPtr[elementSizeInDataUnits()];
|
||||
Block& block = m_blocks.data[blockIndex];
|
||||
|
||||
if (block.isFull()) m_freelist.push_back(blockIndex);
|
||||
block.deallocate(unitPtr);
|
||||
}
|
||||
|
||||
private:
|
||||
inline data_t elementsPerBlock() const {
|
||||
return 512;
|
||||
}
|
||||
inline data_t dataSize() const {
|
||||
return sizeof(data_t);
|
||||
}
|
||||
inline data_t elementSizeInDataUnits() const {
|
||||
return (m_sz + (dataSize() - 1)) / dataSize();
|
||||
}
|
||||
inline data_t unitSizeInDataUnits() const {
|
||||
return elementSizeInDataUnits() + 1;
|
||||
}
|
||||
inline data_t blockSizeInDataUnits() const {
|
||||
return elementsPerBlock() * unitSizeInDataUnits();
|
||||
}
|
||||
|
||||
class Block
|
||||
{
|
||||
public:
|
||||
Block(BlockAllocator *a) :
|
||||
m_a(a),
|
||||
m_block(0),
|
||||
m_firstFreeUnit(data_t(-1)),
|
||||
m_allocated(0),
|
||||
m_end(0)
|
||||
{}
|
||||
|
||||
~Block() {
|
||||
delete[] m_block;
|
||||
}
|
||||
|
||||
bool isFull() const {
|
||||
return m_allocated == m_a->elementsPerBlock();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
delete[] m_block;
|
||||
m_block = 0;
|
||||
m_firstFreeUnit = data_t(-1);
|
||||
}
|
||||
|
||||
void *allocate(data_t index) {
|
||||
|
||||
if (m_firstFreeUnit == data_t(-1)) {
|
||||
|
||||
if (!m_block) {
|
||||
m_block = new data_t[m_a->blockSizeInDataUnits()];
|
||||
m_end = 0;
|
||||
}
|
||||
|
||||
data_t *retval = m_block + m_end;
|
||||
m_end += m_a->unitSizeInDataUnits();
|
||||
retval[m_a->elementSizeInDataUnits()] = index;
|
||||
++m_allocated;
|
||||
return retval;
|
||||
|
||||
} else {
|
||||
|
||||
data_t *retval = m_block + m_firstFreeUnit;
|
||||
m_firstFreeUnit = *retval;
|
||||
++m_allocated;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(data_t *ptr) {
|
||||
|
||||
*ptr = m_firstFreeUnit;
|
||||
m_firstFreeUnit = ptr - m_block;
|
||||
|
||||
if (--m_allocated == 0) clear();
|
||||
}
|
||||
|
||||
private:
|
||||
const BlockAllocator *m_a;
|
||||
data_t *m_block;
|
||||
data_t m_firstFreeUnit;
|
||||
data_t m_allocated;
|
||||
data_t m_end;
|
||||
};
|
||||
|
||||
struct Blocks
|
||||
{
|
||||
std::vector<Block> data;
|
||||
|
||||
Blocks() {
|
||||
data.reserve(1024);
|
||||
}
|
||||
};
|
||||
|
||||
const int m_sz;
|
||||
Blocks m_blocks;
|
||||
std::vector<data_t> m_freelist;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,643 @@
|
|||
/* -*- 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 Chris Cannam, used with permission.
|
||||
*/
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
Thread::Thread() :
|
||||
m_id(0),
|
||||
m_extant(false)
|
||||
{
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Created thread object " << this << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl;
|
||||
#endif
|
||||
if (m_extant) {
|
||||
WaitForSingleObject(m_id, INFINITE);
|
||||
}
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Destroyed thread object " << this << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Thread::start()
|
||||
{
|
||||
m_id = CreateThread(NULL, 0, staticRun, this, 0, 0);
|
||||
if (!m_id) {
|
||||
cerr << "ERROR: thread creation failed" << endl;
|
||||
exit(1);
|
||||
} else {
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl;
|
||||
#endif
|
||||
m_extant = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Thread::wait()
|
||||
{
|
||||
if (m_extant) {
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl;
|
||||
#endif
|
||||
WaitForSingleObject(m_id, INFINITE);
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl;
|
||||
#endif
|
||||
m_extant = false;
|
||||
}
|
||||
}
|
||||
|
||||
Thread::Id
|
||||
Thread::id()
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
bool
|
||||
Thread::threadingAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD
|
||||
Thread::staticRun(LPVOID arg)
|
||||
{
|
||||
Thread *thread = static_cast<Thread *>(arg);
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: " << (void *)GetCurrentThreadId() << ": Running thread " << thread->m_id << " for thread object " << thread << endl;
|
||||
#endif
|
||||
thread->run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Mutex::Mutex()
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
:
|
||||
m_lockedBy(-1)
|
||||
#endif
|
||||
{
|
||||
m_mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
CloseHandle(m_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
Mutex::lock()
|
||||
{
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
if (m_lockedBy == tid) {
|
||||
cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl;
|
||||
}
|
||||
#endif
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
WaitForSingleObject(m_mutex, INFINITE);
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
m_lockedBy = tid;
|
||||
#endif
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Mutex::unlock()
|
||||
{
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
if (m_lockedBy != tid) {
|
||||
cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
m_lockedBy = -1;
|
||||
#endif
|
||||
ReleaseMutex(m_mutex);
|
||||
}
|
||||
|
||||
bool
|
||||
Mutex::trylock()
|
||||
{
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
#endif
|
||||
DWORD result = WaitForSingleObject(m_mutex, 0);
|
||||
if (result == WAIT_TIMEOUT || result == WAIT_FAILED) {
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl;
|
||||
#endif
|
||||
return false;
|
||||
} else {
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
m_lockedBy = tid;
|
||||
#endif
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Condition::Condition(string name) :
|
||||
m_locked(false)
|
||||
#ifdef DEBUG_CONDITION
|
||||
, m_name(name)
|
||||
#endif
|
||||
{
|
||||
m_mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
m_condition = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
if (m_locked) ReleaseMutex(m_mutex);
|
||||
CloseHandle(m_condition);
|
||||
CloseHandle(m_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
Condition::lock()
|
||||
{
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
WaitForSingleObject(m_mutex, INFINITE);
|
||||
m_locked = true;
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Condition::unlock()
|
||||
{
|
||||
if (!m_locked) {
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
m_locked = false;
|
||||
ReleaseMutex(m_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
Condition::wait(int us)
|
||||
{
|
||||
if (us == 0) {
|
||||
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
SignalObjectAndWait(m_mutex, m_condition, INFINITE, FALSE);
|
||||
WaitForSingleObject(m_mutex, INFINITE);
|
||||
|
||||
} else {
|
||||
|
||||
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
|
||||
SignalObjectAndWait(m_mutex, m_condition, ms, FALSE);
|
||||
WaitForSingleObject(m_mutex, INFINITE);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
|
||||
m_locked = true;
|
||||
}
|
||||
|
||||
void
|
||||
Condition::signal()
|
||||
{
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
SetEvent(m_condition);
|
||||
}
|
||||
|
||||
#else /* !_WIN32 */
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
|
||||
Thread::Thread() :
|
||||
m_id(0),
|
||||
m_extant(false)
|
||||
{
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Created thread object " << this << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl;
|
||||
#endif
|
||||
if (m_extant) {
|
||||
pthread_join(m_id, 0);
|
||||
}
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Destroyed thread object " << this << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Thread::start()
|
||||
{
|
||||
if (pthread_create(&m_id, 0, staticRun, this)) {
|
||||
cerr << "ERROR: thread creation failed" << endl;
|
||||
exit(1);
|
||||
} else {
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl;
|
||||
#endif
|
||||
m_extant = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Thread::wait()
|
||||
{
|
||||
if (m_extant) {
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl;
|
||||
#endif
|
||||
pthread_join(m_id, 0);
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl;
|
||||
#endif
|
||||
m_extant = false;
|
||||
}
|
||||
}
|
||||
|
||||
Thread::Id
|
||||
Thread::id()
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
bool
|
||||
Thread::threadingAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void *
|
||||
Thread::staticRun(void *arg)
|
||||
{
|
||||
Thread *thread = static_cast<Thread *>(arg);
|
||||
#ifdef DEBUG_THREAD
|
||||
cerr << "THREAD DEBUG: " << (void *)pthread_self() << ": Running thread " << thread->m_id << " for thread object " << thread << endl;
|
||||
#endif
|
||||
thread->run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Mutex::Mutex()
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
:
|
||||
m_lockedBy(0),
|
||||
m_locked(false)
|
||||
#endif
|
||||
{
|
||||
pthread_mutex_init(&m_mutex, 0);
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Initialised mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Destroying mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
Mutex::lock()
|
||||
{
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
pthread_t tid = pthread_self();
|
||||
if (m_locked && m_lockedBy == tid) {
|
||||
cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl;
|
||||
}
|
||||
#endif
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
m_lockedBy = tid;
|
||||
m_locked = true;
|
||||
#endif
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Mutex::unlock()
|
||||
{
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
pthread_t tid = pthread_self();
|
||||
if (!m_locked) {
|
||||
cerr << "ERROR: Mutex " << &m_mutex << " not locked in unlock" << endl;
|
||||
return;
|
||||
} else if (m_lockedBy != tid) {
|
||||
cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl;
|
||||
#endif
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
m_locked = false;
|
||||
#endif
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
|
||||
bool
|
||||
Mutex::trylock()
|
||||
{
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
pthread_t tid = pthread_self();
|
||||
#endif
|
||||
if (pthread_mutex_trylock(&m_mutex)) {
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl;
|
||||
#endif
|
||||
return false;
|
||||
} else {
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
m_lockedBy = tid;
|
||||
m_locked = true;
|
||||
#endif
|
||||
#ifdef DEBUG_MUTEX
|
||||
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Condition::Condition(string name) :
|
||||
m_locked(false)
|
||||
#ifdef DEBUG_CONDITION
|
||||
, m_name(name)
|
||||
#endif
|
||||
{
|
||||
pthread_mutex_init(&m_mutex, 0);
|
||||
pthread_cond_init(&m_condition, 0);
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
if (m_locked) pthread_mutex_unlock(&m_mutex);
|
||||
pthread_cond_destroy(&m_condition);
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
Condition::lock()
|
||||
{
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
m_locked = true;
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Condition::unlock()
|
||||
{
|
||||
if (!m_locked) {
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
m_locked = false;
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
Condition::wait(int us)
|
||||
{
|
||||
if (us == 0) {
|
||||
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
pthread_cond_wait(&m_condition, &m_mutex);
|
||||
|
||||
} else {
|
||||
|
||||
struct timeval now;
|
||||
gettimeofday(&now, 0);
|
||||
|
||||
now.tv_usec += us;
|
||||
while (now.tv_usec > 1000000) {
|
||||
now.tv_usec -= 1000000;
|
||||
++now.tv_sec;
|
||||
}
|
||||
|
||||
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
|
||||
pthread_cond_timedwait(&m_condition, &m_mutex, &timeout);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
|
||||
m_locked = true;
|
||||
}
|
||||
|
||||
void
|
||||
Condition::signal()
|
||||
{
|
||||
#ifdef DEBUG_CONDITION
|
||||
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl;
|
||||
#endif
|
||||
pthread_cond_signal(&m_condition);
|
||||
}
|
||||
|
||||
#else /* !USE_PTHREADS */
|
||||
|
||||
Thread::Thread()
|
||||
{
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Thread::start()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void
|
||||
Thread::wait()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
Thread::Id
|
||||
Thread::id()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
bool
|
||||
Thread::threadingAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Mutex::lock()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void
|
||||
Mutex::unlock()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
bool
|
||||
Mutex::trylock()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
Condition::Condition(const char *)
|
||||
{
|
||||
}
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Condition::lock()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void
|
||||
Condition::wait(int us)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void
|
||||
Condition::signal()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif /* !USE_PTHREADS */
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
MutexLocker::MutexLocker(Mutex *mutex) :
|
||||
m_mutex(mutex)
|
||||
{
|
||||
if (m_mutex) {
|
||||
m_mutex->lock();
|
||||
}
|
||||
}
|
||||
|
||||
MutexLocker::~MutexLocker()
|
||||
{
|
||||
if (m_mutex) {
|
||||
m_mutex->unlock();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/* -*- 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 Chris Cannam, used with permission.
|
||||
*/
|
||||
|
||||
#ifndef _THREAD_H_
|
||||
#define _THREAD_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else /* !_WIN32 */
|
||||
#ifdef USE_PTHREADS
|
||||
#include <pthread.h>
|
||||
#endif /* USE_PTHREADS */
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
#include <string>
|
||||
|
||||
//#define DEBUG_THREAD 1
|
||||
//#define DEBUG_MUTEX 1
|
||||
//#define DEBUG_CONDITION 1
|
||||
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
typedef HANDLE Id;
|
||||
#else
|
||||
#ifdef USE_PTHREADS
|
||||
typedef pthread_t Id;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
|
||||
Id id();
|
||||
|
||||
void start();
|
||||
void wait();
|
||||
|
||||
static bool threadingAvailable();
|
||||
|
||||
protected:
|
||||
virtual void run() = 0;
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
HANDLE m_id;
|
||||
bool m_extant;
|
||||
static DWORD WINAPI staticRun(LPVOID lpParam);
|
||||
#else
|
||||
#ifdef USE_PTHREADS
|
||||
pthread_t m_id;
|
||||
bool m_extant;
|
||||
static void *staticRun(void *);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
class Mutex
|
||||
{
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
bool trylock();
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
HANDLE m_mutex;
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
DWORD m_lockedBy;
|
||||
#endif
|
||||
#else
|
||||
#ifdef USE_PTHREADS
|
||||
pthread_mutex_t m_mutex;
|
||||
#ifndef NO_THREAD_CHECKS
|
||||
pthread_t m_lockedBy;
|
||||
bool m_locked;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
class MutexLocker
|
||||
{
|
||||
public:
|
||||
MutexLocker(Mutex *);
|
||||
~MutexLocker();
|
||||
|
||||
private:
|
||||
Mutex *m_mutex;
|
||||
};
|
||||
|
||||
class Condition
|
||||
{
|
||||
public:
|
||||
Condition(std::string name);
|
||||
~Condition();
|
||||
|
||||
// Condition bundles a pthread-style condition variable and mutex
|
||||
// into one class.
|
||||
|
||||
// To wait on a condition, call lock(), test termination variables
|
||||
// as appropriate, and then wait(). The condition will be
|
||||
// unlocked for the duration of the wait() call, which will end
|
||||
// when the condition is signalled. The condition will be locked
|
||||
// again when wait() returns.
|
||||
//
|
||||
// To signal a condition, call signal(). If the waiting thread
|
||||
// will be performing tests between its own lock() and wait(),
|
||||
// then the signalling thread should also lock() before it signals
|
||||
// (and then unlock afterwards). If the signalling thread always
|
||||
// locks the mutex during signalling, then the waiting thread
|
||||
// knows that signals will only happen during wait() and not be
|
||||
// missed at other times.
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
void wait(int us = 0);
|
||||
|
||||
void signal();
|
||||
|
||||
private:
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE m_mutex;
|
||||
HANDLE m_condition;
|
||||
bool m_locked;
|
||||
#else
|
||||
#ifdef USE_PTHREADS
|
||||
pthread_mutex_t m_mutex;
|
||||
pthread_cond_t m_condition;
|
||||
bool m_locked;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef DEBUG_CONDITION
|
||||
std::string m_name;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python
|
||||
import autowaf
|
||||
import os
|
||||
|
||||
# Version of this package (even if built as a child)
|
||||
QM_DSP_VERSION = '0.0.0'
|
||||
|
||||
# Library version (UNIX style major, minor, micro)
|
||||
# major increment <=> incompatible changes
|
||||
# minor increment <=> compatible changes (additions)
|
||||
# micro increment <=> no interface changes
|
||||
QM_DSP_LIB_VERSION = '0.0.0'
|
||||
|
||||
# Variables for 'waf dist'
|
||||
APPNAME = 'qm-dsp'
|
||||
VERSION = QM_DSP_VERSION
|
||||
|
||||
# Mandatory variables
|
||||
srcdir = '.'
|
||||
blddir = 'build'
|
||||
|
||||
def set_options(opt):
|
||||
autowaf.set_options(opt)
|
||||
|
||||
def configure(conf):
|
||||
autowaf.configure(conf)
|
||||
conf.check_tool('compiler_cxx')
|
||||
|
||||
def build(bld):
|
||||
# Host Library
|
||||
obj = bld.new_task_gen('cxx', 'shlib')
|
||||
obj.source = '''
|
||||
dsp/onsets/DetectionFunction.cpp
|
||||
dsp/onsets/PeakPicking.cpp
|
||||
dsp/phasevocoder/PhaseVocoder.cpp
|
||||
dsp/rateconversion/Decimator.cpp
|
||||
dsp/rhythm/BeatSpectrum.cpp
|
||||
dsp/signalconditioning/DFProcess.cpp
|
||||
dsp/signalconditioning/Filter.cpp
|
||||
dsp/signalconditioning/FiltFilt.cpp
|
||||
dsp/signalconditioning/Framer.cpp
|
||||
dsp/transforms/FFT.cpp
|
||||
dsp/wavelet/Wavelet.cpp
|
||||
maths/Correlation.cpp
|
||||
maths/CosineDistance.cpp
|
||||
maths/KLDivergence.cpp
|
||||
maths/MathUtilities.cpp
|
||||
base/Pitch.cpp
|
||||
'''
|
||||
obj.export_incdirs = ['.']
|
||||
obj.includes = ['.']
|
||||
obj.name = 'libqmdsp'
|
||||
obj.target = 'qmdsp'
|
||||
obj.vnum = QM_DSP_VERSION
|
||||
obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3')
|
||||
|
||||
def shutdown():
|
||||
autowaf.shutdown()
|
||||
|
Loading…
Reference in New Issue