diff options
author | Daniel Schürmann <daschuer@mixxx.org> | 2019-06-03 00:17:56 +0200 |
---|---|---|
committer | Daniel Schürmann <daschuer@mixxx.org> | 2019-06-03 01:41:18 +0200 |
commit | a6b58824fa2fd5a67a7d7feff79ba1e01e2cc2ad (patch) | |
tree | 9d893e45d7e4e456cf3e4d4a5686df9fafedd11e /lib | |
parent | a4db708c987084a6c990125232eabdff35fdba51 (diff) |
Update qm-dsp to current master https://github.com/c4dm/qm-dsp/commit/4c406d9990bc42e5e298dc28eb8b897095275683
Diffstat (limited to 'lib')
48 files changed, 1533 insertions, 831 deletions
diff --git a/lib/qm-dsp/CONTRIBUTING.md b/lib/qm-dsp/CONTRIBUTING.md new file mode 100644 index 0000000000..6f6229e610 --- /dev/null +++ b/lib/qm-dsp/CONTRIBUTING.md @@ -0,0 +1,79 @@ + +Contributing +============ + +The qm-dsp library is maintained in a Github repository at +https://github.com/c4dm/qm-dsp. + + +Reporting bugs +-------------- + +Please use Github issues for bug reports. Try to make them as specific +as possible. For example, describe an input that triggers some +particular behaviour, and tell us how that behaviour differs from what +you expected. + +If your bug can be reproduced by processing an audio file using one of +the QM Vamp Plugins (https://github.com/c4dm/qm-vamp-plugins), which +are built using this library, that might be a good way to illustrate +the problem. + + +Pull requests +------------- + +We're happy to see pull requests, and can pull them directly in some +circumstances. + + * Please make sure your change compiles without warnings and passes + the existing tests. + + * Please follow the code style guidelines (see below). + + * Please make it as easy as possible to verify the behaviour of the + pull request, for example by adding a new test in the `tests` + directory. This library has only limited test coverage, but we + would like to expand it, and prefer not to make changes unless they + are backed by tests. + + * Please provide your changes under terms which permit Queen Mary + University of London to relicense the code for commercial + purposes. The qm-dsp library as a whole is provided under the GPL, + but QM also make commercial licences available separately, and + cannot accept any pull request whose copyright status would prevent + that. In practice, this means any non-trivial change not + originating from QM must be explicitly licensed using a BSD-like + licence text, either in the source file itself or in an + accompanying file. See `thread/BlockAllocator.h` for an example of + typical language. + +Please note also that fixes which change the behaviour of the existing +QM Vamp Plugins will need particularly close scrutiny - these are +reasonably widely used and, even where they have defects, changes may +cause problems for users and will at least need to be documented with +the plugins. For this reason it may take some time for such changes to +be reviewed or integrated. + + +Code style +---------- + + * C++ code must compile with the C++98 standard, except for the unit + tests which are C++14 + + * Classes are named `LikeThis` - functions, methods, and local + variables `likeThis` - class member variables `m_likeThis` + + * Indentation is four spaces at a time (no tabs) + + * The opening brace for a block goes at the end of the line, except + at the start of a function or class definition where it gets a line + of its own + + * Please use braces around any conditional or loop block that + occupies its own line + +Some of the older code in this library does not follow these +guidelines - usually this means the code needs to be updated. + diff --git a/lib/qm-dsp/README.md b/lib/qm-dsp/README.md new file mode 100644 index 0000000000..20bcb60f04 --- /dev/null +++ b/lib/qm-dsp/README.md @@ -0,0 +1,59 @@ + +QM-DSP library +============== + +This is a C++ library of functions for Digital Signal Processing and +Music Informatics purposes developed in the [Centre for Digital +Music](http://c4dm.eecs.qmul.ac.uk) at Queen Mary, University of +London. + +It is used by [QM Vamp Plugins](http://isophonics.net/QMVampPlugins) +amongst other things. + +Despite the assertive name "qm-dsp", it is not "the official QM DSP +library", just one library for DSP that happens to have been written +at QM. It got this name because nothing else was using it at the time. + + +Compiling the library +--------------------- + + - Linux: `make -f build/linux/Makefile.linux64` + + - Mac: `make -f build/osx/Makefile.osx` + + - Windows (MSVC): Use the project file `build/msvc/QMDSP.vcxproj` + +To build and run unit tests as well, add the `test` target to your +Make invocation, e.g. `make -f build/linux/Makefile.linux64 +test`. Tests require the Boost library. + + +Licence +------- + +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. + +This code is Copyright (c) 2006-2019 Queen Mary, University of London, +with the following exceptions: + + - `ext/kissfft` - Copyright (c) 2003-2010 Mark Borgerding + + - `maths/pca/pca.c` - Fionn Murtagh, from StatLib, used with permission + + - `maths/Polyfit.h` - by 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 a BSD-style license + +See individual files for further authorship details. + +If you wish to use this code in a proprietary application or product +for which the terms of the GPL are not appropriate, please contact QM +Innovation https://www.qminnovation.co.uk/ for licensing terms. diff --git a/lib/qm-dsp/README.txt b/lib/qm-dsp/README.txt deleted file mode 100644 index 6927c7a1b8..0000000000 --- a/lib/qm-dsp/README.txt +++ /dev/null @@ -1,35 +0,0 @@ - - -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-2015 Queen Mary, University of London, -with the following exceptions: - -ext/kissfft -- Copyright (c) 2003-2010 Mark Borgerding - -maths/pca.c -- Fionn Murtagh, from StatLib; with permission - -maths/Polyfit.h -- Allen Miller, David J Taylor and others; also for -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. - - diff --git a/lib/qm-dsp/base/Restrict.h b/lib/qm-dsp/base/Restrict.h new file mode 100644 index 0000000000..723a8b3b67 --- /dev/null +++ b/lib/qm-dsp/base/Restrict.h @@ -0,0 +1,17 @@ +
+#ifndef QM_DSP_RESTRICT_H
+#define QM_DSP_RESTRICT_H
+
+#ifdef _MSC_VER
+#define QM_R__ __restrict
+#endif
+
+#ifdef __GNUC__
+#define QM_R__ __restrict__
+#endif
+
+#ifndef QM_R__
+#define QM_R__
+#endif
+
+#endif
diff --git a/lib/qm-dsp/dsp/chromagram/Chromagram.cpp b/lib/qm-dsp/dsp/chromagram/Chromagram.cpp index 5ba2a762de..9f40b1d2f1 100644 --- a/lib/qm-dsp/dsp/chromagram/Chromagram.cpp +++ b/lib/qm-dsp/dsp/chromagram/Chromagram.cpp @@ -33,8 +33,9 @@ int Chromagram::initialise( ChromaConfig Config ) m_BPO = Config.BPO; // bins per octave m_normalise = Config.normalise; // if frame normalisation is required - // No. of constant Q bins, extended to a full octave - m_uK = m_BPO * (unsigned int)ceil(log(m_FMax/m_FMin)/log(2.0)); + // Extend range to a full octave + double octaves = log(m_FMax / m_FMin) / log(2.0); + m_FMax = m_FMin * pow(2.0, ceil(octaves)); // Create array for chroma result m_chromadata = new double[ m_BPO ]; @@ -53,6 +54,9 @@ int Chromagram::initialise( ChromaConfig Config ) // Initialise ConstantQ operator m_ConstantQ = new ConstantQ( ConstantQConfig ); + // No. of constant Q bins + m_uK = m_ConstantQ->getK(); + // Initialise working arrays m_frameSize = m_ConstantQ->getfftlength(); m_hopSize = m_ConstantQ->gethop(); @@ -112,7 +116,7 @@ void Chromagram::unityNormalise(double *src) MathUtilities::getFrameMinMax( src, m_BPO, & min, &max ); - for( unsigned int i = 0; i < m_BPO; i++ ) + for (int i = 0; i < m_BPO; i++) { val = src[ i ] / max; @@ -121,7 +125,7 @@ void Chromagram::unityNormalise(double *src) } -double* Chromagram::process( const double *data ) +double *Chromagram::process(const double *data) { if (!m_skGenerated) { // Generate CQ Kernel @@ -134,17 +138,25 @@ double* Chromagram::process( const double *data ) m_windowbuf = new double[m_frameSize]; } - for (unsigned int i = 0; i < m_frameSize; ++i) { + for (int i = 0; i < m_frameSize; ++i) { m_windowbuf[i] = data[i]; } m_window->cut(m_windowbuf); + // The frequency-domain version expects pre-fftshifted input - so + // we must do the same here + for (int i = 0; i < m_frameSize/2; ++i) { + double tmp = m_windowbuf[i]; + m_windowbuf[i] = m_windowbuf[i + m_frameSize/2]; + m_windowbuf[i + m_frameSize/2] = tmp; + } + m_FFT->forward(m_windowbuf, m_FFTRe, m_FFTIm); return process(m_FFTRe, m_FFTIm); } -double* Chromagram::process( const double *real, const double *imag ) +double *Chromagram::process(const double *real, const double *imag) { if (!m_skGenerated) { // Generate CQ Kernel @@ -153,20 +165,20 @@ double* Chromagram::process( const double *real, const double *imag ) } // initialise chromadata to 0 - for (unsigned i = 0; i < m_BPO; i++) m_chromadata[i] = 0; + for (int i = 0; i < m_BPO; i++) m_chromadata[i] = 0; // Calculate ConstantQ frame m_ConstantQ->process( real, imag, m_CQRe, m_CQIm ); // add each octave of cq data into Chromagram - const unsigned octaves = m_uK / m_BPO; - for (unsigned octave = 0; octave < octaves; octave++) + const int octaves = m_uK / m_BPO; + for (int 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 ]); - } + int firstBin = octave*m_BPO; + for (int 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); diff --git a/lib/qm-dsp/dsp/chromagram/Chromagram.h b/lib/qm-dsp/dsp/chromagram/Chromagram.h index b2ad72e0ac..ca68ee9071 100644 --- a/lib/qm-dsp/dsp/chromagram/Chromagram.h +++ b/lib/qm-dsp/dsp/chromagram/Chromagram.h @@ -20,11 +20,11 @@ #include "base/Window.h" #include "ConstantQ.h" -struct ChromaConfig{ +struct ChromaConfig { double FS; double min; double max; - unsigned int BPO; + int BPO; double CQThresh; MathUtilities::NormaliseType normalise; }; @@ -35,19 +35,44 @@ 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 ); + + /** + * Process a time-domain input signal of length equal to + * getFrameSize(). + * + * The returned buffer contains the chromagram values indexed by + * bin, with the number of values corresponding to the BPO field + * in the ChromaConfig supplied at construction. It is owned by + * the Chromagram object and is reused from one process call to + * the next. + */ + double *process(const double *data); + + /** + * Process a frequency-domain input signal generated from a + * time-domain signal of length equal to getFrameSize() that has + * been windowed and "fftshifted" to place the zero index in the + * centre of the frame. The real and imag buffers must each + * contain the full getFrameSize() frequency bins. + * + * The returned buffer contains the chromagram values indexed by + * bin, with the number of values corresponding to the BPO field + * in the ChromaConfig supplied at construction. It is owned by + * the Chromagram object and is reused from one process call to + * the next. + */ + double *process(const double *real, const double *imag); + + 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; } - + int getK() { return m_uK;} + int getFrameSize() { return m_frameSize; } + int getHopSize() { return m_hopSize; } + private: int initialise( ChromaConfig Config ); int deInitialise(); @@ -58,13 +83,13 @@ private: double* m_chromadata; double m_FMin; double m_FMax; - unsigned int m_BPO; - unsigned int m_uK; + int m_BPO; + int m_uK; MathUtilities::NormaliseType m_normalise; - unsigned int m_frameSize; - unsigned int m_hopSize; + int m_frameSize; + int m_hopSize; FFTReal* m_FFT; ConstantQ* m_ConstantQ; diff --git a/lib/qm-dsp/dsp/chromagram/ConstantQ.cpp b/lib/qm-dsp/dsp/chromagram/ConstantQ.cpp index f2129f2e6f..4585ddc236 100644 --- a/lib/qm-dsp/dsp/chromagram/ConstantQ.cpp +++ b/lib/qm-dsp/dsp/chromagram/ConstantQ.cpp @@ -125,18 +125,17 @@ void ConstantQ::sparsekernel() hammingWindowIm[u] = 0; } - const double samplesPerCycle = - m_FS / (m_FMin * pow(2, (double)k / (double)m_BPO)); + // Computing a hamming window + const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO))); - // Computing a hamming window - const unsigned hammingLength = (int) ceil( - m_dQ * samplesPerCycle); +// cerr << "k = " << k << ", q = " << m_dQ << ", m_FMin = " << m_FMin << ", hammingLength = " << hammingLength << " (rounded up from " << (m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO))) << ")" << endl; + unsigned origin = m_FFTLength/2 - hammingLength/2; for (unsigned i=0; i<hammingLength; i++) { - const double angle = 2*PI*i/samplesPerCycle; + 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; @@ -144,10 +143,6 @@ void ConstantQ::sparsekernel() hammingWindowIm[ origin + i ] = absol*imag; } - /* This code splits the hanning window and moves it to the beginning - and the end, creating an empty gap in the middle. - It is disabled, because it results in wrong results, - when tested with sin waves centered on a bin frequency. for (unsigned i = 0; i < m_FFTLength/2; ++i) { double temp = hammingWindowRe[i]; hammingWindowRe[i] = hammingWindowRe[i + m_FFTLength/2]; @@ -156,7 +151,6 @@ void ConstantQ::sparsekernel() hammingWindowIm[i] = hammingWindowIm[i + m_FFTLength/2]; hammingWindowIm[i + m_FFTLength/2] = temp; } - */ //do fft of hammingWindow m_FFT.process( 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm ); @@ -168,7 +162,7 @@ void ConstantQ::sparsekernel() const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]); if (squaredBin <= squareThreshold) continue; - // Insert non-zero position indexes, doubled because they are floats + // Insert non-zero position indexes sk->is.push_back(j); sk->js.push_back(k); @@ -280,6 +274,7 @@ double* ConstantQ::process( const double* fftdata ) { const unsigned row = cqbin[i]; const unsigned col = fftbin[i]; + if (col == 0) continue; const double & r1 = real[i]; const double & i1 = imag[i]; const double & r2 = fftdata[ (2*m_FFTLength) - 2*col - 2 ]; @@ -301,17 +296,15 @@ void ConstantQ::initialise( CQConfig Config ) m_BPO = Config.BPO; // bins per octave m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation - // Work out Q value for Filter bank - m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); - // No. of constant Q bins, extended to a full octave - m_uK = m_BPO * (unsigned int)ceil(log(m_FMax/m_FMin)/log(2.0)); + 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 + m_hop = m_FFTLength/8; // std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl; @@ -351,10 +344,11 @@ void ConstantQ::process(const double *FFTRe, const double* FFTIm, { const unsigned row = cqbin[i]; const unsigned col = fftbin[i]; + if (col == 0) continue; 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 ]; + const double & r2 = FFTRe[ m_FFTLength - col ]; + const double & i2 = FFTIm[ m_FFTLength - col ]; // add the multiplication CQRe[ row ] += (r1*r2 - i1*i2); CQIm[ row ] += (r1*i2 + i1*r2); diff --git a/lib/qm-dsp/dsp/chromagram/ConstantQ.h b/lib/qm-dsp/dsp/chromagram/ConstantQ.h index bd666ddd6f..7507a1f964 100644 --- a/lib/qm-dsp/dsp/chromagram/ConstantQ.h +++ b/lib/qm-dsp/dsp/chromagram/ConstantQ.h @@ -20,8 +20,8 @@ #include "maths/MathAliases.h" #include "maths/MathUtilities.h" -struct CQConfig{ - double FS; // samplerate +struct CQConfig { + double FS; // samplerate double min; // minimum frequency double max; // maximum frequency unsigned int BPO; // bins per octave diff --git a/lib/qm-dsp/dsp/keydetection/GetKeyMode.cpp b/lib/qm-dsp/dsp/keydetection/GetKeyMode.cpp index 7407c43feb..cb8bc2477a 100644 --- a/lib/qm-dsp/dsp/keydetection/GetKeyMode.cpp +++ b/lib/qm-dsp/dsp/keydetection/GetKeyMode.cpp @@ -1,7 +1,12 @@ /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
- Copyright (c) 2005 Centre for Digital Music ( C4DM )
- Queen Mary Univesrity of London
+ QM DSP Library
+
+ Centre for Digital Music, Queen Mary, University of London.
+ This file 2005-2006 Christian Landone and Katy Noland.
+
+ Fixes to correct chroma offsets and for thread safety contributed
+ by Daniel Schürmann.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -9,9 +14,6 @@ License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
-// GetKeyMode.cpp: implementation of the CGetKeyMode class.
-//
-//////////////////////////////////////////////////////////////////////
#include "GetKeyMode.h"
#include "maths/MathUtilities.h"
@@ -22,18 +24,20 @@ #include <cstring>
#include <cstdlib>
+static const int kBinsPerOctave = 36;
+
// 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};
+static double MajProfile[kBinsPerOctave] = {
+ 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[kBinsPerOctave] = {
+ 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};
//
@@ -42,7 +46,7 @@ static double MinProfile[36] = //////////////////////////////////////////////////////////////////////
GetKeyMode::GetKeyMode( int sampleRate, float tuningFrequency,
- double hpcpAverage, double medianAverage ) :
+ double hpcpAverage, double medianAverage ) :
m_hpcpAverage( hpcpAverage ),
m_medianAverage( medianAverage ),
m_ChrPointer(0),
@@ -51,7 +55,6 @@ GetKeyMode::GetKeyMode( int sampleRate, float tuningFrequency, m_MeanHPCP(0),
m_MajCorr(0),
m_MinCorr(0),
- m_Keys(0),
m_MedianFilterBuffer(0),
m_SortedBuffer(0),
m_keyStrengths(0)
@@ -61,16 +64,16 @@ GetKeyMode::GetKeyMode( int sampleRate, float tuningFrequency, // Chromagram configuration parameters
m_ChromaConfig.normalise = MathUtilities::NormaliseUnitMax;
m_ChromaConfig.FS = sampleRate/(double)m_DecimationFactor;
+ if (m_ChromaConfig.FS < 1) {
+ m_ChromaConfig.FS = 1;
+ }
// Set C3 (= MIDI #48) as our base:
// This implies that key = 1 => Cmaj, key = 12 => Bmaj, key = 13 => Cmin, etc.
- m_ChromaConfig.min = Pitch::getFrequencyForPitch
- (48, 0, tuningFrequency);
- // C7 (= MIDI #96) is the exclusive maximum key:
- m_ChromaConfig.max = Pitch::getFrequencyForPitch
- (96, 0, tuningFrequency);
+ m_ChromaConfig.min = Pitch::getFrequencyForPitch( 48, 0, tuni |