Add an Interface for Pitchtrackers and set VolTresholds

This commit is contained in:
Flamefire committed 2014-04-16 21:05:57 +02:00
1 parent 5f79406cf2
commit d04cbe7ceb
16 files changed
+344 -173

No files matched your search

+30 -12
View File
@@ -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);
} }
/* /*
+7 -4
View File
@@ -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);
} }
+1 -1
View File
@@ -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"
+14 -10
View File
@@ -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;
} }
+5 -3
View File
@@ -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;
}; };
+1
View File
@@ -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
View File
@@ -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++){
+7 -2
View File
@@ -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);
+29 -27
View File
@@ -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");
+1 -1
View File
@@ -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)
+3 -15
View File
@@ -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>
+39 -21
View File
@@ -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
} }
@@ -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);
}
}
+2 -1
View File
@@ -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" />