Skip to content

vbacollective/riff

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

70 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Riff logo

Riff

A high-performance, single-file audio engine for Microsoft Office.
Real-time WASAPI playback, Media Foundation decoding, Studio DSP, adaptive buffering, burst-safe playback, musical presets, master bus processing, and VBE-safe cleanup.

CI Release Latest Version Language Platform Architecture WASAPI Media Foundation DSP Dependencies License

Riff is a complete, production-grade audio engine contained within a single .bas module. It allows VBA developers to integrate professional-quality audio playback, synthesis, routing, DSP, effect presets, and final mix processing into Microsoft Office applications without external DLLs, ActiveX controls, installers, or additional references.

Whether you are building interactive dashboards in Excel, immersive presentations in PowerPoint, educational tools in Word, or automation systems in Access, Riff provides a practical real-time audio layer powered by the Windows Audio Session API (WASAPI), Media Foundation, and a native timer-driven DSP loop.

Riff is designed for developers who want game-like audio behavior inside Office: responsive UI sounds, background music, routed buses, persistent scene effects, master bus processing, soft limiting, fades, procedural oscillators, one-shot and looped white/pink/brown noise, burst-safe SFX playback, fast preset setup, and safe cleanup when the host application or the VBA editor resets.

Key Capabilities

  • Zero Dependencies: No DLLs to ship or register. Import Riff.bas and run.
  • Single-File Distribution: The entire engine lives in one VBA module.
  • WASAPI Playback: Shared-mode Windows audio output with low-latency rendering.
  • Media Foundation Decoding: Loads common formats supported by the system, including WAV, MP3, AAC, FLAC, WMA, and more.
  • WAV Fast Path: Compatible WAV files bypass Media Foundation and load through a direct RIFF parser for much faster startup.
  • Unified Playback API: RiffPlay accepts bus, loop, volume, and pan parameters directly.
  • Duplicate Prevention: RiffPlayOnce prevents music or repeated ambience from stacking accidentally.
  • Adaptive Buffering: Dynamically increases render queue safety during host stalls and returns to low latency when stable.
  • Burst Protection: Voice stealing, per-buffer caps, and per-bus caps reduce stutter when many SFX are triggered rapidly.
  • Anti-Accumulation Playback: Repeated short sounds, procedural noise, and oscillator beeps are capped and cleaned so game loops do not silently pile up voices.
  • One-Shot Procedural Noise: RiffPlayNoise is finite by default; use RiffPlayNoiseLoop for continuous ambience.
  • Finite Oscillator Beeps: RiffPlayOscillator accepts durationSec for procedural UI/game SFX that clean themselves automatically.
  • Hz-Based Filtering: RiffVoiceSetFilterHz exposes low-pass and high-pass filters using real frequency values.
  • Lazy DSP Buffer Preparation: Delay, reverb, chorus, and flanger ring buffers are prepared only when they are actually needed, reducing Play and preset setup cost.
  • VBE-Safe Timer Cleanup: Idle and Stop/Reset-safe cleanup reduce the chance of the VBA Editor staying stuck in Running mode or breaking IntelliSense.
  • Studio DSP Pipeline: Independent per-voice effects including Reverb, Delay, Chorus, Flanger, Compressor, EQ, Filters, Distortion, Bitcrusher, Ring Modulation, Tremolo, Auto-Pan, and Stereo Width.
  • Musical Preset Packs: Expanded voice presets for tape, VHS, dream pads, caves, tiny speakers, megaphones, retro game effects, wind, rain, horror drones, cinematic booms, and soft-focus scenes.
  • Persistent Bus Effects: Apply a preset to a whole bus so current and future voices inherit the scene style automatically.
  • Master Bus Processors: Final mix chain with low-pass, high-pass, 3-band EQ, compressor, drive, stereo width, output gain, soft clipping, and master presets.
  • Real-Time Synthesis: BLEP-corrected oscillators for sine, square, and saw waveforms.
  • Noise Generation: White, pink, and brown noise for procedural ambience, wind, rain, static, rumble, and retro effects.
  • Audio Routing: 16 global buses for Music, SFX, UI, Voice, and auxiliary groups.
  • Mixer Controls: Bus volume, mute, solo, fades, peak meters, and master peak monitoring.
  • Smoothing: Volume, pan, and pitch smoothing reduce clicks during parameter changes.
  • Soft Clipping: Master soft clipper helps prevent harsh digital clipping when many voices overlap.
  • Diagnostics: Render counters, underrun counters, clipping counters, buffer status, active voice counts, and adaptive queue information.
  • WAV Export: Export loaded buffers and generated oscillators as standard PCM WAV files.
  • Architecture Aware: Compatible with both 32-bit and 64-bit Office through #If VBA7 / #If Win64 declarations.

Getting Started

Installation

  1. Download the latest Riff.bas.
  2. Open the VBA Editor with Alt + F11.
  3. Choose File > Import File... and select Riff.bas.
  4. No external references are required.
  5. Save your Office document as a macro-enabled file, such as .xlsm, .pptm, .docm, or .accdb.

Minimal Implementation

Initialize the engine, load an asset, and play it.

Public Sub PlaySound()
    If Not RiffOpen() Then
        MsgBox "Riff failed to initialize. Error: " & RiffLastError
        Exit Sub
    End If

    Dim buf As Long
    buf = RiffLoad("C:\Audio\click.wav")

    If buf < 0 Then
        MsgBox "Failed to load audio. Error: " & RiffLastError
        Exit Sub
    End If

    Dim voice As Long
    voice = RiffPlay(buf, RiffBusSfx, False, 0.8, 0)

    If voice < 0 Then
        Debug.Print "No free voice. Error:", RiffLastError
    End If
End Sub

Essential Cleanup

Always close the engine when your document, workbook, or presentation is closing.

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    RiffClose
End Sub

For PowerPoint, call cleanup from your slideshow termination flow:

Public Sub OnSlideShowTerminate(ByVal Pres As Presentation)
    RiffClose
End Sub

Important

Riff uses native callbacks and WASAPI COM interfaces. Always call RiffClose before resetting the VBA project, closing the document, or ending a slideshow.

If you are actively developing in the VBA Editor and intentionally hit the Stop/Reset button during a test, the current stop-safe build is designed to kill orphaned timer callbacks automatically. If an old test build ever leaves the editor in a stuck state, call:

RiffEditorEmergencyStop

This is an editor recovery helper, not normal application shutdown. Production code should still use RiffClose.

Basic Usage

Load Assets Once

Audio loading is synchronous. Load your assets during startup, not during gameplay, animation ticks, or button-spam interactions.

Private sndClick As Long
Private sndExplosion As Long
Private sndMusic As Long

Public Sub AudioLoad()
    If Not RiffOpen() Then Exit Sub

    sndClick = RiffLoad(ActivePresentation.Path & "\audio\click.wav")
    sndExplosion = RiffLoad(ActivePresentation.Path & "\audio\explosion.wav")
    sndMusic = RiffLoad(ActivePresentation.Path & "\audio\music.wav")

    RiffBusVolume(RiffBusUi) = 0.9
    RiffBusVolume(RiffBusSfx) = 0.85
    RiffBusVolume(RiffBusMusic) = 0.45
End Sub

Play UI and SFX

Public Sub PlayClick()
    RiffPlay sndClick, RiffBusUi, False, 0.7, 0
End Sub

Public Sub PlayExplosion()
    Dim pan As Single
    pan = (Rnd() * 2!) - 1!

    RiffPlay sndExplosion, RiffBusSfx, False, 1!, pan
End Sub

Play Music Without Duplicating It

Private musicVoice As Long

Public Sub PlayMusic()
    musicVoice = RiffPlayOnce(sndMusic, RiffBusMusic, True, 0.5, 0)
End Sub

Public Sub StopMusic()
    If RiffVoiceActive(musicVoice) Then
        RiffFadeOut musicVoice, 0.5
    End If
End Sub

Generate a Procedural Sound

Use durationSec for short procedural sounds. This avoids leaving a continuous oscillator running by accident.

Public Sub PlayBeep()
    RiffPlayOscillator RiffWaveSine, 880, RiffBusUi, 0.25, 0, 0.08
End Sub

For continuous synthesis, pass durationSec:=0 or omit it, then stop or fade the returned voice manually.

Public Sub StartLowHum()
    Dim hum As Long
    hum = RiffPlayOscillator(RiffWaveSine, 55, RiffBusSfx, 0.12, 0)
    RiffFadeOut hum, 1.5
End Sub

Generate Noise

RiffPlayNoise is a short one-shot by default. This is safer for game SFX because it prevents accidental infinite procedural voices.

Public Sub PlayDustHit()
    Dim v As Long
    v = RiffPlayNoise(RiffWaveWhiteNoise, RiffBusSfx, 0.08, 0, 0.05)

    If v >= 0 Then
        RiffVoiceSetFilterHz v, 2200, 180
    End If
End Sub

For continuous rain, wind, ambience, or drones, use RiffPlayNoiseLoop.

Public Sub PlayRainLayer()
    Dim v As Long
    v = RiffPlayNoiseLoop(RiffWavePinkNoise, RiffBusMusic, 0.08, 0)

    If v >= 0 Then
        RiffVoiceApplyPreset v, RiffFxRain, 0.65
        RiffVoiceStereoWidth(v) = 1.35
    End If
End Sub

Professional Audio Routing

Riff supports logical summing through 16 independent audio buses. Buses allow you to group related sounds, such as Music, SFX, UI, Voice, or Ambience, and control them as a single unit.

This is more efficient and cleaner than iterating through active voices manually. It also enables mixer-like behavior such as lowering music during dialogue, muting UI sounds, soloing a bus for debugging, fading whole categories, or applying scene-wide effects.

Signal Hierarchy

The final volume of a sound is determined by:

Master Volume × Bus Volume × Voice Volume × Fade/Smoothing × Master Processing
Public Sub SetupGameMixer()
    RiffMasterVolume = 1!

    RiffBusVolume(RiffBusMusic) = 0.45
    RiffBusVolume(RiffBusSfx) = 0.85
    RiffBusVolume(RiffBusUi) = 0.9
    RiffBusVolume(RiffBusVoice) = 1!
End Sub

Bus Fade

Public Sub EnterPauseMenu()
    RiffBusFadeTo RiffBusMusic, 0.2, 500
    RiffBusFadeTo RiffBusSfx, 0.5, 300
End Sub

Public Sub LeavePauseMenu()
    RiffBusFadeTo RiffBusMusic, 0.45, 500
    RiffBusFadeTo RiffBusSfx, 0.85, 300
End Sub

Mute and Solo

Public Sub ToggleMusicMute(ByVal muted As Boolean)
    RiffBusMuted(RiffBusMusic) = muted
End Sub

Public Sub DebugSoloSfx()
    RiffBusSolo(RiffBusSfx) = True
End Sub

Public Sub ClearSolo()
    RiffBusSolo(RiffBusSfx) = False
End Sub

Peak Meter

Public Sub PrintBusPeaks()
    Dim l As Single
    Dim r As Single

    RiffBusGetPeak RiffBusMusic, l, r
    Debug.Print "Music peak:", l, r

    RiffMasterGetPeak l, r
    Debug.Print "Master peak:", l, r
End Sub

Unified Playback API

The recommended public API is intentionally compact:

voice = RiffPlay(bufferHandle, busID, looped, volume, pan)
voice = RiffPlayOnce(bufferHandle, busID, looped, volume, pan)
voice = RiffPlayOscillator(waveType, frequencyHz, busID, volume, pan, durationSec)
voice = RiffPlayNoise(noiseType, busID, volume, pan, durationSec)
voice = RiffPlayNoiseLoop(noiseType, busID, volume, pan)

RiffPlay

Dim v As Long
v = RiffPlay(sndExplosion, RiffBusSfx, False, 0.9, -0.2)

RiffPlayOnce

Use this for music, ambience, menu loops, and anything that should not duplicate.

musicVoice = RiffPlayOnce(sndMusic, RiffBusMusic, True, 0.5, 0)

RiffPlayOscillator

frequencyHz controls pitch. durationSec is optional: 0 means continuous, while a positive value creates a finite procedural one-shot.

' Short retro UI beep.
RiffPlayOscillator RiffWaveSquare, 880, RiffBusUi, 0.2, 0, 0.07

' Continuous oscillator layer.
Dim osc As Long
osc = RiffPlayOscillator(RiffWaveSine, 55, RiffBusSfx, 0.12, 0)
RiffFadeOut osc, 0.4

RiffPlayNoise

RiffPlayNoise is finite by default. Use it for dust hits, static bursts, impacts, wind puffs, and other short procedural SFX.

Dim dust As Long
dust = RiffPlayNoise(RiffWaveWhiteNoise, RiffBusSfx, 0.12, 0, 0.04)
RiffVoiceSetFilterHz dust, 2600, 180

For ambience, use the explicit loop helper:

Dim wind As Long
wind = RiffPlayNoiseLoop(RiffWavePinkNoise, RiffBusMusic, 0.05, 0)
RiffVoiceSetFilterHz wind, 1800, 80

Bus-first helpers are also available when readability matters:

RiffPlayNoiseOnBus RiffBusSfx, RiffWaveWhiteNoise, 0.08, 0, 0.03
RiffPlayNoiseLoopOnBus RiffBusMusic, RiffWavePinkNoise, 0.04, 0

Compatibility Wrappers

Older function names are kept for compatibility:

RiffPlayBus bufferHandle, busID
RiffPlayBusOnce bufferHandle, busID, looped
RiffPlayOscillatorBus waveType, frequencyHz, busID, volume, pan, durationSec
RiffPlayNoiseBus noiseType, busID, volume, pan, durationSec
RiffPlayNoiseOnBus busID, noiseType, volume, pan, durationSec
RiffPlayNoiseLoop noiseType, busID, volume, pan
RiffPlayNoiseLoopOnBus busID, noiseType, volume, pan

They forward internally to the unified playback path.

Effect Presets

Presets provide fast, musical starting points for common sound design situations.

RiffVoiceApplyPreset voice, RiffFxLoFi, 0.55
RiffVoiceApplyPreset voice, RiffFxRadio, 0.6
RiffVoiceApplyPreset voice, RiffFxUnderwater, 0.7
RiffVoiceApplyPreset voice, RiffFxWarmTape, 0.5

Core Presets

Preset Use Case
RiffFxDry Clears effect-heavy coloration and returns toward a clean signal.
RiffFxSmallRoom Subtle room reflections for close spaces.
RiffFxHall Wider ambience for music or narration.
RiffFxCathedral Large washed-out reverb tail.
RiffFxEcho Musical delay/echo effect.
RiffFxLoFi Tape/sampler-style degradation without destroying the sound.
RiffFxRadio Band-limited radio or speaker effect.
RiffFxUnderwater Muffled filtered sound with movement.
RiffFxRobot Ring-modulated robotic coloration.
RiffFxWide Enhanced stereo width.
RiffFxAmbient Spacious ambience for beds and pads.

Musical Preset Packs

Preset Use Case
RiffFxWarmTape Warm tape-like coloration for music, ambience, or narration.
RiffFxVHS Warbly degraded old-media sound.
RiffFxDreamPad Wide, soft chorus/reverb for dream scenes and pads.
RiffFxDarkCave Dark, deep, cave-like space.
RiffFxTinySpeaker Phone, toy speaker, laptop, or small radio tone.
RiffFxMegaphone PA, announcement, or projected voice tone.
RiffFxGameBoy Crunchy retro handheld game color.
RiffFxHorrorDrone Dark modulated unsettling texture.
RiffFxWind Airy filtered wind/noise treatment.
RiffFxRain Soft natural noise ambience.
RiffFxCinematicBoom Big low-heavy impact treatment.
RiffFxSoftFocus Gentle smoothing and width.

Preset Amount

amount usually ranges from 0.0 to 1.0.

RiffVoiceApplyPreset v, RiffFxWarmTape, 0.3  ' light coloration
RiffVoiceApplyPreset v, RiffFxWarmTape, 0.7  ' stronger effect
RiffVoiceApplyPreset v, RiffFxDry, 1.0       ' clear preset-style coloration

The current performance build sanitizes preset values and prepares temporal DSP buffers lazily. Presets that do not actually use delay, reverb, chorus, or flanger no longer pay the cost of clearing large ring buffers during the RiffVoiceApplyPreset call.

Persistent Bus Effects

v1.0.9 can apply voice presets to a whole bus. This is useful for scene-wide states such as underwater, cave, radio, dream, horror, or retro menus.

By default, RiffBusApplyPreset affects currently active voices and stores the preset for future voices routed to that bus.

Public Sub EnterUnderwaterScene()
    RiffBusApplyPreset RiffBusMusic, RiffFxUnderwater, 0.55
    RiffBusApplyPreset RiffBusSfx, RiffFxUnderwater, 0.8
    RiffBusApplyPreset RiffBusVoice, RiffFxUnderwater, 0.45
End Sub

Public Sub LeaveUnderwaterScene()
    RiffBusClearEffects RiffBusMusic
    RiffBusClearEffects RiffBusSfx
    RiffBusClearEffects RiffBusVoice
End Sub

Apply only to future voices:

RiffBusApplyPreset RiffBusVoice, RiffFxRadio, 0.65, True, False

Apply only to currently active voices:

RiffBusApplyPreset RiffBusSfx, RiffFxSmallRoom, 0.4, False, True

Inspect bus preset state:

Debug.Print RiffBusPresetEnabled(RiffBusMusic)
Debug.Print RiffBusPreset(RiffBusMusic)
Debug.Print RiffBusPresetAmount(RiffBusMusic)

Master Bus Processors

Master processors run after the full voice/bus mix. They are intended for final polish, safety limiting, and broad scene coloration.

RiffMasterApplyPreset RiffMasterFxGlue, 0.7
RiffMasterApplyPreset RiffMasterFxCinematic, 0.6

Master Presets

Preset Use Case
RiffMasterFxClean Neutral master stage.
RiffMasterFxGlue Light compression and soft limiting for a cohesive mix.
RiffMasterFxWarm Warmer tone and subtle saturation.
RiffMasterFxBright Brighter overall mix.
RiffMasterFxDark Darker and softer output.
RiffMasterFxRadio Global radio/band-limited sound.
RiffMasterFxCinematic Wider, fuller, slightly compressed cinematic shaping.
RiffMasterFxNight Softer, lower-energy night mix.
RiffMasterFxSoftLimiter Safety limiting for SFX-heavy scenes.

Manual Master Chain

Public Sub ApplyManualMasterChain()
    RiffMasterProcessorEnabled = True

    RiffMasterLowPass = 0.92
    RiffMasterHighPass = 0.02

    RiffMasterEqBass = 1.08
    RiffMasterEqMid = 1
    RiffMasterEqTreble = 0.95

    RiffMasterCompressorThreshold = 0.72
    RiffMasterCompressorRatio = 2.5

    RiffMasterDrive = 1.06
    RiffMasterStereoWidth = 1.1
    RiffMasterOutputGain = 0.96
    RiffSoftClipEnabled = True
End Sub

Clear master processing:

RiffMasterClearProcessors
RiffMasterApplyPreset RiffMasterFxClean

Manual DSP Helpers

You can use the individual properties directly, or use helper functions to set common groups of parameters.

RiffVoiceSetReverb v, 0.35, 0.7
RiffVoiceSetDelay v, 0.28, 0.45, 0.5
RiffVoiceSetChorus v, 0.5, 1.2
RiffVoiceSetFlanger v, 0.6, 0.35, 0.4
RiffVoiceSetFilter v, 0.45, 0.05
RiffVoiceSetFilterHz v, 3000, 300
RiffVoiceClearEffects v

Smooth Voice Changes

Use smoothing helpers to avoid clicks and sudden jumps.

RiffVoiceVolumeTo musicVoice, 0.2, 500
RiffVoicePanTo voice, -0.5, 150
RiffVoicePitchTo voice, 1.2, 100

Frequency-Based Filters

Use RiffVoiceSetFilterHz when you want sound-design values that map to real audio frequencies.

' Telephone/radio band.
RiffVoiceSetFilterHz voice, 3000, 300

' Muffled wall or underwater style.
RiffVoiceSetFilterHz voice, 900, 0

' Remove sub-rumble while keeping the top end open.
RiffVoiceSetFilterHz voice, 0, 80

Noise and Oscillators

Riff supports both tonal oscillators and procedural noise.

Waveform Description Common Uses
RiffWaveSine Pure tone UI beeps, tests, soft synth tones
RiffWaveSquare Hollow retro wave Chiptune, alarms, retro UI
RiffWaveSawtooth Bright harmonic wave Synth leads, sweeps, engine-like sounds
RiffWaveWhiteNoise Equal random energy Static, glitch, impacts, noise bursts
RiffWavePinkNoise Natural balanced noise Rain, wind, fire, ambience
RiffWaveBrownNoise Dark low-frequency noise Rumble, thunder, earthquake, machinery
RiffWaveNoise Compatibility alias Equivalent to white noise
' A4 sine beep for 80 ms.
RiffPlayOscillator RiffWaveSine, 440, RiffBusSfx, 0.2, 0, 0.08

' Short pink-noise hit for 60 ms.
RiffPlayNoise RiffWavePinkNoise, RiffBusSfx, 0.08, 0, 0.06

' Continuous ambience.
Dim rain As Long
rain = RiffPlayNoiseLoop(RiffWavePinkNoise, RiffBusMusic, 0.05, 0)

One-Shot vs Loop Behavior

The current stable build treats procedural noise as a one-shot by default. This is intentionally different from early builds, where noise behaved like an infinite generator unless manually stopped.

Task Recommended Call
Dust burst, static tick, hit layer RiffPlayNoise(..., durationSec)
Rain, wind, fire, ambience bed RiffPlayNoiseLoop(...)
Short beep, coin, UI confirmation RiffPlayOscillator(..., durationSec)
Continuous synth/drone RiffPlayOscillator(..., durationSec:=0)

WAV Fast Path

RiffLoad attempts a direct WAV fast path before falling back to Media Foundation.

When possible, compatible WAV files are parsed directly by Riff:

WAV file -> RIFF parser -> VirtualAlloc buffer -> Riff buffer pool

This avoids Media Foundation startup, source reader negotiation, COM loops, and format conversion overhead for simple WAV assets.

Recommended Runtime Format

For fastest loading, use WAV files close to the active output mix:

PCM16 stereo 48 kHz
PCM16 stereo 44.1 kHz
Float32 stereo 48 kHz

Small UI sounds and SFX should generally be shipped as WAV if load speed matters.

Dim click As Long
click = RiffLoad(ActivePresentation.Path & "\audio\click.wav")

If the WAV format is unsupported by the fast path, Riff automatically falls back to the Media Foundation decode path.

Adaptive Buffering

Office hosts can occasionally stall when switching tabs, opening menus, recalculating, rendering slides, or interacting with the VBA editor. Because Riff is implemented in pure VBA with a native callback bridge, those stalls can affect how often the render loop gets serviced.

Riff includes adaptive buffering to reduce audible dropouts:

Normal state:
    target queue stays low for responsive playback

Stall or underrun risk:
    target queue rises automatically for safety

Stable state:
    target queue gradually returns to low latency

The performance build can keep the endpoint warm with silence during rapid SFX bursts so the WASAPI buffer does not drain to zero between short sounds. The editor-safe build also auto-suspends the timer after idle time so the VBA Editor does not remain stuck in Running mode.

Diagnostics

Debug.Print "Adaptive queue:", RiffAdaptiveQueueMs
Debug.Print "Underruns:", RiffUnderrunCount
Debug.Print "Last padding:", RiffLastPaddingFrames
Debug.Print "Frames available:", RiffLastFramesAvailable
Debug.Print "Frames written:", RiffLastFramesWritten

Reset counters before a test:

RiffResetAdaptiveStats
RiffResetDiagnostics

Burst Safety

Triggering the same sound many times in a short time can overwhelm any small mixer. Riff includes safety controls to prevent rapid SFX spam from creating 32 overlapping copies of the same sound.

RiffVoiceStealingEnabled = True
RiffMaxVoicesPerBuffer = 4
RiffMaxVoicesPerBus = 18

Diagnostics

Debug.Print "Active voices:", RiffActiveVoiceCount()
Debug.Print "Click instances:", RiffBufferVoiceCount(sndClick, RiffBusUi)
Debug.Print "SFX bus voices:", RiffBusVoiceCount(RiffBusSfx)

Recommended values for UI-heavy Office projects:

RiffMaxVoicesPerBuffer = 4
RiffMaxVoicesPerBus = 18

Performance and Stability Notes

The current performance pass focuses on making the common gameplay path cheap while keeping the earlier anti-accumulation fixes intact.

Key internal improvements include:

  • RiffPlay no longer clears large temporal ring buffers for dry one-shot voices.
  • RiffVoiceApplyPreset no longer eagerly clears delay/reverb/chorus/flanger buffers unless those stages are actually needed.
  • Temporal DSP buffer preparation is lazy and tied to the first render tick that needs it.
  • Voice allocation combines free-slot search, per-buffer caps, per-bus caps, and voice-steal candidates in a cheaper path.
  • Generated noise and oscillator one-shots use finite lifetimes and short release ramps.
  • Idle warm-buffer behavior reduces underruns during rapid SFX bursts.
  • Stop/Reset-safe editor cleanup kills stale timer callbacks after a VBE reset.

Observed benchmark range from the stabilization/performance pass:

Dry RiffPlay:          ~11–13 µs/call
Noise one-shot:        ~13–15 µs/call
Oscillator one-shot:   ~13–15 µs/call
Preset/DSP setup:      ~20 µs/call
Game-loop underruns:   0 in the final pumped benchmark
Failed handles:        0 in the final benchmark

These numbers are not guaranteed across machines, Office versions, or host load, but they describe the target behavior: fast one-shot playback, no voice accumulation, stable memory, and low underrun counts during normal Office-hosted game loops.

VBA Editor Safety

Riff uses a native timer callback to drive the render loop. That gives Office projects real-time audio behavior, but it also means the VBA Editor must not be left with an orphaned callback after a reset.

The current stop-safe build includes additional cleanup for both idle and editor reset cases:

  • when idle, the timer can auto-suspend to release the VBE;
  • when the VBE Stop/Reset button is clicked, stale timer callbacks attempt to kill themselves;
  • RiffEditorEmergencyStop is available as a manual recovery helper for development sessions.

Recommended development workflow:

Public Sub DevAudioReset()
    RiffEditorEmergencyStop
    RiffClose
End Sub

Use this only while developing. Normal applications should call RiffClose from their shutdown path.

Feature Summary

Category Features
Core Single .bas, 32 voices, 64 buffers, 16 buses, x86/x64 support
I/O Media Foundation decoding, in-memory loading, WAV fast path, WAV export
Playback Unified RiffPlay, RiffPlayOnce, finite oscillator/noise one-shots, seeking, looping, fades, stop/reset behavior
Routing Bus volume, mute, solo, fade, peak meters, persistent bus presets, master volume
Stability Adaptive buffering, burst protection, voice stealing, idle timer cleanup, Stop/Reset-safe editor cleanup
Presets Core FX presets, musical preset packs, persistent bus effects, master presets
Master Processing Soft clip, low-pass, high-pass, 3-band EQ, compressor, drive, stereo width, output gain
Synthesis BLEP sine/square/saw, finite oscillator beeps, one-shot and looped white/pink/brown noise
Dynamics Compressor, soft clipping, distortion, bitcrusher
Spatial Reverb, delay, stereo width, pan, auto-pan
Modulation Chorus, flanger, tremolo, ring modulation
Filters Biquad low-pass, high-pass, Hz-based filter helper, 3-band EQ
Diagnostics Underruns, render errors, clipped samples, buffer state, active voice, bus voice, and buffer voice counters
Export Loaded buffer export and oscillator render to PCM WAV

v1.0.9 Highlights

  • Added expanded musical voice preset packs.
  • Added persistent bus presets for current and future voices.
  • Added master bus processors.
  • Added master presets for glue, warm, bright, dark, radio, cinematic, night, and soft-limiter mixes.
  • Added manual master controls for filters, EQ, compression, drive, stereo width, output gain, and soft clipping.
  • Improved scene-level workflows for underwater, cave, dream, horror, retro, radio, and cinematic states.
  • Added gameplay-stability refinements for repeated SFX, procedural one-shots, and voice reuse.
  • Added finite durationSec support for generated oscillator and noise playback.
  • Added explicit RiffPlayNoiseLoop helpers for continuous procedural ambience.
  • Added RiffVoiceSetFilterHz for frequency-based filter setup.
  • Added editor-safe timer cleanup and RiffEditorEmergencyStop for development recovery.
  • Improved RiffPlay and preset setup performance through lazy temporal-buffer preparation.
  • Preserved the unified playback API and compatibility wrappers from v1.0.8.
  • Preserved adaptive buffering, burst safety, noise generation, WAV fast path, and diagnostics.

Documentation

  • API Reference – Detailed guide to every function, property, enum, and practical pattern.
  • Architecture – Deep dive into WASAPI, Media Foundation, thunks, callback flow, buffers, and DSP.
  • Effect Cookbook – Ready-to-use recipes for voice presets, bus scenes, master processing, ambience, retro effects, and more.
  • Troubleshooting – Fixes for initialization, device, stutter, cleanup, and host-specific issues.
  • Examples – Practical demos and integration patterns.
  • Benchmarks – Optional local stress tests for burst playback, game-loop playback, memory, underruns, and VBE timer behavior.

Roadmap

Current Version (v1.0.9)

  • WASAPI Shared Mode playback.
  • Media Foundation decoding.
  • WAV fast path loader.
  • Unified playback API.
  • 32-voice polyphonic mixer.
  • 16-bus routing system.
  • Bus mute, solo, fade, and peak meters.
  • Persistent bus effect presets.
  • Full per-voice Studio DSP pipeline.
  • Core effect presets.
  • Musical preset packs.
  • Master bus processors.
  • Master processor presets.
  • White, pink, and brown noise.
  • One-shot and looped procedural noise helpers.
  • BLEP oscillators.
  • Finite oscillator duration support.
  • Hz-based voice filter helper.
  • Adaptive buffering.
  • Burst-safe voice management.
  • Lazy temporal-buffer preparation for faster Play and preset setup.
  • VBE-safe idle and Stop/Reset timer cleanup.
  • x86/x64 native thunk driver.
  • Offline WAV export.
  • Diagnostics and render counters.

Planned

  • Optional native decode backend for faster MP3/OGG loading.
  • Background preload helpers.
  • Higher-level asset registry, such as RiffLoadAs and RiffPlayKey.
  • More musical preset packs and scene templates.
  • Additional master bus processors and metering tools.
  • macOS support through CoreAudio/AudioToolbox if the project expands beyond Windows.

Performance Notes

For best performance in Office:

  • Prefer WAV for short SFX and UI sounds.
  • Preload assets at startup with RiffLoad.
  • Avoid decoding MP3/OGG during interaction-heavy moments.
  • Use RiffPlayOnce for music and ambience loaded from buffers.
  • Use RiffPlayNoiseLoop for continuous procedural ambience instead of looping a default RiffPlayNoise one-shot.
  • Use durationSec for short oscillator beeps and procedural noise hits.
  • Use bus volume/fades instead of changing many voices one by one.
  • Use persistent bus presets for scene-wide effects.
  • Use master processors lightly for final polish rather than heavy per-sample coloration on every voice.
  • Keep effect-heavy processing for important voices only.
  • Use RiffMaxVoicesPerBuffer to prevent repeated button clicks from stacking too many copies.
  • Use RiffVoiceSetFilterHz when frequency-based filtering is clearer than normalized filter values.
  • Keep RiffAutoSuspendTimer enabled for editor stability unless you intentionally need a warm timer during heavy gameplay bursts.
  • Use RiffEditorEmergencyStop only as a development recovery helper if an old session leaves the VBE stuck.
  • Always call RiffClose on exit.

Note

Riff is a pure VBA engine with a native callback bridge. It is highly capable for Office, but it is still hosted inside Excel, PowerPoint, Word, or Access. If the host application or the entire system stalls hard enough, audio scheduling can be affected. Adaptive buffering reduces this, but a fully independent audio thread would require a native backend.

License

MIT. Designed for freedom, integration, and serious audio experimentation inside Microsoft Office.

About

A complete audio engine for VBA. Load and play audio files, synthesize waveforms and apply real-time DSP effects like reverb, chorus, delay and compressor, with zero dependencies.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages