Ich habe diese SoundAnalyzer-Klasse erstellt, um Beats in Songs zu erkennen:
class SoundAnalyzer
{
public SoundBuffer soundData;
public Sound sound;
public List<double> beatMarkers = new List<double>();
public SoundAnalyzer(string path)
{
soundData = new SoundBuffer(path);
sound = new Sound(soundData);
}
// C = threshold, N = size of history buffer / 1024 B = bands
public void PlaceBeatMarkers(float C, int N, int B)
{
List<double>[] instantEnergyList = new List<double>[B];
GetEnergyList(B, ref instantEnergyList);
for (int i = 0; i < B; i++)
{
PlaceMarkers(instantEnergyList[i], N, C);
}
beatMarkers.Sort();
}
private short[] getRange(int begin, int end, short[] array)
{
short[] result = new short[end - begin];
for (int i = 0; i < end - begin; i++)
{
result[i] = array[begin + i];
}
return result;
}
// get a array of with a list of energy for each band
private void GetEnergyList(int B, ref List<double>[] instantEnergyList)
{
for (int i = 0; i < B; i++)
{
instantEnergyList[i] = new List<double>();
}
short[] samples = soundData.Samples;
float timePerSample = 1 / (float)soundData.SampleRate;
int sampleIndex = 0;
int nextSamples = 1024;
int samplesPerBand = nextSamples / B;
// for the whole song
while (sampleIndex + nextSamples < samples.Length)
{
complex[] FFT = FastFourier.Calculate(getRange(sampleIndex, nextSamples + sampleIndex, samples));
// foreach band
for (int i = 0; i < B; i++)
{
double energy = 0;
for (int j = 0; j < samplesPerBand; j++)
energy += FFT[i * samplesPerBand + j].GetMagnitude();
energy /= samplesPerBand;
instantEnergyList[i].Add(energy);
}
if (sampleIndex + nextSamples >= samples.Length)
nextSamples = samples.Length - sampleIndex - 1;
sampleIndex += nextSamples;
samplesPerBand = nextSamples / B;
}
}
// place the actual markers
private void PlaceMarkers(List<double> instantEnergyList, int N, float C)
{
double timePerSample = 1 / (double)soundData.SampleRate;
int index = N;
int numInBuffer = index;
double historyBuffer = 0;
//Fill the history buffer with n * instant energy
for (int i = 0; i < index; i++)
{
historyBuffer += instantEnergyList[i];
}
// If instantEnergy / samples in buffer < instantEnergy for the next sample then add beatmarker.
while (index + 1 < instantEnergyList.Count)
{
if(instantEnergyList[index + 1] > (historyBuffer / numInBuffer) * C)
beatMarkers.Add((index + 1) * 1024 * timePerSample);
historyBuffer -= instantEnergyList[index - numInBuffer];
historyBuffer += instantEnergyList[index + 1];
index++;
}
}
}
Aus irgendeinem Grund werden nur Beats zwischen 637 und 641 Sekunden erkannt, und ich habe keine Ahnung, warum. Ich weiß, dass die Beats von mehreren Bändern eingefügt werden, da ich Duplikate finde, und es scheint, als würde jedem momentanen Energiewert zwischen diesen Werten ein Beat zugewiesen.
Es ist nach folgendem Vorbild aufgebaut: http://www.flipcode.com/misc/BeatDetectionAlgorithms.pdf
Warum werden die Beats nicht richtig registriert?
Antworten:
Ich habe es probiert, was dumm war, weil ich mit Fourier-Transformationen oder Musiktheorie nicht vertraut war. Nach einigen Studien habe ich keine Lösung, aber ich sehe einige beunruhigende Dinge:
*// Fill the history buffer with n * instant energy*
und der folgende Code sind nicht lebendig.Nach einer Weile hatte ich das Gefühl, dass der Code nicht wirklich gut organisiert ist und es eine Zeitverschwendung wäre, zu versuchen, ihn zu reparieren. Wenn Sie denken, dass es sich lohnt, ist der nächste Schritt, den ich machen würde:
Tipps
quelle