Add an Interface for Pitchtrackers and set VolTresholds
This commit is contained in:
1 parent
5f79406cf2
commit
d04cbe7ceb
16 files changed
+344
-173
No files matched your search
@@ -39,17 +39,17 @@ void Analyzer_Process(Analyzer* analyzer){
|
|||||||
analyzer->process();
|
analyzer->process();
|
||||||
}
|
}
|
||||||
|
|
||||||
double Analyzer_GetPeak(Analyzer* analyzer){
|
float Analyzer_GetPeak(Analyzer* analyzer){
|
||||||
if(!analyzer)
|
if(!analyzer)
|
||||||
return -999;
|
return -999;
|
||||||
return analyzer->getPeak();
|
return static_cast<float>(analyzer->getPeakRaw());
|
||||||
}
|
}
|
||||||
|
|
||||||
double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq){
|
double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq){
|
||||||
if(!analyzer)
|
if(!analyzer)
|
||||||
return -1;
|
return -1;
|
||||||
const Tone* tone = analyzer->findTone(minFreq, maxFreq);
|
const Tone* tone = analyzer->findTone(minFreq, maxFreq);
|
||||||
return (tone == NULL) ? -1 : FreqToNote(tone->freq);
|
return (tone == NULL) ? -1 : FreqToNote(tone->freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate){
|
bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate){
|
||||||
@@ -71,6 +71,18 @@ int PtAKF_GetNumHalfTones(){
|
|||||||
return PtAKF::GetNumHalfTones();
|
return PtAKF::GetNumHalfTones();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PtAKF_SetVolumeTreshold(PtAKF* analyzer, float threshold){
|
||||||
|
if(!analyzer)
|
||||||
|
return;
|
||||||
|
analyzer->SetVolumeThreshold(threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
float PtAKF_GetVolumeThreshold(PtAKF* analyzer){
|
||||||
|
if(!analyzer)
|
||||||
|
return 0.f;
|
||||||
|
return analyzer->GetVolumeThreshold();
|
||||||
|
}
|
||||||
|
|
||||||
void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt){
|
void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt){
|
||||||
if(sampleCt <= 0 || !analyzer)
|
if(sampleCt <= 0 || !analyzer)
|
||||||
return;
|
return;
|
||||||
@@ -82,7 +94,7 @@ void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt){
|
|||||||
//analyzer->input(dataShort, dataShort + sampleCt);
|
//analyzer->input(dataShort, dataShort + sampleCt);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PtAKF_GetNote(PtAKF* analyzer, double* maxVolume, float* weights){
|
int PtAKF_GetNote(PtAKF* analyzer, float* maxVolume, float* weights){
|
||||||
if(!analyzer)
|
if(!analyzer)
|
||||||
return -1;
|
return -1;
|
||||||
return analyzer->GetNote(maxVolume, weights);
|
return analyzer->GetNote(maxVolume, weights);
|
||||||
@@ -97,6 +109,18 @@ void PtDyWa_Free(PtDyWa* analyzer){
|
|||||||
delete analyzer;
|
delete analyzer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PtDyWa_SetVolumeTreshold(PtDyWa* analyzer, float threshold){
|
||||||
|
if(!analyzer)
|
||||||
|
return;
|
||||||
|
analyzer->SetVolumeThreshold(threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
float PtDyWa_GetVolumeThreshold(PtDyWa* analyzer){
|
||||||
|
if(!analyzer)
|
||||||
|
return 0.f;
|
||||||
|
return analyzer->GetVolumeThreshold();
|
||||||
|
}
|
||||||
|
|
||||||
void PtDyWa_InputByte(PtDyWa* analyzer, char* data, int sampleCt){
|
void PtDyWa_InputByte(PtDyWa* analyzer, char* data, int sampleCt){
|
||||||
if(sampleCt <= 0 || !analyzer)
|
if(sampleCt <= 0 || !analyzer)
|
||||||
return;
|
return;
|
||||||
@@ -105,16 +129,10 @@ void PtDyWa_InputByte(PtDyWa* analyzer, char* data, int sampleCt){
|
|||||||
freeFloatArray(dataFloat);
|
freeFloatArray(dataFloat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PtDyWa_Process(PtDyWa* analyzer){
|
double PtDyWa_FindNote(PtDyWa* analyzer, float* maxVolume){
|
||||||
if(!analyzer)
|
|
||||||
return;
|
|
||||||
analyzer->Process();
|
|
||||||
}
|
|
||||||
|
|
||||||
double PtDyWa_GetNote(PtDyWa* analyzer){
|
|
||||||
if(!analyzer)
|
if(!analyzer)
|
||||||
return -1;
|
return -1;
|
||||||
return analyzer->GetNote();
|
return analyzer->FindNote(maxVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -12,21 +12,24 @@ DllExport void Analyzer_InputFloat(Analyzer* analyzer, float* data, int sampleCt
|
|||||||
DllExport void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt);
|
DllExport void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt);
|
||||||
DllExport void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt);
|
DllExport void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt);
|
||||||
DllExport void Analyzer_Process(Analyzer* analyzer);
|
DllExport void Analyzer_Process(Analyzer* analyzer);
|
||||||
DllExport double Analyzer_GetPeak(Analyzer* analyzer);
|
DllExport float Analyzer_GetPeak(Analyzer* analyzer);
|
||||||
DllExport double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq);
|
DllExport double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq);
|
||||||
DllExport bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate);
|
DllExport bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate);
|
||||||
|
|
||||||
DllExport PtAKF* PtAKF_Create(unsigned step);
|
DllExport PtAKF* PtAKF_Create(unsigned step);
|
||||||
DllExport void PtAKF_Free(PtAKF* analyzer);
|
DllExport void PtAKF_Free(PtAKF* analyzer);
|
||||||
DllExport int PtAKF_GetNumHalfTones();
|
DllExport int PtAKF_GetNumHalfTones();
|
||||||
|
DllExport void PtAKF_SetVolumeThreshold(PtAKF* analyzer, float threshold);
|
||||||
|
DllExport float PtAKF_GetVolumeThreshold(PtAKF* analyzer);
|
||||||
DllExport void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt);
|
DllExport void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt);
|
||||||
DllExport int PtAKF_GetNote(PtAKF* analyzer, double* maxVolume, float* weights);
|
DllExport int PtAKF_GetNote(PtAKF* analyzer, float* maxVolume, float* weights);
|
||||||
|
|
||||||
DllExport PtDyWa* PtDyWa_Create(unsigned step);
|
DllExport PtDyWa* PtDyWa_Create(unsigned step);
|
||||||
DllExport void PtDyWa_Free(PtDyWa* analyzer);
|
DllExport void PtDyWa_Free(PtDyWa* analyzer);
|
||||||
|
DllExport void PtDyWa_SetVolumeTreshold(PtDyWa* analyzer,float threshold);
|
||||||
|
DllExport float PtDyWa_GetVolumeThreshold(PtDyWa* analyzer);
|
||||||
DllExport void PtDyWa_InputByte(PtDyWa* analyzer, char* data, int sampleCt);
|
DllExport void PtDyWa_InputByte(PtDyWa* analyzer, char* data, int sampleCt);
|
||||||
DllExport void PtDyWa_Process(PtDyWa* analyzer);
|
DllExport double PtDyWa_FindNote(PtDyWa* analyzer);
|
||||||
DllExport double PtDyWa_GetNote(PtDyWa* analyzer);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
public ref class CTone {
|
public ref class CTone {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ typedef struct _minmax {
|
|||||||
struct _minmax *next;
|
struct _minmax *next;
|
||||||
} minmax;
|
} minmax;
|
||||||
|
|
||||||
double _dywapitch_computeWaveletPitch(double * samples, int startsample, int samplecount) {
|
double _dywapitch_computeWaveletPitch(double * samples, int startsample, int samplecount, float* maxVolume, double volThreshold) {
|
||||||
double pitchF = 0.0;
|
double pitchF = 0.0;
|
||||||
|
|
||||||
int i, j;
|
int i, j;
|
||||||
@@ -143,6 +143,9 @@ double _dywapitch_computeWaveletPitch(double * samples, int startsample, int sam
|
|||||||
maxValue = maxValue - theDC;
|
maxValue = maxValue - theDC;
|
||||||
minValue = minValue - theDC;
|
minValue = minValue - theDC;
|
||||||
double amplitudeMax = (maxValue > -minValue ? maxValue : -minValue);
|
double amplitudeMax = (maxValue > -minValue ? maxValue : -minValue);
|
||||||
|
*maxVolume = static_cast<float>(amplitudeMax + theDC);
|
||||||
|
if(*maxVolume < volThreshold)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
ampltitudeThreshold = amplitudeMax*maximaThresholdRatio;
|
ampltitudeThreshold = amplitudeMax*maximaThresholdRatio;
|
||||||
//asLog("dywapitch theDC=%f ampltitudeThreshold=%f\n", theDC, ampltitudeThreshold);
|
//asLog("dywapitch theDC=%f ampltitudeThreshold=%f\n", theDC, ampltitudeThreshold);
|
||||||
@@ -439,8 +442,8 @@ void dywapitch_inittracking(dywapitchtracker *pitchtracker) {
|
|||||||
pitchtracker->_pitchConfidence = -1;
|
pitchtracker->_pitchConfidence = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
double dywapitch_computepitch(dywapitchtracker *pitchtracker, double * samples, int startsample, int samplecount) {
|
double dywapitch_computepitch(dywapitchtracker *pitchtracker, double * samples, int startsample, int samplecount, float* maxVolume, double volThreshold) {
|
||||||
double raw_pitch = _dywapitch_computeWaveletPitch(samples, startsample, samplecount);
|
double raw_pitch = _dywapitch_computeWaveletPitch(samples, startsample, samplecount, maxVolume, volThreshold);
|
||||||
return _dywapitch_dynamicprocess(pitchtracker, raw_pitch);
|
return _dywapitch_dynamicprocess(pitchtracker, raw_pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ void dywapitch_inittracking(dywapitchtracker *pitchtracker);
|
|||||||
// startsample : the index of the first sample to use in the sample buffer
|
// startsample : the index of the first sample to use in the sample buffer
|
||||||
// samplecount : the number of samples to use to compte the pitch
|
// samplecount : the number of samples to use to compte the pitch
|
||||||
// return 0.0 if no pitch was found (sound too low, noise, etc..)
|
// return 0.0 if no pitch was found (sound too low, noise, etc..)
|
||||||
double dywapitch_computepitch(dywapitchtracker *pitchtracker, double * samples, int startsample, int samplecount);
|
double dywapitch_computepitch(dywapitchtracker *pitchtracker, double * samples, int startsample, int samplecount, float* maxVolume, double volThreshold);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
@@ -5,23 +5,27 @@
|
|||||||
PtDyWa::PtDyWa(unsigned step){
|
PtDyWa::PtDyWa(unsigned step){
|
||||||
_Step = step;
|
_Step = step;
|
||||||
dywapitch_inittracking(&_State);
|
dywapitch_inittracking(&_State);
|
||||||
|
_VolTreshold = 0.01f;
|
||||||
|
_LastMaxVol = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PtDyWa::Process(){
|
void PtDyWa::SetVolumeThreshold(float threshold){
|
||||||
|
_VolTreshold = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
double PtDyWa::FindNote(float* maxVolume){
|
||||||
double AnaylsisBuf[_SampleCt];
|
double AnaylsisBuf[_SampleCt];
|
||||||
size_t size = _AnalysisBuf.size();
|
size_t size = _AnalysisBuf.size();
|
||||||
if(size > _SampleCt)
|
if(size > _SampleCt)
|
||||||
_AnalysisBuf.pop(size - _SampleCt);
|
_AnalysisBuf.pop(size - _SampleCt);
|
||||||
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt))
|
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt)){
|
||||||
return;
|
*maxVolume = _LastMaxVol * 0.85f;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
_AnalysisBuf.pop(_Step);
|
_AnalysisBuf.pop(_Step);
|
||||||
double pitch = dywapitch_computepitch(&_State, AnaylsisBuf,0,_SampleCt);
|
double pitch = dywapitch_computepitch(&_State, AnaylsisBuf, 0, _SampleCt, maxVolume, _VolTreshold);
|
||||||
if(pitch == 0.0)
|
if(pitch == 0.0)
|
||||||
_LastNote = -1;
|
return -1;
|
||||||
else
|
else
|
||||||
_LastNote = FreqToNote(pitch);
|
return FreqToNote(pitch);
|
||||||
}
|
|
||||||
|
|
||||||
double PtDyWa::GetNote(){
|
|
||||||
return _LastNote;
|
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,14 @@ public:
|
|||||||
_AnalysisBuf.insert(begin, end);
|
_AnalysisBuf.insert(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process();
|
double FindNote(float* maxVolume);
|
||||||
double GetNote();
|
void SetVolumeThreshold(float threshold);
|
||||||
|
float GetVolumeThreshold(){return _VolTreshold;}
|
||||||
private:
|
private:
|
||||||
dywapitchtracker _State;
|
dywapitchtracker _State;
|
||||||
constexpr static size_t _SampleCt = 2048;
|
constexpr static size_t _SampleCt = 2048;
|
||||||
RingBuffer<_SampleCt * 2> _AnalysisBuf;
|
RingBuffer<_SampleCt * 2> _AnalysisBuf;
|
||||||
unsigned _Step;
|
unsigned _Step;
|
||||||
double _LastNote;
|
float _VolTreshold;
|
||||||
|
float _LastMaxVol;
|
||||||
};
|
};
|
||||||
@@ -86,6 +86,7 @@ public:
|
|||||||
fft_t const& getFFT() const { return m_fft; }
|
fft_t const& getFFT() const { return m_fft; }
|
||||||
/** Get the peak level in dB (negative value, 0.0 = clipping). **/
|
/** Get the peak level in dB (negative value, 0.0 = clipping). **/
|
||||||
double getPeak() const { return 10.0 * log10(m_peak); }
|
double getPeak() const { return 10.0 * log10(m_peak); }
|
||||||
|
double getPeakRaw() const { return m_peak; }
|
||||||
/** Get a list of all tones detected. **/
|
/** Get a list of all tones detected. **/
|
||||||
tones_t const& getTones() const { return m_tones; }
|
tones_t const& getTones() const { return m_tones; }
|
||||||
/** Find a tone within the singing range; prefers strong tones around 200-400 Hz. **/
|
/** Find a tone within the singing range; prefers strong tones around 200-400 Hz. **/
|
||||||
|
|||||||
+15
-4
@@ -47,6 +47,8 @@ PtAKF::PtAKF(unsigned step){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_Step = step;
|
_Step = step;
|
||||||
|
_VolTreshold = 0.01f;
|
||||||
|
_LastMaxVol = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
PtAKF::~PtAKF(){
|
PtAKF::~PtAKF(){
|
||||||
@@ -59,13 +61,19 @@ PtAKF::~PtAKF(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int PtAKF::GetNote(double* restrict maxVolume, float* restrict weights){
|
void PtAKF::SetVolumeThreshold(float threshold){
|
||||||
|
_VolTreshold = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PtAKF::GetNote(float* restrict maxVolume, float* restrict weights){
|
||||||
float AnaylsisBuf[_SampleCt];
|
float AnaylsisBuf[_SampleCt];
|
||||||
size_t size = _AnalysisBuf.size();
|
size_t size = _AnalysisBuf.size();
|
||||||
if(size > _SampleCt)
|
if(size > _SampleCt)
|
||||||
_AnalysisBuf.pop(size - _SampleCt);
|
_AnalysisBuf.pop(size - _SampleCt);
|
||||||
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt))
|
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt)){
|
||||||
|
*maxVolume = _LastMaxVol * 0.85f;
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
_AnalysisBuf.pop(_Step);
|
_AnalysisBuf.pop(_Step);
|
||||||
return _GetNote(AnaylsisBuf, maxVolume, weights);
|
return _GetNote(AnaylsisBuf, maxVolume, weights);
|
||||||
}
|
}
|
||||||
@@ -96,7 +104,7 @@ void PtAKF::AddPeak(SPeak peaks[], float curWeight, int toneIndex){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int PtAKF::_GetNote(float* restrict samples, double* restrict maxVolume, float* restrict weights){
|
int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* restrict weights){
|
||||||
// Calculate maximum volume
|
// Calculate maximum volume
|
||||||
|
|
||||||
float maxVolumeL = 0;
|
float maxVolumeL = 0;
|
||||||
@@ -105,9 +113,12 @@ int PtAKF::_GetNote(float* restrict samples, double* restrict maxVolume, float*
|
|||||||
if(vol > maxVolumeL)
|
if(vol > maxVolumeL)
|
||||||
maxVolumeL = vol;
|
maxVolumeL = vol;
|
||||||
}
|
}
|
||||||
//maxVolumeL /= 32767.;
|
//maxVolumeL /= 32767.f;
|
||||||
*maxVolume = maxVolumeL;
|
*maxVolume = maxVolumeL;
|
||||||
|
|
||||||
|
if(maxVolumeL < _VolTreshold)
|
||||||
|
return -1;
|
||||||
|
|
||||||
float samplesWindowed[_SampleCt];
|
float samplesWindowed[_SampleCt];
|
||||||
|
|
||||||
for(int i = 0; i < _SampleCt; i++){
|
for(int i = 0; i < _SampleCt; i++){
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ public:
|
|||||||
_AnalysisBuf.insert(begin, end);
|
_AnalysisBuf.insert(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetNote(double* restrict maxVolume, float* restrict weights);
|
int GetNote(float* restrict maxVolume, float* restrict weights);
|
||||||
|
void SetVolumeThreshold(float threshold);
|
||||||
|
float GetVolumeThreshold(){return _VolTreshold;}
|
||||||
static int GetNumHalfTones(){ return _MaxHalfTone - _MinHalfTone + 1;}
|
static int GetNumHalfTones(){ return _MaxHalfTone - _MinHalfTone + 1;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -48,8 +50,11 @@ private:
|
|||||||
constexpr static size_t _SampleCt = 2048;
|
constexpr static size_t _SampleCt = 2048;
|
||||||
RingBuffer<_SampleCt * 2> _AnalysisBuf;
|
RingBuffer<_SampleCt * 2> _AnalysisBuf;
|
||||||
unsigned _Step;
|
unsigned _Step;
|
||||||
|
float _VolTreshold;
|
||||||
|
float _LastMaxVol;
|
||||||
|
|
||||||
|
int _GetNote(float* restrict samples, float* restrict maxVolume, float* restrict weights);
|
||||||
|
|
||||||
static int _GetNote(float* restrict samples, double* restrict maxVolume, float* restrict weights);
|
|
||||||
static float _AnalyzeBySampleCt(float* restrict samples, float* restrict samplesWindowed, float samplesPerPeriodD);
|
static float _AnalyzeBySampleCt(float* restrict samples, float* restrict samplesWindowed, float samplesPerPeriodD);
|
||||||
static inline float _AnalyzeByTone(float* restrict samples, float* restrict samplesWindowed, int toneIndex);
|
static inline float _AnalyzeByTone(float* restrict samples, float* restrict samplesWindowed, int toneIndex);
|
||||||
static float _AKFBySampleCt(float* restrict samples, float samplesPerPeriodD);
|
static float _AKFBySampleCt(float* restrict samples, float samplesPerPeriodD);
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ using System.IO;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Pitch;
|
using Pitch;
|
||||||
|
using Vocaluxe.Lib.Sound.Record.Pitch;
|
||||||
|
using Vocaluxe.Lib.Sound.Record.PitchTracker;
|
||||||
using VocaluxeLib;
|
using VocaluxeLib;
|
||||||
using VocaluxeLib.Utils;
|
using VocaluxeLib.Utils;
|
||||||
|
|
||||||
@@ -38,17 +40,18 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
private const double _BaseToneFreq = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz)
|
private const double _BaseToneFreq = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz)
|
||||||
private const double _HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
private const double _HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
||||||
#endif
|
#endif
|
||||||
private CPtAKF _Analyzer = new CPtAKF();
|
private CPitchTracker _PitchTracker = new CPtAKF();
|
||||||
|
|
||||||
private double _MaxVolume;
|
private float _MaxVolume;
|
||||||
|
|
||||||
public CBuffer()
|
public CBuffer()
|
||||||
{
|
{
|
||||||
MinVolume = 0.02f;
|
VolTreshold = 0.02f;
|
||||||
ToneWeigths = new float[GetNumHalfTones()];
|
ToneWeigths = new float[GetNumHalfTones()];
|
||||||
Reset();
|
Reset();
|
||||||
#if TEST_PITCH
|
#if TEST_PITCH
|
||||||
_TestPitchDetection();
|
_TestPitchDetection();
|
||||||
|
Reset();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +60,9 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
_Dispose();
|
_Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetNumHalfTones()
|
public int GetNumHalfTones()
|
||||||
{
|
{
|
||||||
return CPtAKF.GetNumHalfTones();
|
return _PitchTracker.GetNumHalfTones();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ToneAbs { get; private set; }
|
public int ToneAbs { get; private set; }
|
||||||
@@ -68,7 +71,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
|
|
||||||
public float MaxVolume
|
public float MaxVolume
|
||||||
{
|
{
|
||||||
get { return (float)_MaxVolume; }
|
get { return _MaxVolume; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ToneValid { get; private set; }
|
public bool ToneValid { get; private set; }
|
||||||
@@ -78,7 +81,11 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum volume for a tone to be valid
|
/// Minimum volume for a tone to be valid
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float MinVolume { get; set; }
|
public float VolTreshold
|
||||||
|
{
|
||||||
|
get { return _PitchTracker.VolumeTreshold; }
|
||||||
|
set { _PitchTracker.VolumeTreshold = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
@@ -97,14 +104,14 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
// voice passthrough (send data to playback-device)
|
// voice passthrough (send data to playback-device)
|
||||||
//if (assigned(fVoiceStream)) then
|
//if (assigned(fVoiceStream)) then
|
||||||
//fVoiceStream.WriteData(Buffer, BufferSize);
|
//fVoiceStream.WriteData(Buffer, BufferSize);
|
||||||
_Analyzer.Input(buffer);
|
_PitchTracker.Input(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AnalyzeBuffer()
|
public void AnalyzeBuffer()
|
||||||
{
|
{
|
||||||
//_MaxVolume = _Analyzer.GetPeak() / 43 + 1;
|
//_MaxVolume = _Analyzer.GetPeak() / 43 + 1;
|
||||||
int tone = _Analyzer.GetNote(out _MaxVolume, ToneWeigths); //(int)Math.Round(_Analyzer.FindNote()); //
|
int tone = _PitchTracker.GetNote(out _MaxVolume, ToneWeigths); //(int)Math.Round(_Analyzer.FindNote()); //
|
||||||
if (tone >= 0 && _MaxVolume >= MinVolume)
|
if (tone >= 0)
|
||||||
{
|
{
|
||||||
ToneAbs = tone;
|
ToneAbs = tone;
|
||||||
Tone = ToneAbs % 12;
|
Tone = ToneAbs % 12;
|
||||||
@@ -122,10 +129,10 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
|
|
||||||
private void _Dispose()
|
private void _Dispose()
|
||||||
{
|
{
|
||||||
if (_Analyzer != null)
|
if (_PitchTracker != null)
|
||||||
{
|
{
|
||||||
_Analyzer.Dispose();
|
_PitchTracker.Dispose();
|
||||||
_Analyzer = null;
|
_PitchTracker = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,11 +295,13 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
int curTimeIndex = -1;
|
int curTimeIndex = -1;
|
||||||
int curNote = -1;
|
int curNote = -1;
|
||||||
const int maxSamplesPerBatch = 512;
|
const int maxSamplesPerBatch = 512;
|
||||||
CAnalyzer analyzer2 = new CAnalyzer();
|
CPitchTracker analyzer2 = new CAnalyzer();
|
||||||
CPtDyWa analyzer3 = new CPtDyWa();
|
CPitchTracker analyzer3 = new CPtDyWa();
|
||||||
PitchTracker analyzer4 = new PitchTracker {SampleRate = 44100, DetectLevelThreshold = 0.001f};
|
CPitchTracker analyzer4 = new CPtSharp();
|
||||||
int[] detectedTones = new int[4];
|
int[] detectedTones = new int[4];
|
||||||
int[] oks = new int[4];
|
int[] oks = new int[4];
|
||||||
|
float dummyMaxVol;
|
||||||
|
float[] dummyWeights = new float[ToneWeigths.Length];
|
||||||
int lCt = 0;
|
int lCt = 0;
|
||||||
while (wavFile.NumSamplesLeft > maxSamplesPerBatch)
|
while (wavFile.NumSamplesLeft > maxSamplesPerBatch)
|
||||||
{
|
{
|
||||||
@@ -303,14 +312,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
AnalyzeBuffer();
|
AnalyzeBuffer();
|
||||||
analyzer2.Input(samples);
|
analyzer2.Input(samples);
|
||||||
analyzer3.Input(samples);
|
analyzer3.Input(samples);
|
||||||
analyzer2.Process();
|
analyzer4.Input(samples);
|
||||||
analyzer3.Process();
|
|
||||||
short[] samplesShort = new short[samples.Length / 2];
|
|
||||||
Buffer.BlockCopy(samples, 0, samplesShort, 0, samples.Length);
|
|
||||||
float[] samplesFloat = new float[samplesShort.Length];
|
|
||||||
for (int i = 0; i < samplesShort.Length; i++)
|
|
||||||
samplesFloat[i] = (float)samplesShort[i] / short.MaxValue;
|
|
||||||
analyzer4.ProcessBuffer(samplesFloat);
|
|
||||||
while (curTimeIndex + 1 < tones.Count && time >= tones[curTimeIndex + 1].Time)
|
while (curTimeIndex + 1 < tones.Count && time >= tones[curTimeIndex + 1].Time)
|
||||||
{
|
{
|
||||||
curTimeIndex++;
|
curTimeIndex++;
|
||||||
@@ -319,9 +321,9 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
if (curNote < 0)
|
if (curNote < 0)
|
||||||
continue;
|
continue;
|
||||||
detectedTones[0] = ToneValid ? ToneAbs : -1;
|
detectedTones[0] = ToneValid ? ToneAbs : -1;
|
||||||
detectedTones[1] = (int)Math.Round(analyzer2.FindNote(64, 1770));
|
detectedTones[1] = analyzer2.GetNote(out dummyMaxVol, dummyWeights);
|
||||||
detectedTones[2] = (int)Math.Round(analyzer3.GetNote());
|
detectedTones[2] = analyzer3.GetNote(out dummyMaxVol, dummyWeights);
|
||||||
detectedTones[3] = analyzer4.CurrentPitchRecord.MidiNote - 15 - 21;
|
detectedTones[3] = analyzer4.GetNote(out dummyMaxVol, dummyWeights);
|
||||||
lCt++;
|
lCt++;
|
||||||
/*if (!ToneValid)
|
/*if (!ToneValid)
|
||||||
CBase.Log.LogDebug("Note " + _ToneToNote(curNote) + " at " + time + "ms not detected");
|
CBase.Log.LogDebug("Note " + _ToneToNote(curNote) + " at " + time + "ms not detected");
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
|
|
||||||
public int NumHalfTones()
|
public int NumHalfTones()
|
||||||
{
|
{
|
||||||
return CBuffer.GetNumHalfTones();
|
return _Buffer[0].GetNumHalfTones();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float[] ToneWeigth(int player)
|
public float[] ToneWeigth(int player)
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ namespace Pitch
|
|||||||
private int m_blockLen34; // 3/4 block len
|
private int m_blockLen34; // 3/4 block len
|
||||||
private int m_blockLen44; // 4/4 block len
|
private int m_blockLen44; // 4/4 block len
|
||||||
private double m_sampleRate;
|
private double m_sampleRate;
|
||||||
private float m_detectLevelThreshold;
|
|
||||||
|
|
||||||
private int m_numCourseSteps;
|
private int m_numCourseSteps;
|
||||||
private float[] m_pCourseFreqOffset;
|
private float[] m_pCourseFreqOffset;
|
||||||
@@ -45,12 +44,11 @@ namespace Pitch
|
|||||||
/// <param name="minFreq"></param>
|
/// <param name="minFreq"></param>
|
||||||
/// <param name="maxFreq"></param>
|
/// <param name="maxFreq"></param>
|
||||||
/// <param name="fFreqStep"></param>
|
/// <param name="fFreqStep"></param>
|
||||||
public PitchDsp(double sampleRate, float minPitch, float maxPitch, float detectLevelThreshold)
|
public PitchDsp(double sampleRate, float minPitch, float maxPitch)
|
||||||
{
|
{
|
||||||
m_sampleRate = sampleRate;
|
m_sampleRate = sampleRate;
|
||||||
m_minPitch = minPitch;
|
m_minPitch = minPitch;
|
||||||
m_maxPitch = maxPitch;
|
m_maxPitch = maxPitch;
|
||||||
m_detectLevelThreshold = detectLevelThreshold;
|
|
||||||
|
|
||||||
m_minNote = (int)(PitchToMidiNote(m_minPitch) + 0.5f) + 2;
|
m_minNote = (int)(PitchToMidiNote(m_minPitch) + 0.5f) + 2;
|
||||||
m_maxNote = (int)(PitchToMidiNote(m_maxPitch) + 0.5f) - 2;
|
m_maxNote = (int)(PitchToMidiNote(m_maxPitch) + 0.5f) - 2;
|
||||||
@@ -117,19 +115,9 @@ namespace Pitch
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Detect the pitch
|
/// Detect the pitch
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float DetectPitch(float[] samplesLo, float[] samplesHi, int numSamples)
|
public float DetectPitch(float[] samplesLo, float[] samplesHi)
|
||||||
{
|
{
|
||||||
float pitch = 0.0f;
|
return DetectPitchLo(samplesLo, samplesHi);
|
||||||
|
|
||||||
if (!LevelIsAbove(samplesLo, numSamples, m_detectLevelThreshold) &&
|
|
||||||
!LevelIsAbove(samplesHi, numSamples, m_detectLevelThreshold))
|
|
||||||
{
|
|
||||||
// Level is too low
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
pitch = DetectPitchLo(samplesLo, samplesHi);
|
|
||||||
return pitch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Threading;
|
using Pitch;
|
||||||
|
|
||||||
namespace Pitch
|
namespace Vocaluxe.Lib.Sound.Record.Pitch
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tracks pitch
|
/// Tracks pitch
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
public class PitchTracker
|
public class PitchTracker
|
||||||
{
|
{
|
||||||
private const int kOctaveSteps = 96;
|
private const int kOctaveSteps = 96;
|
||||||
@@ -50,6 +50,8 @@ namespace Pitch
|
|||||||
private IIRFilter m_iirFilterHiLo;
|
private IIRFilter m_iirFilterHiLo;
|
||||||
private IIRFilter m_iirFilterHiHi;
|
private IIRFilter m_iirFilterHiHi;
|
||||||
|
|
||||||
|
public float LastMaxVol = 0;
|
||||||
|
|
||||||
public delegate void PitchDetectedHandler(PitchTracker sender, PitchRecord pitchRecord);
|
public delegate void PitchDetectedHandler(PitchTracker sender, PitchRecord pitchRecord);
|
||||||
|
|
||||||
public event PitchDetectedHandler PitchDetected;
|
public event PitchDetectedHandler PitchDetected;
|
||||||
@@ -87,8 +89,9 @@ namespace Pitch
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_detectLevelThreshold = newValue;
|
m_detectLevelThreshold = newValue;
|
||||||
Setup();
|
//Setup();
|
||||||
}
|
}
|
||||||
|
get { return m_detectLevelThreshold; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -229,19 +232,20 @@ namespace Pitch
|
|||||||
m_circularBufferHi.Available = m_detectOverlapSamples;
|
m_circularBufferHi.Available = m_detectOverlapSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Process the passed in buffer of data. During this call, the PitchDetected event will
|
/// Process the passed in buffer of data. During this call, the PitchDetected event will
|
||||||
/// be fired zero or more times, depending how many pitch records will fit in the new
|
/// be fired zero or more times, depending how many pitch records will fit in the new
|
||||||
/// and previously cached buffer.
|
/// and previously cached buffer.
|
||||||
///
|
///
|
||||||
/// This means that there is no size restriction on the buffer that is passed into ProcessBuffer.
|
/// This means that there is no size restriction on the buffer that is passed into ProcessBuffer.
|
||||||
/// For instance, ProcessBuffer can be called with one very large buffer that contains all of the
|
/// For instance, ProcessBuffer can be called with one very large buffer that contains all of the
|
||||||
/// audio to be processed (many PitchDetected events will be fired), or just a small buffer at
|
/// audio to be processed (many PitchDetected events will be fired), or just a small buffer at
|
||||||
/// a time which is more typical for realtime applications. In the latter case, the PitchDetected
|
/// a time which is more typical for realtime applications. In the latter case, the PitchDetected
|
||||||
/// event might not be fired at all since additional calls must first be made to accumulate enough
|
/// event might not be fired at all since additional calls must first be made to accumulate enough
|
||||||
/// data do another pitch detect operation.
|
/// data do another pitch detect operation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inBuffer">Input buffer. Samples must be in the range -1.0 to 1.0</param>
|
/// <param name="inBuffer">Input buffer. Samples must be in the range -1.0 to 1.0</param>
|
||||||
|
/// <param name="maxVolume"></param>
|
||||||
/// <param name="sampleCount">Number of samples to process. Zero means all samples in the buffer</param>
|
/// <param name="sampleCount">Number of samples to process. Zero means all samples in the buffer</param>
|
||||||
public void ProcessBuffer(float[] inBuffer, int sampleCount = 0)
|
public void ProcessBuffer(float[] inBuffer, int sampleCount = 0)
|
||||||
{
|
{
|
||||||
@@ -251,6 +255,8 @@ namespace Pitch
|
|||||||
int samplesProcessed = 0;
|
int samplesProcessed = 0;
|
||||||
int srcLength = sampleCount == 0 ? inBuffer.Length : Math.Min(sampleCount, inBuffer.Length);
|
int srcLength = sampleCount == 0 ? inBuffer.Length : Math.Min(sampleCount, inBuffer.Length);
|
||||||
|
|
||||||
|
float maxVolume = LastMaxVol * 0.85f;
|
||||||
|
|
||||||
while (samplesProcessed < srcLength)
|
while (samplesProcessed < srcLength)
|
||||||
{
|
{
|
||||||
int frameCount = Math.Min(srcLength - samplesProcessed, m_pitchBufSize + m_detectOverlapSamples);
|
int frameCount = Math.Min(srcLength - samplesProcessed, m_pitchBufSize + m_detectOverlapSamples);
|
||||||
@@ -268,12 +274,21 @@ namespace Pitch
|
|||||||
while (m_circularBufferLo.ReadBuffer(m_pitchBufLo, m_curPitchSamplePos, m_pitchBufSize + m_detectOverlapSamples))
|
while (m_circularBufferLo.ReadBuffer(m_pitchBufLo, m_curPitchSamplePos, m_pitchBufSize + m_detectOverlapSamples))
|
||||||
{
|
{
|
||||||
float pitch1;
|
float pitch1;
|
||||||
float pitch2 = 0.0f;
|
float pitch2;
|
||||||
float detectedPitch = 0.0f;
|
float detectedPitch = 0.0f;
|
||||||
|
|
||||||
m_circularBufferHi.ReadBuffer(m_pitchBufHi, m_curPitchSamplePos, m_pitchBufSize + m_detectOverlapSamples);
|
m_circularBufferHi.ReadBuffer(m_pitchBufHi, m_curPitchSamplePos, m_pitchBufSize + m_detectOverlapSamples);
|
||||||
|
|
||||||
pitch1 = m_dsp.DetectPitch(m_pitchBufLo, m_pitchBufHi, m_pitchBufSize);
|
maxVolume = 0f;
|
||||||
|
for (int i = 0; i < (m_pitchBufSize + m_detectOverlapSamples) / 2; i++)
|
||||||
|
{
|
||||||
|
if (maxVolume < m_pitchBufHi[i])
|
||||||
|
maxVolume = m_pitchBufHi[i];
|
||||||
|
if (maxVolume < m_pitchBufLo[i])
|
||||||
|
maxVolume = m_pitchBufLo[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
pitch1 = (maxVolume >= m_detectLevelThreshold) ? m_dsp.DetectPitch(m_pitchBufLo, m_pitchBufHi) : 0f;
|
||||||
|
|
||||||
if (pitch1 > 0.0f)
|
if (pitch1 > 0.0f)
|
||||||
{
|
{
|
||||||
@@ -281,7 +296,7 @@ namespace Pitch
|
|||||||
m_pitchBufLo.Copy(m_pitchBufLo, m_detectOverlapSamples - 1, 0, m_pitchBufSize);
|
m_pitchBufLo.Copy(m_pitchBufLo, m_detectOverlapSamples - 1, 0, m_pitchBufSize);
|
||||||
m_pitchBufHi.Copy(m_pitchBufHi, m_detectOverlapSamples - 1, 0, m_pitchBufSize);
|
m_pitchBufHi.Copy(m_pitchBufHi, m_detectOverlapSamples - 1, 0, m_pitchBufSize);
|
||||||
|
|
||||||
pitch2 = m_dsp.DetectPitch(m_pitchBufLo, m_pitchBufHi, m_pitchBufSize);
|
pitch2 = m_dsp.DetectPitch(m_pitchBufLo, m_pitchBufHi);
|
||||||
|
|
||||||
if (pitch2 > 0.0f)
|
if (pitch2 > 0.0f)
|
||||||
{
|
{
|
||||||
@@ -301,6 +316,7 @@ namespace Pitch
|
|||||||
|
|
||||||
samplesProcessed += frameCount;
|
samplesProcessed += frameCount;
|
||||||
}
|
}
|
||||||
|
LastMaxVol = maxVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -311,7 +327,7 @@ namespace Pitch
|
|||||||
if (m_sampleRate < 1.0f)
|
if (m_sampleRate < 1.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_dsp = new PitchDsp(m_sampleRate, kMinFreq, kMaxFreq, m_detectLevelThreshold);
|
m_dsp = new PitchDsp(m_sampleRate, kMinFreq, kMaxFreq);
|
||||||
|
|
||||||
m_iirFilterLoLo = new IIRFilter();
|
m_iirFilterLoLo = new IIRFilter();
|
||||||
m_iirFilterLoLo.Proto = IIRFilter.ProtoType.Butterworth;
|
m_iirFilterLoLo.Proto = IIRFilter.ProtoType.Butterworth;
|
||||||
@@ -411,4 +427,6 @@ namespace Pitch
|
|||||||
public int MidiCents { get; set; }
|
public int MidiCents { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper restore InconsistentNaming
|
||||||
}
|
}
|
||||||
+111
-69
@@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Vocaluxe.Lib.Sound.Record
|
namespace Vocaluxe.Lib.Sound.Record.PitchTracker
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Analyzer from Performous
|
/// Analyzer from Performous
|
||||||
/// Should be very reliable but needs some proof (artificial tests show some errors)
|
/// Should be very reliable but needs some proof (artificial tests show some errors)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class CAnalyzer : IDisposable
|
class CAnalyzer : CPitchTracker
|
||||||
{
|
{
|
||||||
#region Imports
|
#region Imports
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
@@ -29,7 +29,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
private static extern void Analyzer_Process(IntPtr analyzer);
|
private static extern void Analyzer_Process(IntPtr analyzer);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern double Analyzer_GetPeak(IntPtr analyzer);
|
private static extern float Analyzer_GetPeak(IntPtr analyzer);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern double Analyzer_FindNote(IntPtr analyzer, double minFreq, double maxFreq);
|
private static extern double Analyzer_FindNote(IntPtr analyzer, double minFreq, double maxFreq);
|
||||||
@@ -39,45 +39,28 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private IntPtr _Instance;
|
private IntPtr _Instance;
|
||||||
|
private float _VolumeTreshold;
|
||||||
|
|
||||||
public CAnalyzer(uint step = 200)
|
public CAnalyzer(uint step = 200)
|
||||||
{
|
{
|
||||||
_Instance = Analyzer_Create(step);
|
_Instance = Analyzer_Create(step);
|
||||||
|
_VolumeTreshold = 0.01f;
|
||||||
}
|
}
|
||||||
|
|
||||||
~CAnalyzer()
|
public override void Input(byte[] data)
|
||||||
{
|
|
||||||
_Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Input(float[] data)
|
|
||||||
{
|
|
||||||
Analyzer_InputFloat(_Instance, data, data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Input(short[] data)
|
|
||||||
{
|
|
||||||
Analyzer_InputShort(_Instance, data, data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Input(byte[] data)
|
|
||||||
{
|
{
|
||||||
Analyzer_InputByte(_Instance, data, data.Length / 2);
|
Analyzer_InputByte(_Instance, data, data.Length / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process()
|
public override int GetNote(out float maxVolume, float[] weights)
|
||||||
{
|
{
|
||||||
Analyzer_Process(_Instance);
|
Analyzer_Process(_Instance);
|
||||||
}
|
maxVolume = Analyzer_GetPeak(_Instance);
|
||||||
|
if (maxVolume < _VolumeTreshold)
|
||||||
public double GetPeak()
|
return -1;
|
||||||
{
|
int note = (int)Math.Round(Analyzer_FindNote(_Instance, 60, 1800));
|
||||||
return Analyzer_GetPeak(_Instance);
|
_SetWeights(note, weights);
|
||||||
}
|
return note;
|
||||||
|
|
||||||
public double FindNote(double minFreq = 35.0, double maxFreq = 1000.0)
|
|
||||||
{
|
|
||||||
return Analyzer_FindNote(_Instance, minFreq, maxFreq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Output(float[] data, float rate)
|
public bool Output(float[] data, float rate)
|
||||||
@@ -85,7 +68,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
return Analyzer_OutputFloat(_Instance, data, data.Length, rate);
|
return Analyzer_OutputFloat(_Instance, data, data.Length, rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _Dispose(bool disposing)
|
protected override void _Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_Instance == IntPtr.Zero)
|
if (_Instance == IntPtr.Zero)
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
@@ -93,10 +76,15 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
_Instance = IntPtr.Zero;
|
_Instance = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public override int GetNumHalfTones()
|
||||||
{
|
{
|
||||||
_Dispose(true);
|
return NumHalfTonesDef;
|
||||||
GC.SuppressFinalize(this);
|
}
|
||||||
|
|
||||||
|
public override float VolumeTreshold
|
||||||
|
{
|
||||||
|
get { return _VolumeTreshold; }
|
||||||
|
set { _VolumeTreshold = value; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +92,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
/// Pitchtracker (Pt) that uses autocorrelation (AKF) and AMDF
|
/// Pitchtracker (Pt) that uses autocorrelation (AKF) and AMDF
|
||||||
/// Quite fast and perfect in artificial tests, but may fail in real world scenarios (e.g. missing fundamental)
|
/// Quite fast and perfect in artificial tests, but may fail in real world scenarios (e.g. missing fundamental)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class CPtAKF : IDisposable
|
class CPtAKF : CPitchTracker
|
||||||
{
|
{
|
||||||
#region Imports
|
#region Imports
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
@@ -113,14 +101,20 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern void PtAKF_Free(IntPtr analyzer);
|
private static extern void PtAKF_Free(IntPtr analyzer);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", EntryPoint = "PtAKF_GetNumHalfTones", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern int GetNumHalfTones();
|
private static extern int PtAKF_GetNumHalfTones();
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern void PtAKF_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt);
|
private static extern void PtAKF_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern int PtAKF_GetNote(IntPtr analyzer, [Out] out double maxVolume, [Out] float[] weights);
|
private static extern void PtAKF_SetVolumeThreshold(IntPtr analyzer, float threshold);
|
||||||
|
|
||||||
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern float PtAKF_GetVolumeThreshold(IntPtr analyzer);
|
||||||
|
|
||||||
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern int PtAKF_GetNote(IntPtr analyzer, [Out] out float maxVolume, [Out] float[] weights);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private IntPtr _Instance;
|
private IntPtr _Instance;
|
||||||
@@ -130,41 +124,40 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
_Instance = PtAKF_Create(step);
|
_Instance = PtAKF_Create(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
~CPtAKF()
|
public override int GetNumHalfTones()
|
||||||
{
|
{
|
||||||
_Dispose(false);
|
return PtAKF_GetNumHalfTones();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Input(byte[] data)
|
public override float VolumeTreshold
|
||||||
|
{
|
||||||
|
get { return PtAKF_GetVolumeThreshold(_Instance); }
|
||||||
|
set { PtAKF_SetVolumeThreshold(_Instance, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Input(byte[] data)
|
||||||
{
|
{
|
||||||
PtAKF_InputByte(_Instance, data, data.Length / 2);
|
PtAKF_InputByte(_Instance, data, data.Length / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetNote(out double maxVolume, float[] weights)
|
public override int GetNote(out float maxVolume, float[] weights)
|
||||||
{
|
{
|
||||||
return PtAKF_GetNote(_Instance, out maxVolume, weights);
|
return PtAKF_GetNote(_Instance, out maxVolume, weights);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _Dispose(bool disposing)
|
protected override void _Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_Instance == IntPtr.Zero)
|
if (_Instance == IntPtr.Zero)
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
PtAKF_Free(_Instance);
|
PtAKF_Free(_Instance);
|
||||||
_Instance = IntPtr.Zero;
|
_Instance = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pitchtracker (Pt) that uses a dynamic wavelet (DyWa) algorithm
|
/// Pitchtracker (Pt) that uses a dynamic wavelet (DyWa) algorithm
|
||||||
/// Quite fast and perfect in artificial tests, but may fail in real world scenarios (e.g. missing fundamental)
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class CPtDyWa : IDisposable
|
class CPtDyWa : CPitchTracker
|
||||||
{
|
{
|
||||||
#region Imports
|
#region Imports
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
@@ -173,14 +166,17 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern void PtDyWa_Free(IntPtr analyzer);
|
private static extern void PtDyWa_Free(IntPtr analyzer);
|
||||||
|
|
||||||
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern void PtDyWa_SetVolumeThreshold(IntPtr analyzer, float threshold);
|
||||||
|
|
||||||
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern float PtDyWa_GetVolumeThreshold(IntPtr analyzer);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern void PtDyWa_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt);
|
private static extern void PtDyWa_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern int PtDyWa_Process(IntPtr analyzer);
|
private static extern double PtDyWa_FindNote(IntPtr analyzer, [Out] out float maxVolume);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern double PtDyWa_GetNote(IntPtr analyzer);
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private IntPtr _Instance;
|
private IntPtr _Instance;
|
||||||
@@ -190,38 +186,84 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
_Instance = PtDyWa_Create(step);
|
_Instance = PtDyWa_Create(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
~CPtDyWa()
|
public override void Input(byte[] data)
|
||||||
{
|
|
||||||
_Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Input(byte[] data)
|
|
||||||
{
|
{
|
||||||
PtDyWa_InputByte(_Instance, data, data.Length / 2);
|
PtDyWa_InputByte(_Instance, data, data.Length / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process()
|
public override int GetNote(out float maxVolume, float[] weights)
|
||||||
{
|
{
|
||||||
PtDyWa_Process(_Instance);
|
int note = (int)Math.Round(PtDyWa_FindNote(_Instance, out maxVolume));
|
||||||
|
_SetWeights(note, weights);
|
||||||
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double GetNote()
|
public override int GetNumHalfTones()
|
||||||
{
|
{
|
||||||
return PtDyWa_GetNote(_Instance);
|
return NumHalfTonesDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _Dispose(bool disposing)
|
public override float VolumeTreshold
|
||||||
|
{
|
||||||
|
get { return PtDyWa_GetVolumeThreshold(_Instance); }
|
||||||
|
set { PtDyWa_SetVolumeThreshold(_Instance, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void _Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_Instance == IntPtr.Zero)
|
if (_Instance == IntPtr.Zero)
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
PtDyWa_Free(_Instance);
|
PtDyWa_Free(_Instance);
|
||||||
_Instance = IntPtr.Zero;
|
_Instance = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
/// <summary>
|
||||||
|
/// Pitchtracker (Pt) in C# that uses IIR filters for smoothing and an intelligent detection algorithm to reduce calculations
|
||||||
|
/// </summary>
|
||||||
|
class CPtSharp : CPitchTracker
|
||||||
|
{
|
||||||
|
private Pitch.PitchTracker _Instance;
|
||||||
|
|
||||||
|
public CPtSharp()
|
||||||
{
|
{
|
||||||
_Dispose(true);
|
_Instance = new Pitch.PitchTracker {SampleRate = 44100, DetectLevelThreshold = 0.01f, PitchRecordHistorySize = 1};
|
||||||
GC.SuppressFinalize(this);
|
}
|
||||||
|
|
||||||
|
public override void Input(byte[] data)
|
||||||
|
{
|
||||||
|
short[] samplesShort = new short[data.Length / 2];
|
||||||
|
Buffer.BlockCopy(data, 0, samplesShort, 0, data.Length);
|
||||||
|
float[] samplesFloat = new float[samplesShort.Length];
|
||||||
|
for (int i = 0; i < samplesShort.Length; i++)
|
||||||
|
samplesFloat[i] = (float)samplesShort[i] / short.MaxValue;
|
||||||
|
_Instance.ProcessBuffer(samplesFloat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetNote(out float maxVolume, float[] weights)
|
||||||
|
{
|
||||||
|
int note = _Instance.CurrentPitchRecord.MidiNote - 15 - 21;
|
||||||
|
maxVolume = _Instance.LastMaxVol;
|
||||||
|
_SetWeights(note, weights);
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetNumHalfTones()
|
||||||
|
{
|
||||||
|
return NumHalfTonesDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float VolumeTreshold
|
||||||
|
{
|
||||||
|
get { return _Instance.DetectLevelThreshold; }
|
||||||
|
set { _Instance.DetectLevelThreshold = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void _Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_Instance == null)
|
||||||
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
|
_Instance = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Vocaluxe.Lib.Sound.Record.PitchTracker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class/Interface for pitch detectors
|
||||||
|
/// Most likely some structures or system resources are used, hence it should be disposed if no longer needed
|
||||||
|
/// </summary>
|
||||||
|
abstract class CPitchTracker : IDisposable
|
||||||
|
{
|
||||||
|
//Default number of half tones (1 octave)
|
||||||
|
protected const int NumHalfTonesDef = 12;
|
||||||
|
|
||||||
|
~CPitchTracker()
|
||||||
|
{
|
||||||
|
_Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void _Dispose(bool disposing);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method to set weights if the algorithm does not provide anything that could simulate a visualizer.
|
||||||
|
/// Should be called with each valid detection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="note">Current note</param>
|
||||||
|
/// <param name="weights">Array of weights (Length = NumHalfTonesDef)</param>
|
||||||
|
protected static void _SetWeights(int note, float[] weights)
|
||||||
|
{
|
||||||
|
Debug.Assert(weights.Length == NumHalfTonesDef);
|
||||||
|
for (int i = 0; i < NumHalfTonesDef; i++)
|
||||||
|
weights[i] *= 0.9f;
|
||||||
|
if (note < 0)
|
||||||
|
return;
|
||||||
|
int w = note % NumHalfTonesDef;
|
||||||
|
weights[w] = Math.Min(1f, weights[w] + 0.15f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of half tones the algorithm provides weights for.
|
||||||
|
/// This may not be the number of half tones the algorithm detects!
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract int GetNumHalfTones();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The treshold for detecting silence
|
||||||
|
/// Value (0-1) for which everything below is considered silence
|
||||||
|
/// </summary>
|
||||||
|
public abstract float VolumeTreshold { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recorded data. Has to be 16 bit short values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">16bit short values of data</param>
|
||||||
|
public abstract void Input(byte[] data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the current note detected
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maxVolume">Current maximum volume (0-1)</param>
|
||||||
|
/// <param name="weights">Array of NumHalfTones floats that gets filled with the current weights (0-1) where each value indicates how strong that tone is.
|
||||||
|
/// Only for basic visualization as it may not be accurate.</param>
|
||||||
|
/// <returns>Current note index (0 = C2, 12 = C3, ...)</returns>
|
||||||
|
public abstract int GetNote(out float maxVolume, float[] weights);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
<Compile Include="Lib\Sound\Playback\OpenAL\COpenAlStream.cs" />
|
<Compile Include="Lib\Sound\Playback\OpenAL\COpenAlStream.cs" />
|
||||||
<Compile Include="Lib\Sound\Playback\PortAudio\CPortAudioStream.cs" />
|
<Compile Include="Lib\Sound\Playback\PortAudio\CPortAudioStream.cs" />
|
||||||
<Compile Include="Lib\Sound\CPortAudioHandle.cs" />
|
<Compile Include="Lib\Sound\CPortAudioHandle.cs" />
|
||||||
<Compile Include="Lib\Sound\Record\CAnalyzer.cs" />
|
<Compile Include="Lib\Sound\Record\PitchTracker\CAnalyzer.cs" />
|
||||||
<Compile Include="Lib\Sound\Record\CBuffer.cs" />
|
<Compile Include="Lib\Sound\Record\CBuffer.cs" />
|
||||||
<Compile Include="Base\CConfig.cs" />
|
<Compile Include="Base\CConfig.cs" />
|
||||||
<Compile Include="Base\CCover.cs" />
|
<Compile Include="Base\CCover.cs" />
|
||||||
@@ -268,6 +268,7 @@
|
|||||||
<Compile Include="Lib\Sound\Record\CRecordBase.cs" />
|
<Compile Include="Lib\Sound\Record\CRecordBase.cs" />
|
||||||
<Compile Include="Lib\Sound\Record\DirectSound\CSampleDataEventArgs.cs" />
|
<Compile Include="Lib\Sound\Record\DirectSound\CSampleDataEventArgs.cs" />
|
||||||
<Compile Include="Lib\Sound\Record\DirectSound\CSoundCardSource.cs" />
|
<Compile Include="Lib\Sound\Record\DirectSound\CSoundCardSource.cs" />
|
||||||
|
<Compile Include="Lib\Sound\Record\PitchTracker\IPitchTracker.cs" />
|
||||||
<Compile Include="Lib\Sound\Record\Pitch\CircularBuffer.cs" />
|
<Compile Include="Lib\Sound\Record\Pitch\CircularBuffer.cs" />
|
||||||
<Compile Include="Lib\Sound\Record\Pitch\Extensions.cs" />
|
<Compile Include="Lib\Sound\Record\Pitch\Extensions.cs" />
|
||||||
<Compile Include="Lib\Sound\Record\Pitch\IIRFilter.cs" />
|
<Compile Include="Lib\Sound\Record\Pitch\IIRFilter.cs" />
|
||||||
|
|||||||
Reference in new issue
Block a user