Last active
November 17, 2021 17:12
-
-
Save nekomimi-daimao/9a96fa5415ca2bb51e780dc04293fe34 to your computer and use it in GitHub Desktop.
unity, create musical scale AudioClip
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
namespace Nekomimi.Daimao | |
{ | |
public static class MusicalScale | |
{ | |
[Flags] | |
public enum Scale | |
{ | |
None = 0, | |
C4 = 1 << 0, | |
D4 = 1 << 1, | |
E4 = 1 << 2, | |
F = 1 << 3, | |
G4 = 1 << 4, | |
A4 = 1 << 5, | |
B4 = 1 << 6, | |
C5 = 1 << 7, | |
} | |
internal static readonly Dictionary<Scale, float> HzTable = new Dictionary<Scale, float> | |
{ | |
{ Scale.None, 0f }, | |
{ Scale.C4, 261.626f }, | |
{ Scale.D4, 293.665f }, | |
{ Scale.E4, 329.628f }, | |
{ Scale.F, 349.228f }, | |
{ Scale.G4, 391.995f }, | |
{ Scale.A4, 440.000f }, | |
{ Scale.B4, 493.883f }, | |
{ Scale.C5, 523.251f }, | |
}; | |
public static AudioClip Create( | |
Scale s, int samplingRate = 8000, float second = 1f, bool fade = true) | |
{ | |
if (s == Scale.None) | |
{ | |
return CreateAudioClip(Scale.None.ToString(), samplingRate, new float[(int)(samplingRate * second)]); | |
} | |
var scales = s.ToArray(); | |
var dataList = scales | |
.Select(scale => CreateBuffer(samplingRate, scale.Hz(), second)) | |
.ToArray(); | |
var buffer = JoinData(dataList); | |
if (fade) | |
{ | |
buffer = Fade(buffer); | |
} | |
var name = scales.Length == 1 | |
? scales[0].ToString() | |
: scales.Select(scale => scale.ToString()).Aggregate((a, b) => $"{a}{b}"); | |
return CreateAudioClip(name, samplingRate, buffer); | |
} | |
private static AudioClip CreateAudioClip(string name, int samplingRate, float[] data) | |
{ | |
var clip = AudioClip.Create( | |
name, | |
data.Length, | |
1, | |
samplingRate, | |
false | |
); | |
clip.SetData(data, 0); | |
return clip; | |
} | |
private static float[] CreateBuffer(int samplingRate, float hz, float second) | |
{ | |
var ratio = 2f * Mathf.PI * hz / samplingRate; | |
var length = samplingRate * second; | |
var data = new float[(int)length]; | |
for (var count = 0; count < data.Length; count++) | |
{ | |
data[count] = Mathf.Sin(count * ratio); | |
} | |
return data; | |
} | |
private static float[] JoinData(params float[][] data) | |
{ | |
var total = data.Length; | |
if (total == 0) | |
{ | |
return Array.Empty<float>(); | |
} | |
var result = new float[data[0].Length]; | |
for (var count = 0; count < result.Length; count++) | |
{ | |
var sum = 0f; | |
foreach (var floats in data) | |
{ | |
sum += floats[count]; | |
} | |
result[count] = sum / total; | |
} | |
return result; | |
} | |
private static float[] Fade(float[] source) | |
{ | |
var l = (float)source.Length; | |
var ret = new float[source.Length]; | |
for (var count = 0; count < ret.Length; count++) | |
{ | |
var ratio = count / l; | |
ret[count] = source[count] * (1f - ratio); | |
} | |
return ret; | |
} | |
} | |
internal static class ExtensionScale | |
{ | |
internal static float Hz(this MusicalScale.Scale scale) | |
{ | |
return MusicalScale.HzTable[scale]; | |
} | |
internal static MusicalScale.Scale[] ToArray(this MusicalScale.Scale scale) | |
{ | |
var all = (MusicalScale.Scale[])Enum.GetValues(typeof(MusicalScale.Scale)); | |
return all.Where(s => scale.HasFlag(s) && s != MusicalScale.Scale.None).ToArray(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment