How can I avoid Raylib (c# bindings) AudioStream distorting Noise channel output on playback

3 weeks ago 20
ARTICLE AD BOX

I am trying to generate and play audio in an emulator using Raylib's AudioStream API and its callback feature. My emulator produces Square, Triangle, Sawtooth and Noise. All waveforms sound correct save for Noise, which sounds like buzzing, horrible static, zipping, and a Geiger counter smashed together - nothing like old-timey noise that was used for percussion, flames, explosions and anything in between. These are the relevant methods from my emulator's audio processing unit class:

private float GenerateSample(int channel) { var baseAddr = Memory.AudioRamStart + (channel * 4); var freq = _mobo.ReadWord(baseAddr); var control = _mobo.ReadByte(baseAddr + 3); if (freq == 0) return 0f; var volume = ProcessEnvelope(channel, control); if (volume <= 0f && _stages[channel] == AdsrStage.Idle) return 0f; if (channel >= 6) { _noisePeriod[channel] = Math.Max(1, (int)(44100f / Math.Max(1, (int)freq))); } var delta = freq / 44100f; _phases[channel] += delta; if (_phases[channel] >= 1f) { _phases[channel] -= 1f; } var wave = channel switch { 0 or 1 => Square(_phases[channel], delta), 2 or 3 => Triangle(_phases[channel]), 4 or 5 => Sawtooth(_phases[channel], delta), _ => Noise(channel), }; return wave * volume; } private float Noise(int channel) { if (--_noiseTimer[channel] <= 0) { // Advance LFSR // NES long mode: bit 0 ^ bit 1 var bit = (ushort)((_noiseLfsr[channel] ^ (_noiseLfsr[channel] >> 1)) & 1); _noiseLfsr[channel] = (ushort)((_noiseLfsr[channel] >> 1) | (bit << 14)); _noiseTimer[channel] = _noisePeriod[channel]; } return _noiseLfsr[channel]; }

Do keep in mind this all broke when I started using the raylib audio callback. I suspect it is because raylib is consuming samples at an off rate, but any help is appreciated.

EDIT: After some research I suspect this may be raylib and how it consumes samples. Here is my raylib-cs audio code:

public class RaylibAudioOutput : IAudioOutput { private AudioStream _stream; private static int SequencerCounter = 0; public unsafe void Initialize(int sampleRate) { Raylib.InitAudioDevice(); Raylib.SetAudioStreamBufferSizeDefault(4096); _stream = Raylib.LoadAudioStream((uint)sampleRate, 32, 1); Raylib.SetAudioStreamCallback(_stream, &AudioCallback); Raylib.PlayAudioStream(_stream); } [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] private static unsafe void AudioCallback(void* buffer, uint frames) { if (Sharpie.Core.Hardware.Apu.Instance == null) // just in case it's still uninitialized return; float* floatBuffer = (float*)buffer; Sharpie.Core.Hardware.Apu.Instance.FillBufferRange(floatBuffer, frames); SequencerCounter += (int)frames; while (SequencerCounter >= 735) { SequencerCounter -= 735; Sequencer.Instance?.Step(); } } // legacy method, unused public void HandleAudioBuffer(float[] audioBuffer) { } public void Cleanup() { Raylib.UnloadAudioStream(_stream); Raylib.CloseAudioDevice(); } }
Read Entire Article