From e24192fb26a41cdb8c712a12ef2ae41dcfd246d3 Mon Sep 17 00:00:00 2001 From: Alessandro Proto Date: Mon, 13 Feb 2023 22:36:30 +0100 Subject: [PATCH] Add support for other audio waveforms --- Capy64/Assets/bios.lua | 2 +- Capy64/Core/Audio.cs | 54 +++++++++++++++++++++++++++++++ Capy64/Runtime/Libraries/Audio.cs | 26 ++++++++++++--- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/Capy64/Assets/bios.lua b/Capy64/Assets/bios.lua index 14b57e3..198e4d9 100644 --- a/Capy64/Assets/bios.lua +++ b/Capy64/Assets/bios.lua @@ -241,7 +241,7 @@ local function bootScreen() end end -audio.beep(1000, 0.4, 0.2) +audio.beep(1000, 0.4, 0.2, "square") if shouldInstallOS() then installOS() diff --git a/Capy64/Core/Audio.cs b/Capy64/Core/Audio.cs index a097d62..3a2eb19 100644 --- a/Capy64/Core/Audio.cs +++ b/Capy64/Core/Audio.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; @@ -14,8 +15,11 @@ public class Audio : IDisposable public const int SampleRate = 48000; public const AudioChannels Channels = AudioChannels.Mono; public readonly DynamicSoundEffectInstance Sound; + + private static readonly Random rng = new(); public Audio() { + GenerateSawtoothWave(440, 1); Sound = new DynamicSoundEffectInstance(SampleRate, Channels); } @@ -59,6 +63,56 @@ public class Audio : IDisposable return buffer; } + public static byte[] GenerateTriangleWave(double frequency, double time, double volume = 1d) + { + var amplitude = 128 * volume; + var timeStep = 1d / SampleRate; + frequency /= 2; + + var buffer = new byte[(int)(SampleRate * time)]; + var ctime = 0d; + for (int i = 0; i < buffer.Length; i++) + { + var st = ctime * frequency - Math.Floor(ctime * frequency + 0.5); + var x = Math.Abs(st) * 2.0f - 1.0f; + buffer[i] = (byte)(amplitude * x); + ctime += timeStep; + } + return buffer; + } + + public static byte[] GenerateSawtoothWave(double frequency, double time, double volume = 1d) + { + var amplitude = 128 * volume; + var timeStep = 1d / SampleRate; + frequency /= 2; + + var buffer = new byte[(int)(SampleRate * time)]; + var ctime = 0d; + for (int i = 0; i < buffer.Length; i++) + { + var x = ctime * frequency - Math.Floor(ctime * frequency + 0.5); + buffer[i] = (byte)(amplitude * x); + ctime += timeStep; + } + return buffer; + } + + public static byte[] GenerateNoiseWave(double time, double volume = 1d) + { + var amplitude = 128 * volume; + var timeStep = 1d / SampleRate; + + var buffer = new byte[(int)(SampleRate * time)]; + var ctime = 0d; + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = (byte)rng.Next(0, (int)amplitude); + ctime += timeStep; + } + return buffer; + } + public void Dispose() { GC.SuppressFinalize(this); diff --git a/Capy64/Runtime/Libraries/Audio.cs b/Capy64/Runtime/Libraries/Audio.cs index d64d30e..f19cff0 100644 --- a/Capy64/Runtime/Libraries/Audio.cs +++ b/Capy64/Runtime/Libraries/Audio.cs @@ -139,15 +139,31 @@ public class Audio : IPlugin { var L = Lua.FromIntPtr(state); - var freq = L.CheckNumber(1); + var freq = L.OptNumber(1, 440); var time = L.OptNumber(2, 1); var volume = L.OptNumber(3, 1); - - - volume = Math.Clamp(volume, 0, 1); - var buffer = Core.Audio.GenerateSquareWave(freq, time, volume); + var form = L.CheckOption(4, "sine", new string[] + { + "sine", + "square", + "triangle", + "sawtooth", + "noise", + null, + }); + + + var buffer = form switch + { + 0 => Core.Audio.GenerateSineWave(freq, time, volume), + 1 => Core.Audio.GenerateSquareWave(freq, time, volume), + 2 => Core.Audio.GenerateTriangleWave(freq, time, volume), + 3 => Core.Audio.GenerateSawtoothWave(freq, time, volume), + 4 => Core.Audio.GenerateNoiseWave(time, volume), + _ => throw new NotImplementedException() + }; try {