feat(core/audio): add WAV writer and audio track buffer

This commit is contained in:
dualfroz committed 2026-08-24 09:53:41 +02:00
1 parent e18e59beb4
commit d14fcd126d
2 files changed
+120

No files matched your search

+68
View File
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
namespace Chip8.Core.Audio;
public sealed class AudioTrack
{
readonly List<short> samples = new();
readonly int sampleRate;
double squarePhase;
double patternPosition;
public AudioTrack(int sampleRate)
{
if (sampleRate <= 0)
{
throw new ArgumentOutOfRangeException(nameof(sampleRate), "sample rate must be positive");
}
this.sampleRate = sampleRate;
}
public int sampleCount => samples.Count;
public double durationSeconds => (double)samples.Count / sampleRate;
public int samplesForSeconds(double seconds) => (int)Math.Round(seconds * sampleRate);
public void appendSilence(int count)
{
for (int index = 0; index < count; index++)
{
samples.Add(0);
}
}
public void appendSquare(double frequency, int count, double amplitude = 0.4, double duty = 0.5)
{
short high = AudioSynth.amplitudeToPcm(amplitude);
short low = (short)-high;
double step = frequency / sampleRate;
for (int index = 0; index < count; index++)
{
samples.Add((squarePhase % 1.0) < duty ? high : low);
squarePhase += step;
}
}
public void appendPattern(byte[] pattern, double playbackRate, int count, double amplitude = 0.4)
{
bool[] bits = AudioSynth.decodePattern(pattern);
short high = AudioSynth.amplitudeToPcm(amplitude);
short low = (short)-high;
double step = playbackRate / sampleRate;
for (int index = 0; index < count; index++)
{
int bit = ((int)patternPosition) % AudioSynth.patternBits;
samples.Add(bits[bit] ? high : low);
patternPosition += step;
}
}
public short[] toSamples() => samples.ToArray();
public byte[] toWav() => WavFile.write(samples.ToArray(), sampleRate);
}
+52
View File
@@ -0,0 +1,52 @@
using System;
using System.IO;
using System.Text;
namespace Chip8.Core.Audio;
public static class WavFile
{
public const int bitsPerSample = 16;
public const int channels = 1;
public static byte[] write(short[] samples, int sampleRate)
{
if (sampleRate <= 0)
{
throw new ArgumentOutOfRangeException(nameof(sampleRate), "sample rate must be positive");
}
int dataBytes = samples.Length * sizeof(short);
int blockAlign = channels * (bitsPerSample / 8);
int byteRate = sampleRate * blockAlign;
using var stream = new MemoryStream(44 + dataBytes);
using var writer = new BinaryWriter(stream, Encoding.ASCII);
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataBytes);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16);
writer.Write((short)1);
writer.Write((short)channels);
writer.Write(sampleRate);
writer.Write(byteRate);
writer.Write((short)blockAlign);
writer.Write((short)bitsPerSample);
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataBytes);
foreach (short sample in samples)
{
writer.Write(sample);
}
writer.Flush();
return stream.ToArray();
}
public static void writeFile(string path, short[] samples, int sampleRate) =>
File.WriteAllBytes(path, write(samples, sampleRate));
}