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();
}
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);
}
/*
+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_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);
}
+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
// 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"
+14 -10
View File
@@ -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);
}
+5 -3
View File
@@ -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;
};
+1
View File
@@ -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
View File
@@ -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++){
+7 -2
View File
@@ -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);
+29 -27
View File
@@ -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");
+1 -1
View File
@@ -132,7 +132,7 @@ namespace Vocaluxe.Lib.Sound.Record
public int NumHalfTones()
{
return CBuffer.GetNumHalfTones();
return _Buffer[0].GetNumHalfTones();
}
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_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>
+39 -21
View File
@@ -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
}
@@ -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);
}
}
+2 -1
View File
@@ -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" />