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; } }