From 12d77591c29edda2f0e5fd7ac7ae69a86c35990d Mon Sep 17 00:00:00 2001
From: Nytra <14206961+Nytra@users.noreply.github.com>
Date: Sun, 29 Dec 2024 08:02:34 +0000
Subject: [PATCH 1/4] Add frequency quantize and note frequence, update lang
version
---
ProjectObsidian/ProjectObsidian.csproj | 2 +-
.../ProtoFlux/Math/FrequencyQuantize.cs | 144 ++++++++++++++++++
.../ProtoFlux/Math/NoteFrequency.cs | 22 +++
3 files changed, 167 insertions(+), 1 deletion(-)
create mode 100644 ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
create mode 100644 ProjectObsidian/ProtoFlux/Math/NoteFrequency.cs
diff --git a/ProjectObsidian/ProjectObsidian.csproj b/ProjectObsidian/ProjectObsidian.csproj
index 6f42d64..462344f 100644
--- a/ProjectObsidian/ProjectObsidian.csproj
+++ b/ProjectObsidian/ProjectObsidian.csproj
@@ -3,7 +3,7 @@
Project-Obsidian
Project-Obsidian
net48
- 11
+ 12
Copyright © 2024
Project-Obsidian
diff --git a/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs b/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
new file mode 100644
index 0000000..516428f
--- /dev/null
+++ b/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
@@ -0,0 +1,144 @@
+using System;
+using ProtoFlux.Core;
+using ProtoFlux.Runtimes.Execution;
+using FrooxEngine.ProtoFlux;
+using FrooxEngine;
+using Elements.Assets;
+using Elements.Core;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace ProtoFlux.Runtimes.Execution.Nodes.Obsidian.Math
+{
+ [DataModelType]
+ public enum NoteScale
+ {
+ Chromatic,
+ Major,
+ Minor,
+ Blues,
+ Dorian,
+ Phrygian,
+ Lydian,
+ Mixolydian,
+ Locrian
+ }
+
+ [NodeCategory("Obsidian/Math")]
+ public class FrequencyQuantize : VoidNode
+ {
+ public readonly ValueInput Frequency;
+ public readonly ValueInput Scale;
+ public readonly ValueInput RoundToNearest;
+
+ [DefaultValueAttribute(440f)]
+ public readonly ValueInput ReferenceFrequency;
+
+ public readonly ValueOutput QuantizedFrequency;
+ public readonly ValueOutput InScale;
+
+ public static readonly List ChromaticScale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
+ public static readonly List MajorScale = [0, 2, 4, 5, 7, 9, 11];
+ public static readonly List MinorScale = [0, 2, 3, 5, 7, 8, 10];
+ public static readonly List BluesScale = [0, 3, 5, 6, 7, 10];
+ public static readonly List DorianScale = [0, 2, 3, 5, 7, 9, 10];
+ public static readonly List PhrygianScale = [0, 1, 3, 5, 7, 8, 10];
+ public static readonly List LydianScale = [0, 2, 4, 6, 7, 9, 11];
+ public static readonly List MixolydianScale = [0, 2, 4, 5, 7, 9, 10];
+ public static readonly List LocrianScale = [0, 1, 3, 5, 6, 8, 10];
+ private static int FreqToNearestMidi(float freq)
+ {
+ var n = 12f * (float)System.Math.Log(freq / 440f, 2f) + 69f;
+ return (int)MathX.Round(n, MidpointRounding.AwayFromZero);
+ }
+ private static float MidiToFreq(int midi)
+ {
+ return 440f * MathX.Pow(2, (midi - 69f) / 12f);
+ }
+ protected override void ComputeOutputs(FrooxEngineContext context)
+ {
+ var freq = Frequency.Evaluate(context);
+ var refFreq = ReferenceFrequency.Evaluate(context, 440f);
+
+ //var offset = FreqToNearestMidi(refFreq) - 69; // offset from A4
+ var midi = FreqToNearestMidi(freq);
+
+ //UniLog.Log($"refFreq: {refFreq} offset: {offset}");
+ //UniLog.Log($"freq: {freq} midi: {midi} rawmidi: {FreqToNearestMidi(freq)}");
+
+ bool inScale = false;
+ List scaleList;
+ switch (Scale.Evaluate(context))
+ {
+ case NoteScale.Chromatic:
+ scaleList = ChromaticScale;
+ break;
+ case NoteScale.Major:
+ scaleList = MajorScale;
+ break;
+ case NoteScale.Minor:
+ scaleList = MinorScale;
+ break;
+ case NoteScale.Blues:
+ scaleList = BluesScale;
+ break;
+ case NoteScale.Dorian:
+ scaleList = DorianScale;
+ break;
+ case NoteScale.Phrygian:
+ scaleList = PhrygianScale;
+ break;
+ case NoteScale.Lydian:
+ scaleList = LydianScale;
+ break;
+ case NoteScale.Mixolydian:
+ scaleList = MixolydianScale;
+ break;
+ case NoteScale.Locrian:
+ scaleList = LocrianScale;
+ break;
+ default:
+ scaleList = ChromaticScale;
+ break;
+ }
+ foreach (var note in scaleList)
+ {
+ if (note == midi % 12)
+ {
+ inScale = true;
+ break;
+ }
+ }
+ if (!inScale)
+ {
+ if (RoundToNearest.Evaluate(context))
+ {
+ int octave = (int)(midi / 12f);
+ int closestSemitone = scaleList[0];
+ foreach (int scaleSemitone in scaleList)
+ {
+ if (MathX.Abs(midi % 12 - scaleSemitone) < MathX.Abs(midi % 12 - closestSemitone))
+ closestSemitone = scaleSemitone;
+ }
+ midi = closestSemitone + octave * 12;
+ inScale = true;
+ }
+ }
+ InScale.Write(inScale, context);
+ if (inScale)
+ {
+ var res = MidiToFreq(midi);
+ QuantizedFrequency.Write(res, context);
+ }
+ else
+ {
+ QuantizedFrequency.Write(0f, context);
+ }
+ }
+ public FrequencyQuantize()
+ {
+ QuantizedFrequency = new ValueOutput(this);
+ InScale = new ValueOutput(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectObsidian/ProtoFlux/Math/NoteFrequency.cs b/ProjectObsidian/ProtoFlux/Math/NoteFrequency.cs
new file mode 100644
index 0000000..0fd111b
--- /dev/null
+++ b/ProjectObsidian/ProtoFlux/Math/NoteFrequency.cs
@@ -0,0 +1,22 @@
+using System;
+using ProtoFlux.Core;
+using ProtoFlux.Runtimes.Execution;
+using FrooxEngine.ProtoFlux;
+using FrooxEngine;
+using Elements.Assets;
+using Elements.Core;
+using CoreMidi;
+
+namespace ProtoFlux.Runtimes.Execution.Nodes.Obsidian.Math
+{
+ [NodeCategory("Obsidian/Math")]
+ public class NoteFrequency : ValueFunctionNode
+ {
+ public readonly ValueInput NoteNumber;
+ protected override float Compute(FrooxEngineContext context)
+ {
+ var note = NoteNumber.Evaluate(context);
+ return 440f * MathX.Pow(2, (note - 69f) / 12f);
+ }
+ }
+}
\ No newline at end of file
From a7bc5040b5d7d770bf47e5836ef2b6d20c6aed3a Mon Sep 17 00:00:00 2001
From: Nytra <14206961+Nytra@users.noreply.github.com>
Date: Sun, 29 Dec 2024 10:04:46 +0000
Subject: [PATCH 2/4] Add offset
---
.../ProtoFlux/Math/FrequencyQuantize.cs | 26 +++++++++++--------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs b/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
index 516428f..1005183 100644
--- a/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
+++ b/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
@@ -31,8 +31,7 @@ public class FrequencyQuantize : VoidNode
public readonly ValueInput Scale;
public readonly ValueInput RoundToNearest;
- [DefaultValueAttribute(440f)]
- public readonly ValueInput ReferenceFrequency;
+ public readonly ValueInput Offset;
public readonly ValueOutput QuantizedFrequency;
public readonly ValueOutput InScale;
@@ -46,25 +45,26 @@ public class FrequencyQuantize : VoidNode
public static readonly List LydianScale = [0, 2, 4, 6, 7, 9, 11];
public static readonly List MixolydianScale = [0, 2, 4, 5, 7, 9, 10];
public static readonly List LocrianScale = [0, 1, 3, 5, 6, 8, 10];
+
private static int FreqToNearestMidi(float freq)
{
var n = 12f * (float)System.Math.Log(freq / 440f, 2f) + 69f;
return (int)MathX.Round(n, MidpointRounding.AwayFromZero);
}
+
private static float MidiToFreq(int midi)
{
return 440f * MathX.Pow(2, (midi - 69f) / 12f);
}
+
protected override void ComputeOutputs(FrooxEngineContext context)
{
var freq = Frequency.Evaluate(context);
- var refFreq = ReferenceFrequency.Evaluate(context, 440f);
- //var offset = FreqToNearestMidi(refFreq) - 69; // offset from A4
- var midi = FreqToNearestMidi(freq);
+ var offset = Offset.Evaluate(context);
+ var rootNote = 69 + offset;
- //UniLog.Log($"refFreq: {refFreq} offset: {offset}");
- //UniLog.Log($"freq: {freq} midi: {midi} rawmidi: {FreqToNearestMidi(freq)}");
+ var midi = FreqToNearestMidi(freq);
bool inScale = false;
List scaleList;
@@ -103,7 +103,9 @@ protected override void ComputeOutputs(FrooxEngineContext context)
}
foreach (var note in scaleList)
{
- if (note == midi % 12)
+ var scaleDegree = (midi - rootNote) % 12;
+ if (scaleDegree < 0) scaleDegree += 12;
+ if (note == scaleDegree)
{
inScale = true;
break;
@@ -113,14 +115,15 @@ protected override void ComputeOutputs(FrooxEngineContext context)
{
if (RoundToNearest.Evaluate(context))
{
- int octave = (int)(midi / 12f);
int closestSemitone = scaleList[0];
+ var scaleDegree = (midi - rootNote) % 12;
+ if (scaleDegree < 0) scaleDegree += 12;
foreach (int scaleSemitone in scaleList)
{
- if (MathX.Abs(midi % 12 - scaleSemitone) < MathX.Abs(midi % 12 - closestSemitone))
+ if (MathX.Abs(scaleDegree - scaleSemitone) < MathX.Abs(scaleDegree - closestSemitone))
closestSemitone = scaleSemitone;
}
- midi = closestSemitone + octave * 12;
+ midi += (scaleDegree - closestSemitone);
inScale = true;
}
}
@@ -135,6 +138,7 @@ protected override void ComputeOutputs(FrooxEngineContext context)
QuantizedFrequency.Write(0f, context);
}
}
+
public FrequencyQuantize()
{
QuantizedFrequency = new ValueOutput(this);
From d061a65cff4e67ee82a146cc4b01826629e51629 Mon Sep 17 00:00:00 2001
From: Nytra <14206961+Nytra@users.noreply.github.com>
Date: Sun, 29 Dec 2024 11:43:48 +0000
Subject: [PATCH 3/4] Rename note frequency
---
.../ProtoFlux/Math/{NoteFrequency.cs => MIDI_NoteFrequency.cs} | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
rename ProjectObsidian/ProtoFlux/Math/{NoteFrequency.cs => MIDI_NoteFrequency.cs} (84%)
diff --git a/ProjectObsidian/ProtoFlux/Math/NoteFrequency.cs b/ProjectObsidian/ProtoFlux/Math/MIDI_NoteFrequency.cs
similarity index 84%
rename from ProjectObsidian/ProtoFlux/Math/NoteFrequency.cs
rename to ProjectObsidian/ProtoFlux/Math/MIDI_NoteFrequency.cs
index 0fd111b..78e7f61 100644
--- a/ProjectObsidian/ProtoFlux/Math/NoteFrequency.cs
+++ b/ProjectObsidian/ProtoFlux/Math/MIDI_NoteFrequency.cs
@@ -5,12 +5,11 @@
using FrooxEngine;
using Elements.Assets;
using Elements.Core;
-using CoreMidi;
namespace ProtoFlux.Runtimes.Execution.Nodes.Obsidian.Math
{
[NodeCategory("Obsidian/Math")]
- public class NoteFrequency : ValueFunctionNode
+ public class MIDI_NoteFrequency : ValueFunctionNode
{
public readonly ValueInput NoteNumber;
protected override float Compute(FrooxEngineContext context)
From af40eb487194ef0f7088bc719a8e7b4ab823abe9 Mon Sep 17 00:00:00 2001
From: Nytra <14206961+Nytra@users.noreply.github.com>
Date: Sun, 29 Dec 2024 13:59:12 +0000
Subject: [PATCH 4/4] Fix RoundToNearest
---
.../ProtoFlux/Math/FrequencyQuantize.cs | 22 +++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs b/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
index 1005183..6f2be63 100644
--- a/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
+++ b/ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
@@ -115,15 +115,29 @@ protected override void ComputeOutputs(FrooxEngineContext context)
{
if (RoundToNearest.Evaluate(context))
{
+ int scaleDegree = (midi - rootNote) % 12;
+ if (scaleDegree < 0)
+ scaleDegree += 12;
+
int closestSemitone = scaleList[0];
- var scaleDegree = (midi - rootNote) % 12;
- if (scaleDegree < 0) scaleDegree += 12;
+ int minDifference = int.MaxValue;
+
foreach (int scaleSemitone in scaleList)
{
- if (MathX.Abs(scaleDegree - scaleSemitone) < MathX.Abs(scaleDegree - closestSemitone))
+ int difference = MathX.Abs(scaleDegree - scaleSemitone);
+ if (difference < minDifference)
+ {
+ minDifference = difference;
closestSemitone = scaleSemitone;
+ }
}
- midi += (scaleDegree - closestSemitone);
+
+ int octave = (midi - rootNote) / 12;
+
+ midi += closestSemitone - scaleDegree;
+
+ //UniLog.Log($"midi: {midi} octave: {octave} rootNote: {rootNote} closestSemitone: {closestSemitone} scaleDegree: {scaleDegree}");
+
inScale = true;
}
}