Skip to content

Instantly share code, notes, and snippets.

@nekomimi-daimao
Last active November 17, 2021 17:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nekomimi-daimao/9a96fa5415ca2bb51e780dc04293fe34 to your computer and use it in GitHub Desktop.
Save nekomimi-daimao/9a96fa5415ca2bb51e780dc04293fe34 to your computer and use it in GitHub Desktop.
unity, create musical scale AudioClip
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