Skip to content

Commit

Permalink
Merge pull request #66 from Nytra/audioWork
Browse files Browse the repository at this point in the history
Add frequency quantize and MIDI note frequency nodes
  • Loading branch information
Xlinka authored Dec 29, 2024
2 parents 30fd351 + af40eb4 commit 536d406
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 1 deletion.
2 changes: 1 addition & 1 deletion ProjectObsidian/ProjectObsidian.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<RootNamespace>Project-Obsidian</RootNamespace>
<AssemblyName>Project-Obsidian</AssemblyName>
<TargetFramework>net48</TargetFramework>
<LangVersion>11</LangVersion>
<LangVersion>12</LangVersion>
<Copyright>Copyright © 2024</Copyright>
<Product>Project-Obsidian</Product>
</PropertyGroup>
Expand Down
162 changes: 162 additions & 0 deletions ProjectObsidian/ProtoFlux/Math/FrequencyQuantize.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
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<FrooxEngineContext>
{
public readonly ValueInput<float> Frequency;
public readonly ValueInput<NoteScale> Scale;
public readonly ValueInput<bool> RoundToNearest;

public readonly ValueInput<int> Offset;

public readonly ValueOutput<float> QuantizedFrequency;
public readonly ValueOutput<bool> InScale;

public static readonly List<int> ChromaticScale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
public static readonly List<int> MajorScale = [0, 2, 4, 5, 7, 9, 11];
public static readonly List<int> MinorScale = [0, 2, 3, 5, 7, 8, 10];
public static readonly List<int> BluesScale = [0, 3, 5, 6, 7, 10];
public static readonly List<int> DorianScale = [0, 2, 3, 5, 7, 9, 10];
public static readonly List<int> PhrygianScale = [0, 1, 3, 5, 7, 8, 10];
public static readonly List<int> LydianScale = [0, 2, 4, 6, 7, 9, 11];
public static readonly List<int> MixolydianScale = [0, 2, 4, 5, 7, 9, 10];
public static readonly List<int> 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 offset = Offset.Evaluate(context);
var rootNote = 69 + offset;

var midi = FreqToNearestMidi(freq);

bool inScale = false;
List<int> 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)
{
var scaleDegree = (midi - rootNote) % 12;
if (scaleDegree < 0) scaleDegree += 12;
if (note == scaleDegree)
{
inScale = true;
break;
}
}
if (!inScale)
{
if (RoundToNearest.Evaluate(context))
{
int scaleDegree = (midi - rootNote) % 12;
if (scaleDegree < 0)
scaleDegree += 12;

int closestSemitone = scaleList[0];
int minDifference = int.MaxValue;

foreach (int scaleSemitone in scaleList)
{
int difference = MathX.Abs(scaleDegree - scaleSemitone);
if (difference < minDifference)
{
minDifference = difference;
closestSemitone = scaleSemitone;
}
}

int octave = (midi - rootNote) / 12;

midi += closestSemitone - scaleDegree;

//UniLog.Log($"midi: {midi} octave: {octave} rootNote: {rootNote} closestSemitone: {closestSemitone} scaleDegree: {scaleDegree}");

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<float>(this);
InScale = new ValueOutput<bool>(this);
}
}
}
21 changes: 21 additions & 0 deletions ProjectObsidian/ProtoFlux/Math/MIDI_NoteFrequency.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using ProtoFlux.Core;
using ProtoFlux.Runtimes.Execution;
using FrooxEngine.ProtoFlux;
using FrooxEngine;
using Elements.Assets;
using Elements.Core;

namespace ProtoFlux.Runtimes.Execution.Nodes.Obsidian.Math
{
[NodeCategory("Obsidian/Math")]
public class MIDI_NoteFrequency : ValueFunctionNode<FrooxEngineContext, float>
{
public readonly ValueInput<int> NoteNumber;
protected override float Compute(FrooxEngineContext context)
{
var note = NoteNumber.Evaluate(context);
return 440f * MathX.Pow(2, (note - 69f) / 12f);
}
}
}

0 comments on commit 536d406

Please sign in to comment.