From e3616e4ac6b6e5efc068c546b1f2cfb9a76c77fa Mon Sep 17 00:00:00 2001 From: Alessandro Proto Date: Tue, 14 Feb 2023 21:05:15 +0100 Subject: [PATCH] Fix audio that was fucked up since the beginning --- Capy64/Assets/bios.lua | 2 +- Capy64/Core/Audio.cs | 144 +++++++++++------------------- Capy64/Runtime/Libraries/Audio.cs | 14 +-- 3 files changed, 58 insertions(+), 102 deletions(-) diff --git a/Capy64/Assets/bios.lua b/Capy64/Assets/bios.lua index 198e4d9..92ba936 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, "square") +audio.beep(1000, 0.2, 0.2, "square") if shouldInstallOS() then installOS() diff --git a/Capy64/Core/Audio.cs b/Capy64/Core/Audio.cs index 93df52c..8653a04 100644 --- a/Capy64/Core/Audio.cs +++ b/Capy64/Core/Audio.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; @@ -12,6 +14,15 @@ namespace Capy64.Core; public class Audio : IDisposable { + public enum Waveform + { + Sine, + Square, + Triangle, + Sawtooth, + Noise + } + public const int SampleRate = 48000; public const AudioChannels Channels = AudioChannels.Mono; public readonly DynamicSoundEffectInstance Sound; @@ -19,7 +30,6 @@ public class Audio : IDisposable private static readonly Random rng = new(); public Audio() { - GenerateSawtoothWave(440, 1); Sound = new DynamicSoundEffectInstance(SampleRate, Channels); } @@ -29,77 +39,63 @@ public class Audio : IDisposable return Sound.GetSampleDuration(buffer.Length); } - public static byte[] GenerateSineWave(double frequency, double time, double volume = 1d) + public byte[] GenerateWave(Waveform form, double frequency, TimeSpan time) { - var amplitude = 128 * volume; - var timeStep = 1d / SampleRate; + var size = Sound.GetSampleSizeInBytes(time); + var buffer = new byte[size]; - var buffer = new byte[(int)(SampleRate * time)]; - var ctime = 0d; - for (int i = 0; i < buffer.Length; i++) + var step = 1d / SampleRate; + double x = 0; + for (int i = 0; i < size; i += 2) { - double angle = (Math.PI * frequency) * ctime; - double factor = 0.5 * (Math.Sin(angle) + 1.0); - buffer[i] = (byte)(amplitude * factor); - ctime += timeStep; + var value = form switch + { + Waveform.Sine => GetSinePoint(frequency, x), + Waveform.Square => GetSquarePoint(frequency, x), + Waveform.Triangle => GetTrianglePoint(frequency, x), + Waveform.Sawtooth => GetSawtoothPoint(frequency, x), + Waveform.Noise => rng.NextDouble() * 2 - 1, + _ => throw new NotImplementedException(), + }; + Console.WriteLine(value); + value = Math.Clamp(value, -1, 1); + var sample = (short)(value >= 0.0f ? value * short.MaxValue : value * short.MinValue * -1); + if (!BitConverter.IsLittleEndian) + { + buffer[i] = (byte)(sample >> 8); + buffer[i + 1] = (byte)sample; + } + else + { + buffer[i] = (byte)sample; + buffer[i + 1] = (byte)(sample >> 8); + } + x += step; } + return buffer; } + public static double GetSinePoint(double frequency, double x) + { + return Math.Sin(x * 2 * Math.PI * frequency); + } + private static double GetSquarePoint(double frequency, double x) { - double v = 0; - for (int k = 1; k <= 50; k++) - { - v += 1 / (2 * k - 1) * Math.Sin(frequency * 2 * Math.PI * (2 * k - 1) * x) / 2 + 0.5; - } - return v; - } - - public static byte[] GenerateSquareWave(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 v = GetSquarePoint(frequency, ctime); - buffer[i] = (byte)(v * amplitude); - ctime += timeStep; - } - return buffer; + double v = GetSinePoint(frequency, x); + return v > 0 ? 1 : -1; } private static double GetTrianglePoint(double frequency, double x) { double v = 0; - for (int k = 1; k <= 50; k++) + for (int k = 1; k <= 25; k++) { v += (Math.Pow(-1, k) / Math.Pow(2 * k - 1, 2)) - * Math.Sin(2 * Math.PI * (frequency * 2 * k - 1) * x) - / 2 + 0.5; + * Math.Sin(frequency * 2 * Math.PI * (2 * k - 1) * x); } - return -(8 / (Math.PI * Math.PI)) * v; - } - - 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 x = GetTrianglePoint(frequency, ctime); - buffer[i] = (byte)(amplitude * x); - ctime += timeStep; - } - return buffer; + return -(8 / Math.Pow(Math.PI, 2)) * v; } private static double GetSawtoothPoint(double frequency, double x) @@ -107,43 +103,9 @@ public class Audio : IDisposable double v = 0; for (int k = 1; k <= 50; k++) { - v += (Math.Pow(-1, k) / k) * Math.Sin(frequency * 2 * Math.PI * k * x) - / 4 + 0.5; + v += (Math.Pow(-1, k) / k) * Math.Sin(frequency * 2 * Math.PI * k * x); } - return -v; - } - - 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); - var x = GetSawtoothPoint(frequency, ctime); - 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; + return -(2 / Math.PI) * v; } public void Dispose() diff --git a/Capy64/Runtime/Libraries/Audio.cs b/Capy64/Runtime/Libraries/Audio.cs index f19cff0..ce4d371 100644 --- a/Capy64/Runtime/Libraries/Audio.cs +++ b/Capy64/Runtime/Libraries/Audio.cs @@ -3,6 +3,7 @@ using KeraLua; using Microsoft.Xna.Framework.Audio; using System; using System.Threading.Tasks; +using static Capy64.Core.Audio; namespace Capy64.Runtime.Libraries; @@ -144,6 +145,8 @@ public class Audio : IPlugin var volume = L.OptNumber(3, 1); volume = Math.Clamp(volume, 0, 1); + var timespan = TimeSpan.FromSeconds(time); + var form = L.CheckOption(4, "sine", new string[] { "sine", @@ -154,16 +157,7 @@ public class Audio : IPlugin 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() - }; + var buffer = _game.Audio.GenerateWave((Waveform)form, freq, timespan); try {