環境
Unity2021.3.4f1
概要
プロシージャルに音階の波形を生成して、かえるのうたを流してみました。
そのままだとノイズがひどいので、AudioMixerを作成してLowpassフィルタを追加して値調整するとノイズが軽減されます。
using UnityEngine; [RequireComponent(typeof(AudioSource))] public class ProcedualAudio : MonoBehaviour { private float _outputSampleRate; private float _bpm = 120; //[BeatsPerMinute] BPM60=1分間に四分音符を60回鳴らす速度 BPM=120の場合は60に比べてテンポが速い private class NoteParam { public enum Note { C4, //ド D4, //レ E4, //ミ F4, //ファ G4, //ソ A4, //ラ B4, //シ } private static float[] _tones = new float[] { 261.626f, 293.665f, 329.628f, 349.228f, 391.995f, 440.000f, 493.883f, }; public Note note; public float tone; public float beat; //1小節の中に含まれる拍数 public NoteParam(Note note_, float beat_) { note = note_; tone = _tones[(int)note]; beat = beat_; } public float GetTime(float bpm) { //BPM=60の場合、四分音符=1秒 return (4.0f / beat) / (bpm / 60.0f); } } private NoteParam[] _notes = new NoteParam[] { new NoteParam(NoteParam.Note.C4, 4.0f), new NoteParam(NoteParam.Note.D4, 4.0f), new NoteParam(NoteParam.Note.E4, 4.0f), new NoteParam(NoteParam.Note.F4, 4.0f), new NoteParam(NoteParam.Note.E4, 4.0f), new NoteParam(NoteParam.Note.D4, 4.0f), new NoteParam(NoteParam.Note.C4, 2.0f), new NoteParam(NoteParam.Note.E4, 4.0f), new NoteParam(NoteParam.Note.F4, 4.0f), new NoteParam(NoteParam.Note.G4, 4.0f), new NoteParam(NoteParam.Note.A4, 4.0f), new NoteParam(NoteParam.Note.G4, 4.0f), new NoteParam(NoteParam.Note.F4, 4.0f), new NoteParam(NoteParam.Note.E4, 2.0f), new NoteParam(NoteParam.Note.C4, 2.0f), new NoteParam(NoteParam.Note.C4, 2.0f), new NoteParam(NoteParam.Note.C4, 2.0f), new NoteParam(NoteParam.Note.C4, 2.0f), new NoteParam(NoteParam.Note.C4, 8.0f), new NoteParam(NoteParam.Note.C4, 8.0f), new NoteParam(NoteParam.Note.D4, 8.0f), new NoteParam(NoteParam.Note.D4, 8.0f), new NoteParam(NoteParam.Note.E4, 8.0f), new NoteParam(NoteParam.Note.E4, 8.0f), new NoteParam(NoteParam.Note.F4, 8.0f), new NoteParam(NoteParam.Note.F4, 8.0f), new NoteParam(NoteParam.Note.E4, 4.0f), new NoteParam(NoteParam.Note.D4, 4.0f), new NoteParam(NoteParam.Note.C4, 2.0f), }; private int _noteIndex = 0; private double _prevDspTime; private void Start() { _outputSampleRate = AudioSettings.outputSampleRate; _noteIndex = 0; _prevDspTime = AudioSettings.dspTime; } private void OnAudioFilterRead(float[] data, int channels) { var note = _notes[_noteIndex]; var noteTime = note.GetTime(_bpm); var dspTime = AudioSettings.dspTime; var elapsedDspTime = dspTime - _prevDspTime; if(elapsedDspTime > noteTime) { _prevDspTime = dspTime; _noteIndex ++; _noteIndex %= _notes.Length; note = _notes[_noteIndex]; noteTime = note.GetTime(_bpm); } Debug.Log($"noteIndex:{_noteIndex} dspTime:{dspTime} noteTime:{noteTime} elapsedDspTime:{elapsedDspTime}"); var begin = (float)(AudioSettings.dspTime % (1.0 / (double)note.tone)); int currentSampleIndex = 0; var volume = Mathf.Lerp(1.0f, 0.5f, (float)elapsedDspTime / noteTime); for (int i = 0; i< data.Length; i++) { float time = begin + (float)currentSampleIndex / _outputSampleRate; data[i] = volume * Mathf.Sin(2.0f * Mathf.PI * time * note.tone); currentSampleIndex++; if (channels == 2) { data[i + 1] = data[i]; i++; } } } }
感想
これをもっとちゃんとやったものがmidi再生ってことになるのでしょうか。