Skip to content

Commit

Permalink
Merge pull request #4 from Seank23/CX475_Project_Main
Browse files Browse the repository at this point in the history
Cx475 project main
  • Loading branch information
Seank23 authored Feb 6, 2021
2 parents 54f51be + 3af3ba9 commit 3b114bd
Show file tree
Hide file tree
Showing 27 changed files with 1,001 additions and 397 deletions.
30 changes: 15 additions & 15 deletions MusicAnalyser.UnitTests/AppControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@ public void Setup()
[Test]
public void Test_PerformFFT_100HzSine()
{
short[] inputSignal = new short[1024];
double[] fft;
for(int i = 0; i < inputSignal.Length; i++)
{
inputSignal[i] = (short)(Math.Sin(2 * Math.PI * 100 * i / 1000) * 32768);
}
double fftScale = app.PerformFFT(inputSignal, out fft, 1000);
Assert.AreEqual(100, Math.Round(GetLargestIndex(fft) / fftScale));
//short[] inputSignal = new short[1024];
//double[] fft;
//for(int i = 0; i < inputSignal.Length; i++)
//{
// inputSignal[i] = (short)(Math.Sin(2 * Math.PI * 100 * i / 1000) * 32768);
//}
//double fftScale = app.PerformFFT(inputSignal, out fft, 1000);
//Assert.AreEqual(100, Math.Round(GetLargestIndex(fft) / fftScale));
}

[Test]
public void Test_SmoothSignal()
{
app.dataFftPrev.Add(new double[] { 7, 3, 6, 7, 1 });
app.dataFftPrev.Add(new double[] { 3, 8, 2, 9, 3 });
app.dataFftPrev.Add(new double[] { 5, 2, 4, 7, 8 });
app.dataFftPrev.Add(new double[] { 8, 2, 4, 1, 7 });
double[] signal = new double[] { 2, 7, 4, 5, 9 };
signal = app.SmoothSignal(signal, 5);
Assert.AreEqual(new double[] { 5, 4.4, 4, 5.8, 5.6 }, signal);
//app.dataFftPrev.Add(new double[] { 7, 3, 6, 7, 1 });
//app.dataFftPrev.Add(new double[] { 3, 8, 2, 9, 3 });
//app.dataFftPrev.Add(new double[] { 5, 2, 4, 7, 8 });
//app.dataFftPrev.Add(new double[] { 8, 2, 4, 1, 7 });
//double[] signal = new double[] { 2, 7, 4, 5, 9 };
//signal = app.SmoothSignal(signal, 5);
//Assert.AreEqual(new double[] { 5, 4.4, 4, 5.8, 5.6 }, signal);
}

private int GetLargestIndex(double[] array)
Expand Down
214 changes: 75 additions & 139 deletions MusicAnalyser/App/Analysis/Analyser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,75 +153,15 @@ public void FindKey()
{
double[] percents = new double[notePercent.Length];
Array.Copy(notePercent, percents, percents.Length);
string[] dominantNotes = new string[7];
double largestPercent;
int largestIndex;
Dictionary<int, double> noteDict = new Dictionary<int, double>();

// Finds the 7 most common notes based on the percentage of the total notes identified that note occupies
for (int i = 0; i < dominantNotes.Length; i++)
{
largestPercent = percents[0];
largestIndex = 0;
for (int j = 1; j < percents.Length; j++)
{
if (percents[j] > largestPercent)
{
largestPercent = percents[j];
largestIndex = j;
}
}
dominantNotes[i] = Music.GetNoteName(largestIndex);
percents[largestIndex] = 0;
}

int[] keyProbability = Music.FindScaleProbability(dominantNotes);
for (int i = 0; i < percents.Length; i++)
noteDict.Add(i, percents[i]);

largestIndex = 0;
bool confident = false;
List<int> possibleKeys = new List<int>();
double[] keyPercents = Music.FindTotalScalePercentages(noteDict);
var maxPercent = keyPercents.Select((n, i) => (Number: n, Index: i)).Max();

// Based on the key probablities, decides the most likely key signature at current time
for (int i = 0; i < keyProbability.Length; i++)
{
if (keyProbability[i] == 7) // If the 7 most common notes are present in a single key signature, then that is the most likely key signature
{
largestIndex = i;
possibleKeys.Clear();
confident = true;
break;
}
else if (keyProbability[i] == 6) // If 6 of the most common notes are present in a key signature, then that is a possible key
{
possibleKeys.Add(i);
}
}
if (possibleKeys.Count > 0)
{
List<double> lowNotes = new List<double>();
// Finds most likely of possible keys by checking the missing note in each, if it is more common than the missing notes in other possible keys then that is the most likely key
foreach (int keyIndex in possibleKeys)
{
string[] scale = new string[7];
Array.Copy(Music.Scales, keyIndex * 7, scale, 0, scale.Length);
double lowest = notePercent[Music.GetNoteIndex(scale[0] + "0")];
for (int i = 1; i < scale.Length; i++)
{
double percentage = notePercent[Music.GetNoteIndex(scale[i] + "0")];
if (percentage < lowest)
lowest = percentage;
}
lowNotes.Add(lowest);
}
int highOfLowIndex = lowNotes.IndexOf(lowNotes.Max());
largestIndex = possibleKeys[highOfLowIndex];
}
else if (!confident) // There is no discernable key
{
currentKey = "N/A";
majorKeyRoot = "N/A";
return;
}
string keyRoot = Music.GetNoteName(largestIndex);
string keyRoot = Music.GetNoteName(maxPercent.Index);
if (music.IsMinor(keyRoot, out string minorRoot)) // Checks if key is most likely the relative minor of original prediction
currentKey = minorRoot + " Minor";
else
Expand All @@ -236,15 +176,58 @@ public bool FindChordsNotes()
if (aggregateNotes.Count == 0)
return false;

int[,] tempNoteOccurences = new int[12, 2];
// UNUSED
//List<Note>[] notesByName = new List<Note>[12];
//for (int i = 0; i < 12; i++)
// notesByName[i] = new List<Note>();
//Dictionary<string, double> noteDistributionDict = new Dictionary<string, double>();

//for(int i = 0; i < aggregateNotes.Count; i++)
//{
// string noteName = aggregateNotes[i].Name;
// double noteMag = aggregateNotes[i].Magnitude;
// int index = aggregateNotes[i].NoteIndex;
// notesByName[index].Add(aggregateNotes[i]);
// if (noteDistributionDict.ContainsKey(noteName))
// noteDistributionDict[noteName] += noteMag;
// else
// noteDistributionDict.Add(noteName, noteMag);
//}
//string[] keys = noteDistributionDict.Keys.ToArray();
//foreach (string key in keys)
// noteDistributionDict[key] /= Prefs.CHORD_DETECTION_INTERVAL;

//noteDistributionDict = noteDistributionDict.OrderByDescending(x => x.Value).Take(4).ToDictionary(x => x.Key, x => x.Value);

//chordNotes = new List<Note>[noteDistributionDict.Count];
//keys = noteDistributionDict.Keys.ToArray();
//for (int i = 0; i < chordNotes.Length; i++)
//{
// chordNotes[i] = new List<Note>();
// List<int> octaves = new List<int>();
// int noteIndex = Music.GetNoteIndex(keys[i] + "0");
// for (int j = 0; j < notesByName[noteIndex].Count; j++)
// {
// if (!octaves.Contains(notesByName[noteIndex][j].Octave)) // Stores each prominent note in collected notes only once
// {
// chordNotes[i].Add(notesByName[noteIndex][j]);
// octaves.Add(notesByName[noteIndex][j].Octave);
// }
// }
// chordNotes[i] = chordNotes[i].OrderBy(x => x.Frequency).ToList(); // Order: Frequency - low to high
//}
//aggregateNotes.Clear();
//return true;

int[,] tempNoteOccurences = new int[12, 2]; // Note index, timestamp
List<Note>[] notesByName = new List<Note>[12];
for (int i = 0; i < 12; i++)
notesByName[i] = new List<Note>();

int initialTimeStamp = aggregateNotes[0].TimeStamp;
int timeStampOffset = 0;

for(int i = 0; i < aggregateNotes.Count; i++)
for (int i = 0; i < aggregateNotes.Count; i++)
{
int index = aggregateNotes[i].NoteIndex;
notesByName[index].Add(aggregateNotes[i]);
Expand All @@ -258,68 +241,28 @@ public bool FindChordsNotes()
}
aggregateNotes.Clear();

int numChordNotes = 0;
List<int> chordNoteIndexes = new List<int>();
for(int i = 0; i < tempNoteOccurences.Length / 2; i++)
for (int i = 0; i < tempNoteOccurences.Length / 2; i++)
{
if(tempNoteOccurences[i, 0] >= Prefs.CHORD_NOTE_OCCURENCE_OFFSET)
{
numChordNotes++;
if (tempNoteOccurences[i, 0] >= Prefs.CHORD_NOTE_OCCURENCE_OFFSET) // Prunes spurious notes
chordNoteIndexes.Add(i);
}
}

chordNotes = new List<Note>[numChordNotes];
for(int i = 0; i < numChordNotes; i++)
chordNotes = new List<Note>[chordNoteIndexes.Count];
for (int i = 0; i < chordNotes.Length; i++)
{
chordNotes[i] = new List<Note>();
List<int> octaves = new List<int>();
for(int j = 0; j < notesByName[chordNoteIndexes[i]].Count; j++)
for (int j = 0; j < notesByName[chordNoteIndexes[i]].Count; j++)
{
if (!octaves.Contains(notesByName[chordNoteIndexes[i]][j].Octave))
if (!octaves.Contains(notesByName[chordNoteIndexes[i]][j].Octave)) // Stores each prominent note in collected notes only once
{
chordNotes[i].Add(notesByName[chordNoteIndexes[i]][j]);
octaves.Add(notesByName[chordNoteIndexes[i]][j].Octave);
}
}
chordNotes[i] = chordNotes[i].OrderBy(x => x.Frequency).ToList(); // Order: Frequency - low to high
}

int removed = 0;
for(int i = 0; i < chordNotes.Length; i++)
{
if(chordNotes[i].Count == 1 && chordNotes[i][0].Octave > 4)
{
chordNotes[i].Clear();
removed++;
continue;
}

int lowestOctave = chordNotes[i][0].Octave;
for(int j = 1; j < chordNotes[i].Count; j++)
{
if (chordNotes[i][j].Octave < lowestOctave)
lowestOctave = chordNotes[i][j].Octave;
}

if (lowestOctave > 4)
{
chordNotes[i].Clear();
removed++;
}
}

List<Note>[] chordTemp = new List<Note>[chordNotes.Length - removed];
int tempIndex = 0;
for(int i = 0; i < chordNotes.Length; i++)
{
if (chordNotes[i].Count != 0)
{
chordTemp[tempIndex] = chordNotes[i];
tempIndex++;
}
}
chordNotes = chordTemp;
return true;
}

Expand Down Expand Up @@ -353,9 +296,9 @@ public void FindChords()
noteDifference = 12 + noteDifference;
intervals.Add(noteDifference);
}
string chordQuality = Music.GetChordQuality(intervals); // Determines chord quality from intervals
if(chordQuality != "N/A")
chords.Add(CreateChord(myChordNotes[0].Name, chordQuality, myChordNotes));
string chordQuality = Music.GetChordQuality(intervals, out int fifthOmitted); // Determines chord quality from intervals
if (chordQuality != "N/A")
chords.Add(CreateChord(myChordNotes[0].Name, chordQuality, myChordNotes, fifthOmitted));

myChordNotes = NextChord(myChordNotes); // Iterates chord root note
}
Expand All @@ -365,7 +308,7 @@ public void FindChords()
prevChord = chords[0];
}

private Chord CreateChord(string root, string quality, List<Note> notes)
private Chord CreateChord(string root, string quality, List<Note> notes, int fifthOmitted)
{
Note[] tempNotes = new Note[notes.Count];
Array.Copy(notes.ToArray(), tempNotes, notes.Count);
Expand All @@ -381,7 +324,8 @@ private Chord CreateChord(string root, string quality, List<Note> notes)
Root = root,
Quality = quality,
Notes = tempNotes.ToList(),
NumExtensions = numExtensions
NumExtensions = numExtensions,
FifthOmitted = fifthOmitted
};
return myChord;
}
Expand Down Expand Up @@ -412,44 +356,36 @@ private void AdjustChordProbabilities()
rootFreq[i] = chords[i].Notes[0].Frequency;
double avgFreq = rootFreq.Average();
for (int i = 0; i < rootFreq.Length; i++)
rootFreq[i] -= avgFreq;
rootFreq[i] = avgFreq - rootFreq[i];
rootFreq = Normalise(rootFreq);

double[] chordExtensions = new double[chords.Count];
for (int i = 0; i < chords.Count; i++)
chordExtensions[i] = chords[i].NumExtensions;
double avgExtensions = chordExtensions.Average();
for (int i = 0; i < chordExtensions.Length; i++)
chordExtensions[i] -= avgExtensions;
chordExtensions[i] = avgExtensions - chordExtensions[i];
chordExtensions = Normalise(chordExtensions);

double[] fifthOmitted = new double[chords.Count];
for (int i = 0; i < chords.Count; i++)
fifthOmitted[i] = chords[i].FifthOmitted;
fifthOmitted = Normalise(fifthOmitted);

double[] chordPredictedBefore = new double[chords.Count];
if (prevChord != null)
{
for(int i = 0; i < chords.Count; i++)
{
if (chords[i].Root == prevChord.Root)
chordPredictedBefore[i] += 1;
}
}

double[] rootInKey = new double[chords.Count];
if (majorKeyRoot != "N/A")
{
string[] scale = new string[7];
Array.Copy(Music.Scales, Music.GetNoteIndex(majorKeyRoot + "0") * 7, scale, 0, scale.Length);
for (int i = 0; i < chords.Count; i++)
{
if (scale.Contains(chords[i].Root))
rootInKey[i] += 1;
else
rootInKey[i] -= 1;
if (chords[i].Root == prevChord.Root)
chordPredictedBefore[i] += 1;
}
}

double[] overallProb = new double[chords.Count];
for(int i = 0; i < chords.Count; i++)
overallProb[i] = rootMagnitudes[i] + rootOccurences[i] - 2 * rootFreq[i] - chordExtensions[i] + 1.5 * chordPredictedBefore[i] + rootInKey[i];
for (int i = 0; i < chords.Count; i++)
//overallProb[i] = 1.0 * rootMagnitudes[i] + 1.0 * rootOccurences[i] + 1.0 * chordExtensions[i] + 2 * rootFreq[i] + 1.0 * fifthOmitted[i] + 1.5 * chordPredictedBefore[i];
overallProb[i] = 1.1*rootMagnitudes[i] + 1.0*rootOccurences[i] + 2.3*chordExtensions[i] + 1.8*rootFreq[i] + 1.0*fifthOmitted[i] + 0.7*chordPredictedBefore[i];
overallProb = Normalise(overallProb);
double probSum = overallProb[0] + 1;
for (int i = 1; i < chords.Count; i++)
Expand Down
1 change: 1 addition & 0 deletions MusicAnalyser/App/Analysis/Chord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Chord
public string Quality { get; set; }
public List<Note> Notes { get; set; }
public int NumExtensions { get; set; }
public int FifthOmitted { get; set; }
public double Probability { get; set; }
}
}
Loading

0 comments on commit 3b114bd

Please sign in to comment.