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();
|
||||
}
|
||||
|
||||
double Analyzer_GetPeak(Analyzer* analyzer){
|
||||
float Analyzer_GetPeak(Analyzer* analyzer){
|
||||
if(!analyzer)
|
||||
return -999;
|
||||
return analyzer->getPeak();
|
||||
return static_cast<float>(analyzer->getPeakRaw());
|
||||
}
|
||||
|
||||
double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq){
|
||||
if(!analyzer)
|
||||
return -1;
|
||||
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){
|
||||
@@ -71,6 +71,18 @@ int 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){
|
||||
if(sampleCt <= 0 || !analyzer)
|
||||
return;
|
||||
@@ -82,7 +94,7 @@ void PtAKF_InputByte(PtAKF* analyzer, char* data, int 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)
|
||||
return -1;
|
||||
return analyzer->GetNote(maxVolume, weights);
|
||||
@@ -97,6 +109,18 @@ void PtDyWa_Free(PtDyWa* 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){
|
||||
if(sampleCt <= 0 || !analyzer)
|
||||
return;
|
||||
@@ -105,16 +129,10 @@ void PtDyWa_InputByte(PtDyWa* analyzer, char* data, int sampleCt){
|
||||
freeFloatArray(dataFloat);
|
||||
}
|
||||
|
||||
void PtDyWa_Process(PtDyWa* analyzer){
|
||||
if(!analyzer)
|
||||
return;
|
||||
analyzer->Process();
|
||||
}
|
||||
|
||||
double PtDyWa_GetNote(PtDyWa* analyzer){
|
||||
double PtDyWa_FindNote(PtDyWa* analyzer, float* maxVolume){
|
||||
if(!analyzer)
|
||||
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_InputByte(Analyzer* analyzer, char* data, int sampleCt);
|
||||
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 bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate);
|
||||
|
||||
DllExport PtAKF* PtAKF_Create(unsigned step);
|
||||
DllExport void PtAKF_Free(PtAKF* analyzer);
|
||||
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 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 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_Process(PtDyWa* analyzer);
|
||||
DllExport double PtDyWa_GetNote(PtDyWa* analyzer);
|
||||
DllExport double PtDyWa_FindNote(PtDyWa* analyzer);
|
||||
|
||||
/*
|
||||
public ref class CTone {
|
||||
|
||||
@@ -102,7 +102,7 @@ typedef struct _minmax {
|
||||
struct _minmax *next;
|
||||
} 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;
|
||||
|
||||
int i, j;
|
||||
@@ -143,6 +143,9 @@ double _dywapitch_computeWaveletPitch(double * samples, int startsample, int sam
|
||||
maxValue = maxValue - theDC;
|
||||
minValue = minValue - theDC;
|
||||
double amplitudeMax = (maxValue > -minValue ? maxValue : -minValue);
|
||||
*maxVolume = static_cast<float>(amplitudeMax + theDC);
|
||||
if(*maxVolume < volThreshold)
|
||||
return 0.0;
|
||||
|
||||
ampltitudeThreshold = amplitudeMax*maximaThresholdRatio;
|
||||
//asLog("dywapitch theDC=%f ampltitudeThreshold=%f\n", theDC, ampltitudeThreshold);
|
||||
@@ -439,8 +442,8 @@ void dywapitch_inittracking(dywapitchtracker *pitchtracker) {
|
||||
pitchtracker->_pitchConfidence = -1;
|
||||
}
|
||||
|
||||
double dywapitch_computepitch(dywapitchtracker *pitchtracker, double * samples, int startsample, int samplecount) {
|
||||
double raw_pitch = _dywapitch_computeWaveletPitch(samples, startsample, samplecount);
|
||||
double dywapitch_computepitch(dywapitchtracker *pitchtracker, double * samples, int startsample, int samplecount, float* maxVolume, double volThreshold) {
|
||||
double raw_pitch = _dywapitch_computeWaveletPitch(samples, startsample, samplecount, maxVolume, volThreshold);
|
||||
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
|
||||
// samplecount : the number of samples to use to compte the pitch
|
||||
// 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
|
||||
} // extern "C"
|
||||
|
||||
@@ -5,23 +5,27 @@
|
||||
PtDyWa::PtDyWa(unsigned step){
|
||||
_Step = step;
|
||||
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];
|
||||
size_t size = _AnalysisBuf.size();
|
||||
if(size > _SampleCt)
|
||||
_AnalysisBuf.pop(size - _SampleCt);
|
||||
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt))
|
||||
return;
|
||||
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt)){
|
||||
*maxVolume = _LastMaxVol * 0.85f;
|
||||
return -1;
|
||||
}
|
||||
_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)
|
||||
_LastNote = -1;
|
||||
return -1;
|
||||
else
|
||||
_LastNote = FreqToNote(pitch);
|
||||
}
|
||||
|
||||
double PtDyWa::GetNote(){
|
||||
return _LastNote;
|
||||
return FreqToNote(pitch);
|
||||
}
|
||||
@@ -11,12 +11,14 @@ public:
|
||||
_AnalysisBuf.insert(begin, end);
|
||||
}
|
||||
|
||||
void Process();
|
||||
double GetNote();
|
||||
double FindNote(float* maxVolume);
|
||||
void SetVolumeThreshold(float threshold);
|
||||
float GetVolumeThreshold(){return _VolTreshold;}
|
||||
private:
|
||||
dywapitchtracker _State;
|
||||
constexpr static size_t _SampleCt = 2048;
|
||||
RingBuffer<_SampleCt * 2> _AnalysisBuf;
|
||||
unsigned _Step;
|
||||
double _LastNote;
|
||||
float _VolTreshold;
|
||||
float _LastMaxVol;
|
||||
};
|
||||
@@ -86,6 +86,7 @@ public:
|
||||
fft_t const& getFFT() const { return m_fft; }
|
||||
/** Get the peak level in dB (negative value, 0.0 = clipping). **/
|
||||
double getPeak() const { return 10.0 * log10(m_peak); }
|
||||
double getPeakRaw() const { return m_peak; }
|
||||
/** Get a list of all tones detected. **/
|
||||
tones_t const& getTones() const { return m_tones; }
|
||||
/** 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;
|
||||
_VolTreshold = 0.01f;
|
||||
_LastMaxVol = 0.f;
|
||||
}
|
||||
|
||||
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];
|
||||
size_t size = _AnalysisBuf.size();
|
||||
if(size > _SampleCt)
|
||||
_AnalysisBuf.pop(size - _SampleCt);
|
||||
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt))
|
||||
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt)){
|
||||
*maxVolume = _LastMaxVol * 0.85f;
|
||||
return -1;
|
||||
}
|
||||
_AnalysisBuf.pop(_Step);
|
||||
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
|
||||
|
||||
float maxVolumeL = 0;
|
||||
@@ -105,9 +113,12 @@ int PtAKF::_GetNote(float* restrict samples, double* restrict maxVolume, float*
|
||||
if(vol > maxVolumeL)
|
||||
maxVolumeL = vol;
|
||||
}
|
||||
//maxVolumeL /= 32767.;
|
||||
//maxVolumeL /= 32767.f;
|
||||
*maxVolume = maxVolumeL;
|
||||
|
||||
if(maxVolumeL < _VolTreshold)
|
||||
return -1;
|
||||
|
||||
float samplesWindowed[_SampleCt];
|
||||
|
||||
for(int i = 0; i < _SampleCt; i++){
|
||||
|
||||
@@ -29,7 +29,9 @@ public:
|
||||
_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;}
|
||||
|
||||
private:
|
||||
@@ -48,8 +50,11 @@ private:
|
||||
constexpr static size_t _SampleCt = 2048;
|
||||
RingBuffer<_SampleCt * 2> _AnalysisBuf;
|
||||
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 inline float _AnalyzeByTone(float* restrict samples, float* restrict samplesWindowed, int toneIndex);
|
||||
static float _AKFBySampleCt(float* restrict samples, float samplesPerPeriodD);
|
||||
|
||||
@@ -24,6 +24,8 @@ using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using System.Diagnostics;
|
||||
using Pitch;
|
||||
using Vocaluxe.Lib.Sound.Record.Pitch;
|
||||
using Vocaluxe.Lib.Sound.Record.PitchTracker;
|
||||
using VocaluxeLib;
|
||||
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 _HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
||||
#endif
|
||||
private CPtAKF _Analyzer = new CPtAKF();
|
||||
private CPitchTracker _PitchTracker = new CPtAKF();
|
||||
|
||||
private double _MaxVolume;
|
||||
private float _MaxVolume;
|
||||
|
||||
public CBuffer()
|
||||
{
|
||||
MinVolume = 0.02f;
|
||||
VolTreshold = 0.02f;
|
||||
ToneWeigths = new float[GetNumHalfTones()];
|
||||
Reset();
|
||||
#if TEST_PITCH
|
||||
_TestPitchDetection();
|
||||
Reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -57,9 +60,9 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
_Dispose();
|
||||
}
|
||||
|
||||
public static int GetNumHalfTones()
|
||||
public int GetNumHalfTones()
|
||||
{
|
||||
return CPtAKF.GetNumHalfTones();
|
||||
return _PitchTracker.GetNumHalfTones();
|
||||
}
|
||||
|
||||
public int ToneAbs { get; private set; }
|
||||
@@ -68,7 +71,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
|
||||
public float MaxVolume
|
||||
{
|
||||
get { return (float)_MaxVolume; }
|
||||
get { return _MaxVolume; }
|
||||
}
|
||||
|
||||
public bool ToneValid { get; private set; }
|
||||
@@ -78,7 +81,11 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
/// <summary>
|
||||
/// Minimum volume for a tone to be valid
|
||||
/// </summary>
|
||||
public float MinVolume { get; set; }
|
||||
public float VolTreshold
|
||||
{
|
||||
get { return _PitchTracker.VolumeTreshold; }
|
||||
set { _PitchTracker.VolumeTreshold = value; }
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
@@ -97,14 +104,14 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
// voice passthrough (send data to playback-device)
|
||||
//if (assigned(fVoiceStream)) then
|
||||
//fVoiceStream.WriteData(Buffer, BufferSize);
|
||||
_Analyzer.Input(buffer);
|
||||
_PitchTracker.Input(buffer);
|
||||
}
|
||||
|
||||
public void AnalyzeBuffer()
|
||||
{
|
||||
//_MaxVolume = _Analyzer.GetPeak() / 43 + 1;
|
||||
int tone = _Analyzer.GetNote(out _MaxVolume, ToneWeigths); //(int)Math.Round(_Analyzer.FindNote()); //
|
||||
if (tone >= 0 && _MaxVolume >= MinVolume)
|
||||
int tone = _PitchTracker.GetNote(out _MaxVolume, ToneWeigths); //(int)Math.Round(_Analyzer.FindNote()); //
|
||||
if (tone >= 0)
|
||||
{
|
||||
ToneAbs = tone;
|
||||
Tone = ToneAbs % 12;
|
||||
@@ -122,10 +129,10 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
|
||||
private void _Dispose()
|
||||
{
|
||||
if (_Analyzer != null)
|
||||
if (_PitchTracker != null)
|
||||
{
|
||||
_Analyzer.Dispose();
|
||||
_Analyzer = null;
|
||||
_PitchTracker.Dispose();
|
||||
_PitchTracker = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,11 +295,13 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
int curTimeIndex = -1;
|
||||
int curNote = -1;
|
||||
const int maxSamplesPerBatch = 512;
|
||||
CAnalyzer analyzer2 = new CAnalyzer();
|
||||
CPtDyWa analyzer3 = new CPtDyWa();
|
||||
PitchTracker analyzer4 = new PitchTracker {SampleRate = 44100, DetectLevelThreshold = 0.001f};
|
||||
CPitchTracker analyzer2 = new CAnalyzer();
|
||||
CPitchTracker analyzer3 = new CPtDyWa();
|
||||
CPitchTracker analyzer4 = new CPtSharp();
|
||||
int[] detectedTones = new int[4];
|
||||
int[] oks = new int[4];
|
||||
float dummyMaxVol;
|
||||
float[] dummyWeights = new float[ToneWeigths.Length];
|
||||
int lCt = 0;
|
||||
while (wavFile.NumSamplesLeft > maxSamplesPerBatch)
|
||||
{
|
||||
@@ -303,14 +312,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
AnalyzeBuffer();
|
||||
analyzer2.Input(samples);
|
||||
analyzer3.Input(samples);
|
||||
analyzer2.Process();
|
||||
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);
|
||||
analyzer4.Input(samples);
|
||||
while (curTimeIndex + 1 < tones.Count && time >= tones[curTimeIndex + 1].Time)
|
||||
{
|
||||
curTimeIndex++;
|
||||
@@ -319,9 +321,9 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
if (curNote < 0)
|
||||
continue;
|
||||
detectedTones[0] = ToneValid ? ToneAbs : -1;
|
||||
detectedTones[1] = (int)Math.Round(analyzer2.FindNote(64, 1770));
|
||||
detectedTones[2] = (int)Math.Round(analyzer3.GetNote());
|
||||
detectedTones[3] = analyzer4.CurrentPitchRecord.MidiNote - 15 - 21;
|
||||
detectedTones[1] = analyzer2.GetNote(out dummyMaxVol, dummyWeights);
|
||||
detectedTones[2] = analyzer3.GetNote(out dummyMaxVol, dummyWeights);
|
||||
detectedTones[3] = analyzer4.GetNote(out dummyMaxVol, dummyWeights);
|
||||
lCt++;
|
||||
/*if (!ToneValid)
|
||||
CBase.Log.LogDebug("Note " + _ToneToNote(curNote) + " at " + time + "ms not detected");
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
|
||||
public int NumHalfTones()
|
||||
{
|
||||
return CBuffer.GetNumHalfTones();
|
||||
return _Buffer[0].GetNumHalfTones();
|
||||
}
|
||||
|
||||
public float[] ToneWeigth(int player)
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace Pitch
|
||||
private int m_blockLen34; // 3/4 block len
|
||||
private int m_blockLen44; // 4/4 block len
|
||||
private double m_sampleRate;
|
||||
private float m_detectLevelThreshold;
|
||||
|
||||
private int m_numCourseSteps;
|
||||
private float[] m_pCourseFreqOffset;
|
||||
@@ -45,12 +44,11 @@ namespace Pitch
|
||||
/// <param name="minFreq"></param>
|
||||
/// <param name="maxFreq"></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_minPitch = minPitch;
|
||||
m_maxPitch = maxPitch;
|
||||
m_detectLevelThreshold = detectLevelThreshold;
|
||||
|
||||
m_minNote = (int)(PitchToMidiNote(m_minPitch) + 0.5f) + 2;
|
||||
m_maxNote = (int)(PitchToMidiNote(m_maxPitch) + 0.5f) - 2;
|
||||
@@ -117,19 +115,9 @@ namespace Pitch
|
||||
/// <summary>
|
||||
/// Detect the pitch
|
||||
/// </summary>
|
||||
public float DetectPitch(float[] samplesLo, float[] samplesHi, int numSamples)
|
||||
public float DetectPitch(float[] samplesLo, float[] samplesHi)
|
||||
{
|
||||
float pitch = 0.0f;
|
||||
|
||||
if (!LevelIsAbove(samplesLo, numSamples, m_detectLevelThreshold) &&
|
||||
!LevelIsAbove(samplesHi, numSamples, m_detectLevelThreshold))
|
||||
{
|
||||
// Level is too low
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
pitch = DetectPitchLo(samplesLo, samplesHi);
|
||||
return pitch;
|
||||
return DetectPitchLo(samplesLo, samplesHi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Threading;
|
||||
using Pitch;
|
||||
|
||||
namespace Pitch
|
||||
namespace Vocaluxe.Lib.Sound.Record.Pitch
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracks pitch
|
||||
/// </summary>
|
||||
// ReSharper disable InconsistentNaming
|
||||
public class PitchTracker
|
||||
{
|
||||
private const int kOctaveSteps = 96;
|
||||
@@ -50,6 +50,8 @@ namespace Pitch
|
||||
private IIRFilter m_iirFilterHiLo;
|
||||
private IIRFilter m_iirFilterHiHi;
|
||||
|
||||
public float LastMaxVol = 0;
|
||||
|
||||
public delegate void PitchDetectedHandler(PitchTracker sender, PitchRecord pitchRecord);
|
||||
|
||||
public event PitchDetectedHandler PitchDetected;
|
||||
@@ -87,8 +89,9 @@ namespace Pitch
|
||||
return;
|
||||
|
||||
m_detectLevelThreshold = newValue;
|
||||
Setup();
|
||||
//Setup();
|
||||
}
|
||||
get { return m_detectLevelThreshold; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -229,19 +232,20 @@ namespace Pitch
|
||||
m_circularBufferHi.Available = m_detectOverlapSamples;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// and previously cached buffer.
|
||||
///
|
||||
/// 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
|
||||
/// 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
|
||||
/// event might not be fired at all since additional calls must first be made to accumulate enough
|
||||
/// data do another pitch detect operation.
|
||||
/// </summary>
|
||||
/// <param name="inBuffer">Input buffer. Samples must be in the range -1.0 to 1.0</param>
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// and previously cached buffer.
|
||||
///
|
||||
/// 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
|
||||
/// 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
|
||||
/// event might not be fired at all since additional calls must first be made to accumulate enough
|
||||
/// data do another pitch detect operation.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
public void ProcessBuffer(float[] inBuffer, int sampleCount = 0)
|
||||
{
|
||||
@@ -251,6 +255,8 @@ namespace Pitch
|
||||
int samplesProcessed = 0;
|
||||
int srcLength = sampleCount == 0 ? inBuffer.Length : Math.Min(sampleCount, inBuffer.Length);
|
||||
|
||||
float maxVolume = LastMaxVol * 0.85f;
|
||||
|
||||
while (samplesProcessed < srcLength)
|
||||
{
|
||||
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))
|
||||
{
|
||||
float pitch1;
|
||||
float pitch2 = 0.0f;
|
||||
float pitch2;
|
||||
float detectedPitch = 0.0f;
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -281,7 +296,7 @@ namespace Pitch
|
||||
m_pitchBufLo.Copy(m_pitchBufLo, 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)
|
||||
{
|
||||
@@ -301,6 +316,7 @@ namespace Pitch
|
||||
|
||||
samplesProcessed += frameCount;
|
||||
}
|
||||
LastMaxVol = maxVolume;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -311,7 +327,7 @@ namespace Pitch
|
||||
if (m_sampleRate < 1.0f)
|
||||
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.Proto = IIRFilter.ProtoType.Butterworth;
|
||||
@@ -411,4 +427,6 @@ namespace Pitch
|
||||
public int MidiCents { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
+111
-69
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Record
|
||||
namespace Vocaluxe.Lib.Sound.Record.PitchTracker
|
||||
{
|
||||
/// <summary>
|
||||
/// Analyzer from Performous
|
||||
/// Should be very reliable but needs some proof (artificial tests show some errors)
|
||||
/// </summary>
|
||||
class CAnalyzer : IDisposable
|
||||
class CAnalyzer : CPitchTracker
|
||||
{
|
||||
#region Imports
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
@@ -29,7 +29,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
private static extern void Analyzer_Process(IntPtr analyzer);
|
||||
|
||||
[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)]
|
||||
private static extern double Analyzer_FindNote(IntPtr analyzer, double minFreq, double maxFreq);
|
||||
@@ -39,45 +39,28 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
#endregion
|
||||
|
||||
private IntPtr _Instance;
|
||||
private float _VolumeTreshold;
|
||||
|
||||
public CAnalyzer(uint step = 200)
|
||||
{
|
||||
_Instance = Analyzer_Create(step);
|
||||
_VolumeTreshold = 0.01f;
|
||||
}
|
||||
|
||||
~CAnalyzer()
|
||||
{
|
||||
_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)
|
||||
public override void Input(byte[] data)
|
||||
{
|
||||
Analyzer_InputByte(_Instance, data, data.Length / 2);
|
||||
}
|
||||
|
||||
public void Process()
|
||||
public override int GetNote(out float maxVolume, float[] weights)
|
||||
{
|
||||
Analyzer_Process(_Instance);
|
||||
}
|
||||
|
||||
public double GetPeak()
|
||||
{
|
||||
return Analyzer_GetPeak(_Instance);
|
||||
}
|
||||
|
||||
public double FindNote(double minFreq = 35.0, double maxFreq = 1000.0)
|
||||
{
|
||||
return Analyzer_FindNote(_Instance, minFreq, maxFreq);
|
||||
maxVolume = Analyzer_GetPeak(_Instance);
|
||||
if (maxVolume < _VolumeTreshold)
|
||||
return -1;
|
||||
int note = (int)Math.Round(Analyzer_FindNote(_Instance, 60, 1800));
|
||||
_SetWeights(note, weights);
|
||||
return note;
|
||||
}
|
||||
|
||||
public bool Output(float[] data, float rate)
|
||||
@@ -85,7 +68,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
return Analyzer_OutputFloat(_Instance, data, data.Length, rate);
|
||||
}
|
||||
|
||||
private void _Dispose(bool disposing)
|
||||
protected override void _Dispose(bool disposing)
|
||||
{
|
||||
if (_Instance == IntPtr.Zero)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
@@ -93,10 +76,15 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
_Instance = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public override int GetNumHalfTones()
|
||||
{
|
||||
_Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
return NumHalfTonesDef;
|
||||
}
|
||||
|
||||
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
|
||||
/// Quite fast and perfect in artificial tests, but may fail in real world scenarios (e.g. missing fundamental)
|
||||
/// </summary>
|
||||
class CPtAKF : IDisposable
|
||||
class CPtAKF : CPitchTracker
|
||||
{
|
||||
#region Imports
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
@@ -113,14 +101,20 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void PtAKF_Free(IntPtr analyzer);
|
||||
|
||||
[DllImport("PitchTracker.dll", EntryPoint = "PtAKF_GetNumHalfTones", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int GetNumHalfTones();
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int PtAKF_GetNumHalfTones();
|
||||
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void PtAKF_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt);
|
||||
|
||||
[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
|
||||
|
||||
private IntPtr _Instance;
|
||||
@@ -130,41 +124,40 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
_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);
|
||||
}
|
||||
|
||||
public int GetNote(out double maxVolume, float[] weights)
|
||||
public override int GetNote(out float maxVolume, float[] weights)
|
||||
{
|
||||
return PtAKF_GetNote(_Instance, out maxVolume, weights);
|
||||
}
|
||||
|
||||
private void _Dispose(bool disposing)
|
||||
protected override void _Dispose(bool disposing)
|
||||
{
|
||||
if (_Instance == IntPtr.Zero)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
PtAKF_Free(_Instance);
|
||||
_Instance = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
class CPtDyWa : IDisposable
|
||||
class CPtDyWa : CPitchTracker
|
||||
{
|
||||
#region Imports
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
@@ -173,14 +166,17 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
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)]
|
||||
private static extern void PtDyWa_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt);
|
||||
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int PtDyWa_Process(IntPtr analyzer);
|
||||
|
||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern double PtDyWa_GetNote(IntPtr analyzer);
|
||||
private static extern double PtDyWa_FindNote(IntPtr analyzer, [Out] out float maxVolume);
|
||||
#endregion
|
||||
|
||||
private IntPtr _Instance;
|
||||
@@ -190,38 +186,84 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
_Instance = PtDyWa_Create(step);
|
||||
}
|
||||
|
||||
~CPtDyWa()
|
||||
{
|
||||
_Dispose(false);
|
||||
}
|
||||
|
||||
public void Input(byte[] data)
|
||||
public override void Input(byte[] data)
|
||||
{
|
||||
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)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
PtDyWa_Free(_Instance);
|
||||
_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);
|
||||
GC.SuppressFinalize(this);
|
||||
_Instance = new Pitch.PitchTracker {SampleRate = 44100, DetectLevelThreshold = 0.01f, PitchRecordHistorySize = 1};
|
||||
}
|
||||
|
||||
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\PortAudio\CPortAudioStream.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="Base\CConfig.cs" />
|
||||
<Compile Include="Base\CCover.cs" />
|
||||
@@ -268,6 +268,7 @@
|
||||
<Compile Include="Lib\Sound\Record\CRecordBase.cs" />
|
||||
<Compile Include="Lib\Sound\Record\DirectSound\CSampleDataEventArgs.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\Extensions.cs" />
|
||||
<Compile Include="Lib\Sound\Record\Pitch\IIRFilter.cs" />
|
||||
|
||||
Reference in new issue
Block a user