A high-performance, single-file audio engine for Microsoft Office.
Real-time WASAPI playback, Media Foundation decoding, Studio DSP, asset-key playback, cooperative preload, scene templates, adaptive buffering, dynamic SafeArray pools, automatic overload protection, burst-safe playback, musical presets, limiter-safe master processing, editor-safe development guards, and VBE-safe cleanup.
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, named asset playback, cooperative loading screens, one-call scene templates, persistent scene effects, master bus processing, limiter/headroom safety, fades, procedural oscillators, one-shot and looped white/pink/brown noise, dynamic buffer/voice capacity, automatic burst coalescing, fast preset setup, and safer cleanup when the host application or the VBA editor resets.
- Zero Dependencies: No DLLs to ship or register. Import
Riff.basand 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:
RiffPlayaccepts bus, loop, volume, and pan parameters directly. - Asset Registry:
RiffLoadAs,RiffPlayKey,RiffPlayKeyOnce,RiffPlayKeyLoop,RiffStopKey, andRiffFadeOutKeylet projects play audio by stable string keys instead of manually passing handles everywhere. - Cooperative Preload:
RiffPreloadAdd,RiffPreloadStart, andRiffPreloadUpdatesupport loading-screen style asset preparation without forcing a giant one-frame load spike. - Duplicate Prevention:
RiffPlayOnceandRiffPlayKeyOnceprevent music or repeated ambience from stacking accidentally. - Scene Templates:
RiffApplySceneprovides ready-made mixer states for battle, night, cave, underwater, radio call, dream, horror, retro, cinematic, pause menu, and normal playback. - Dynamic SafeArray Pools: Buffers, voices, and bus state start with practical defaults and grow automatically instead of being locked to a hardcoded ceiling.
- Capacity Reservation:
RiffReserveBuffers,RiffReserveVoices, andRiffReserveBuseslet large projects preallocate room before loading or playing many sounds. - Adaptive Buffering: Dynamically increases render queue safety during host stalls and returns to low latency when stable.
- Automatic Overload Protection: The engine detects extreme play-call spam internally and coalesces/reuses voices without requiring the user to call manual begin/end burst functions.
- Burst Protection: Voice stealing, active-voice budgets, per-buffer caps, per-bus caps, and loop coalescing reduce stutter when many SFX are triggered rapidly.
- Stress-Safe Defaults:
RiffApplyStressSafeDefaultsapplies practical voice budgets, overload protection, limiter/headroom safety, and clipping protection for SFX-heavy projects. - Anti-Accumulation Playback: Repeated short sounds, procedural noise, and oscillator beeps are capped, stolen, coalesced, or cleaned so game loops do not silently pile up voices.
- One-Shot Procedural Noise:
RiffPlayNoiseis finite by default; useRiffPlayNoiseLoopfor continuous ambience. - Finite Oscillator Beeps:
RiffPlayOscillatoracceptsdurationSecfor procedural UI/game SFX that clean themselves automatically. - Hz-Based Filtering:
RiffVoiceSetFilterHzexposes 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
Playand preset setup cost. - VBE-Safe Timer Cleanup: Idle and Stop/Reset-safe cleanup reduce the chance of the VBA Editor staying stuck in
Runningmode or breaking IntelliSense. - Editor-Safe Development Guards:
RiffPrepareForVbeEditandRiffResumeAfterVbeEditprovide a controlled way to pause the risky timer/callback path before editing VBA code while audio is active. - 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, cassette, VHS, old computer, arcade cabinets, dream menus, caves, dungeons, boss rooms, space stations, low health, memory flashbacks, cutscenes, combat impacts, 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, limiter controls, balance, tilt EQ, master presets, and master meters.
- 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: Named buses for Music, SFX, UI, Voice, and auxiliary groups, backed by dynamically sized bus state.
- Mixer Controls: Bus volume, mute, solo, fades, peak meters, RMS meters, clip counters, and master peak/RMS monitoring.
- Smoothing: Volume, pan, and pitch smoothing reduce clicks during parameter changes.
- Soft Clipping and Limiting: Master soft clipper and limiter help prevent harsh digital clipping when many voices overlap.
- Diagnostics: Render counters, underrun counters, overload counters, clipping counters, buffer status, active voice counts, RMS/peak meters, 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 Win64declarations.
- Download the latest
Riff.bas. - Open the VBA Editor with
Alt + F11. - Choose File > Import File... and select
Riff.bas. - No external references are required.
- Save your Office document as a macro-enabled file, such as
.xlsm,.pptm,.docm, or.accdb.
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 "Playback failed. Error:", RiffLastError
End If
End SubFor bigger projects, prefer the v1.1.3 asset registry. It lets you load sounds with names and play them later without keeping a global variable for every buffer handle.
Public Sub AudioLoadKeys()
If Not RiffOpen() Then Exit Sub
RiffLoadAs "ui.click", ActivePresentation.Path & "\audio\click.wav"
RiffLoadAs "sfx.hit", ActivePresentation.Path & "\audio\hit.wav"
RiffLoadAs "music.menu", ActivePresentation.Path & "\audio\menu.mp3"
End Sub
Public Sub PlayMenuClick()
RiffPlayKey "ui.click", RiffBusUi, False, 0.75!, 0!
End Sub
Public Sub PlayMenuMusic()
RiffPlayKeyOnce "music.menu", RiffBusMusic, True, 0.45!, 0!
End SubThis is especially useful in PowerPoint games and large Excel tools where asset names are easier to manage than dozens of Long variables.
For SFX-heavy projects, v1.1.3 provides one setup call that enables practical overload protection, limiter/headroom safety, and voice budgets.
Public Sub AudioInitForHeavyGameplay()
If Not RiffOpen() Then Exit Sub
RiffReserveBuffers 500
RiffReserveVoices 160
RiffReserveBuses 32
' max active voices, max voices per buffer, max voices per bus
RiffApplyStressSafeDefaults 64, 6, 32
End SubYou still call RiffPlay, RiffPlayKey, RiffPlayNoise, and RiffPlayOscillator normally. The overload/coalescing behavior is internal.
Riff starts with practical default capacity for compatibility, but the internal buffer, voice, and bus pools are no longer fixed to the old hardcoded limits. When more room is needed, the engine can grow its internal SafeArray pools automatically.
For small projects, you do not need to do anything special:
click = RiffLoad("C:\Audio\click.wav")
voice = RiffPlay(click, RiffBusUi)For larger games, presentations, or tools with many assets, you can reserve capacity during startup. This avoids repeated pool growth while the project is already busy loading or playing sounds.
Public Sub AudioReserveForLargeProject()
If Not RiffOpen() Then Exit Sub
RiffReserveBuffers 500
RiffReserveVoices 128
RiffReserveBuses 32
End SubThe diagnostic capacity properties now report the current pool size instead of a permanent hard limit:
Debug.Print "Buffer capacity:", RiffMaxBuffers
Debug.Print "Voice capacity:", RiffMaxVoices
Debug.Print "Bus capacity:", RiffMaxBusesAlways close the engine when your document, workbook, or presentation is closing.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
RiffClose
End SubFor PowerPoint, call cleanup from your slideshow termination flow:
Public Sub OnSlideShowTerminate(ByVal Pres As Presentation)
RiffClose
End SubImportant
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:
RiffEditorEmergencyStopThis is an editor recovery helper, not normal application shutdown. Production code should still use RiffClose.
When you need to edit VBA code while Riff audio is active, use the manual editor-safe workflow. This pauses the dangerous timer/callback path before the VBE recompiles or rearranges project memory, then resumes the engine after editing.
Public Sub EnterSafeEditMode()
RiffPrepareForVbeEdit
End Sub
Public Sub LeaveSafeEditMode()
RiffResumeAfterVbeEdit
End SubRiffEditorSafeMode also exists for automatic experiments, but the recommended development workflow is still manual because automatic editor detection can pause audio when you are simply testing from the VBE.
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
' Optional for larger projects: reserve before loading many assets.
RiffReserveBuffers 256
RiffReserveVoices 96
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 SubThe asset registry keeps the code cleaner when a project has many sounds.
Public Sub AudioLoadRegistry()
If Not RiffOpen() Then Exit Sub
RiffLoadAs "ui.click", ActivePresentation.Path & "\audio\ui_click.wav"
RiffLoadAs "ui.cancel", ActivePresentation.Path & "\audio\ui_cancel.wav"
RiffLoadAs "sfx.explosion", ActivePresentation.Path & "\audio\explosion.wav"
RiffLoadAs "music.menu", ActivePresentation.Path & "\audio\music.wav"
End Sub
Public Sub PlayRegistryClick()
RiffPlayKey "ui.click", RiffBusUi, False, 0.7!, 0!
End Sub
Public Sub EnsureRegistryMusic()
RiffPlayKeyOnce "music.menu", RiffBusMusic, True, 0.5!, 0!
End SubUseful helpers:
Debug.Print RiffAssetExists("ui.click")
Debug.Print RiffAssetHandle("ui.click")
Debug.Print RiffAssetPath("ui.click")
RiffStopKey "music.menu"
RiffFadeOutKey "music.menu", 0.75For a loading screen, queue assets and advance loading over multiple calls.
Public Sub BuildLoadingQueue()
RiffPreloadClear
RiffPreloadAdd "ui.click", ActivePresentation.Path & "\audio\ui_click.wav"
RiffPreloadAdd "sfx.explosion", ActivePresentation.Path & "\audio\explosion.wav"
RiffPreloadAdd "music.menu", ActivePresentation.Path & "\audio\music.wav"
RiffPreloadStart
End Sub
Public Sub LoadingTick()
RiffPreloadUpdate
Debug.Print "Loading:", Format$(RiffPreloadProgress * 100!, "0") & "%"
If RiffPreloadFinished Then
Debug.Print "Loaded:", RiffPreloadLoadedCount
Debug.Print "Failed:", RiffPreloadFailedCount
End If
End SubUse RiffPreloadUpdateAll only when you intentionally want to finish the whole queue immediately.
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 SubPrivate 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 SubUse 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 SubFor 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 SubRiffPlayNoise 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 SubFor 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 SubRiff supports logical summing through named audio buses backed by dynamically sized bus state. 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.
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 SubPublic 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 SubPublic 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 SubPublic 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 Subv1.1.3 includes built-in scene templates. These are higher-level mixer recipes that adjust buses, persistent effects, and master color in one call.
Public Sub EnterBattle()
RiffApplyScene RiffSceneBattle
End Sub
Public Sub EnterCave()
RiffApplyScene RiffSceneCave
End Sub
Public Sub EnterRadioCall()
RiffApplyScene RiffSceneRadioCall
End Sub
Public Sub LeaveSpecialScene()
RiffApplyScene RiffSceneNormal
End SubAvailable scenes:
RiffSceneNormal
RiffScenePauseMenu
RiffSceneBattle
RiffSceneNight
RiffSceneCave
RiffSceneUnderwater
RiffSceneRadioCall
RiffSceneDream
RiffSceneHorror
RiffSceneRetro
RiffSceneCinematicScenes work best when audio is routed to meaningful buses:
RiffPlayKey "music.battle", RiffBusMusic, True, 0.45!, 0!
RiffPlayKey "sfx.hit", RiffBusSfx, False, 0.85!, 0!
RiffPlayKey "ui.confirm", RiffBusUi, False, 0.65!, 0!
RiffPlayKey "voice.npc", RiffBusVoice, False, 1!, 0!If everything is played on RiffBusMain, scene templates have less useful routing information to work with.
The recommended public API is intentionally compact. Handle-based playback remains available, and v1.1.3 adds key-based playback for larger projects.
voice = RiffPlay(bufferHandle, busID, looped, volume, pan)
voice = RiffPlayOnce(bufferHandle, busID, looped, volume, pan)
voice = RiffPlayKey("sfx.hit", busID, looped, volume, pan)
voice = RiffPlayKeyOnce("music.menu", busID, looped, volume, pan)
voice = RiffPlayKeyLoop("amb.wind", busID, volume, pan)
voice = RiffPlayOscillator(waveType, frequencyHz, busID, volume, pan, durationSec)
voice = RiffPlayNoise(noiseType, busID, volume, pan, durationSec)
voice = RiffPlayNoiseLoop(noiseType, busID, volume, pan)Dim v As Long
v = RiffPlay(sndExplosion, RiffBusSfx, False, 0.9, -0.2)Use this for music, ambience, menu loops, and anything that should not duplicate.
musicVoice = RiffPlayOnce(sndMusic, RiffBusMusic, True, 0.5, 0)Use this when the sound was loaded with RiffLoadAs or registered through the asset registry.
RiffLoadAs "sfx.explosion", ActivePresentation.Path & "\audio\explosion.wav"
RiffPlayKey "sfx.explosion", RiffBusSfx, False, 0.9!, 0!Use these for music and ambience that should not stack.
RiffLoadAs "music.menu", ActivePresentation.Path & "\audio\menu.mp3"
RiffPlayKeyOnce "music.menu", RiffBusMusic, True, 0.45!, 0!
RiffLoadAs "amb.wind", ActivePresentation.Path & "\audio\wind.wav"
RiffPlayKeyLoop "amb.wind", RiffBusMusic, 0.25!, 0!RiffFadeOutKey "music.menu", 0.8
RiffStopKey "amb.wind"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.4RiffPlayNoise 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, 180For ambience, use the explicit loop helper:
Dim wind As Long
wind = RiffPlayNoiseLoop(RiffWavePinkNoise, RiffBusMusic, 0.05, 0)
RiffVoiceSetFilterHz wind, 1800, 80Bus-first helpers are also available when readability matters:
RiffPlayNoiseOnBus RiffBusSfx, RiffWaveWhiteNoise, 0.08, 0, 0.03
RiffPlayNoiseLoopOnBus RiffBusMusic, RiffWavePinkNoise, 0.04, 0Older 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, panThey forward internally to the unified playback path.
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| 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. |
| 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 | Use Case |
|---|---|
RiffFxCassette |
Cassette-style warmth, dullness, and soft degradation. |
RiffFxOldComputer |
Crunchy old PC / multimedia speaker character. |
RiffFxArcadeCabinet |
Bright, narrow, arcade-machine style UI and SFX tone. |
RiffFxDreamMenu |
Soft menu ambience with a dreamlike polished tone. |
RiffFxSpaceStation |
Cold sci-fi room tone for machinery, ambience, and UI. |
RiffFxDungeon |
Darker reverb and low-mid weight for RPG rooms. |
RiffFxBossRoom |
Stronger, heavier scene color for combat areas. |
RiffFxLowHealth |
Tense filtered state for low HP, warning, or danger moments. |
RiffFxMemoryFlashback |
Soft degraded recollection / flashback coloration. |
RiffFxCutscene |
Polished cinematic treatment for narrative moments. |
RiffFxCombatImpact |
Punchier transient-heavy effect for hits and impacts. |
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 colorationThe 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.
Riff 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 SubApply only to future voices:
RiffBusApplyPreset RiffBusVoice, RiffFxRadio, 0.65, True, FalseApply only to currently active voices:
RiffBusApplyPreset RiffBusSfx, RiffFxSmallRoom, 0.4, False, TrueInspect bus preset state:
Debug.Print RiffBusPresetEnabled(RiffBusMusic)
Debug.Print RiffBusPreset(RiffBusMusic)
Debug.Print RiffBusPresetAmount(RiffBusMusic)Master processors run after the full voice/bus mix. They are intended for final polish, safety limiting, broad scene coloration, diagnostics, and loud SFX protection.
RiffMasterApplyPreset RiffMasterFxGlue, 0.7
RiffMasterApplyPreset RiffMasterFxCinematic, 0.6| 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. |
For scenes where many one-shots can overlap, enable the master processor, limiter, output headroom, and soft clipper.
Public Sub ApplySafeMasterForGameplay()
RiffMasterProcessorEnabled = True
RiffMasterSetLimiter 0.92!, 0.9!
RiffMasterOutputGain = 0.78!
RiffSoftClipEnabled = True
End SubRiffApplyStressSafeDefaults applies this kind of setup automatically with practical voice budgets.
RiffApplyStressSafeDefaults 64, 6, 32Public 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' Negative values lean darker; positive values lean brighter.
RiffMasterTiltEq -0.15!
' -1 = left, 0 = center, 1 = right.
RiffMasterBalance 0!Public Sub PrintMasterMeters()
Dim l As Single
Dim r As Single
RiffMasterGetRms l, r
Debug.Print "Master RMS:", l, r
Debug.Print "Master RMS dB:", RiffMasterRmsDb
Debug.Print "Master Peak dB:", RiffMasterPeakDb
Debug.Print "Master Clips:", RiffMasterClipCount
End SubBus and voice-level RMS helpers are also available for visualizers, debug overlays, and balancing tools.
Dim l As Single
Dim r As Single
RiffBusGetRms RiffBusMusic, l, r
RiffVoiceGetRms musicVoice, l, rClear master processing:
RiffMasterClearProcessors
RiffMasterApplyPreset RiffMasterFxCleanYou 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 vUse smoothing helpers to avoid clicks and sudden jumps.
RiffVoiceVolumeTo musicVoice, 0.2, 500
RiffVoicePanTo voice, -0.5, 150
RiffVoicePitchTo voice, 1.2, 100Use 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, 80Riff 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)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) |
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.
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.
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.
Debug.Print "Adaptive queue:", RiffAdaptiveQueueMs
Debug.Print "Underruns:", RiffUnderrunCount
Debug.Print "Last padding:", RiffLastPaddingFrames
Debug.Print "Frames available:", RiffLastFramesAvailable
Debug.Print "Frames written:", RiffLastFramesWrittenReset counters before a test:
RiffResetAdaptiveStats
RiffResetDiagnosticsTriggering the same sound many times in a short time can overwhelm any mixer, even with dynamic voice capacity. Riff v1.1.3 protects this path internally.
The important part: the user does not need to call manual Begin / End burst functions. You keep using the normal API.
RiffPlay sndHit, RiffBusSfx, False, 0.8!, 0!
RiffPlayKey "sfx.hit", RiffBusSfx, False, 0.8!, 0!
RiffPlayNoise RiffWaveWhiteNoise, RiffBusSfx, 0.08!, 0!, 0.04!
RiffPlayOscillator RiffWaveSquare, 880!, RiffBusUi, 0.15!, 0!, 0.05!Internally, the overload path can:
- coalesce repeated play requests;
- reuse compatible voices during extreme spam;
- steal old non-looping voices before touching important looped audio;
- respect active-voice budgets;
- respect per-buffer and per-bus budgets;
- keep limiter/headroom safety active when stress defaults are enabled.
Recommended heavy-game setup:
RiffVoiceStealingEnabled = True
RiffOverloadProtectionEnabled = True
RiffLoopCoalescingEnabled = True
RiffMaxActiveVoices = 64
RiffMaxVoicesPerBuffer = 6
RiffMaxVoicesPerBus = 32
RiffMasterProcessorEnabled = True
RiffMasterSetLimiter 0.92!, 0.9!
RiffMasterOutputGain = 0.78!
RiffSoftClipEnabled = TrueOr use the helper:
RiffApplyStressSafeDefaults 64, 6, 32Debug.Print "Active voices:", RiffActiveVoiceCount()
Debug.Print "Click instances:", RiffBufferVoiceCount(sndClick, RiffBusUi)
Debug.Print "SFX bus voices:", RiffBusVoiceCount(RiffBusSfx)
Debug.Print "Stolen:", RiffOverloadStolenCount
Debug.Print "Dropped:", RiffOverloadDroppedCount
Debug.Print "Coalesced:", RiffOverloadCoalescedCount
Debug.Print "Clips:", RiffMasterClipCountReset counters before stress tests:
RiffResetOverloadStats
RiffResetDiagnostics
RiffResetAdaptiveStatsDynamic voice capacity gives the engine more room, while overload protection keeps gameplay mistakes from turning into hundreds or thousands of redundant real voices.
The v1.1.3 performance pass focuses on making the common gameplay path extremely cheap while keeping the earlier anti-accumulation and editor-safety fixes intact.
Key internal improvements include:
RiffPlayno longer clears large temporal ring buffers for dry one-shot voices.RiffVoiceApplyPresetno 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, dynamic pool growth, per-buffer caps, per-bus caps, active voice budgets, and voice-steal candidates in a cheaper path.
- Buffers, voices, and bus state use dynamic
SafeArraypools instead of being locked to the old fixed capacities. - 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.
- Automatic overload detection coalesces or reuses repeated play requests during extreme same-tick spam.
- Stress-safe master settings keep limiter/headroom protection active during loud SFX-heavy bursts.
Observed benchmark from the v1.1.3 local performance test:
Short dry RiffPlay burst: 5000 calls, ~22.921 ms total, ~4.584 us/call
Long dry RiffPlay burst: 5000 calls, ~22.234 ms total, ~4.447 us/call
Noise one-shot burst: 5000 calls, ~24.315 ms total, ~4.863 us/call
Oscillator one-shot burst: 5000 calls, ~22.946 ms total, ~4.589 us/call
Preset/DSP setup burst: 2500 calls, ~32.318 ms total, ~12.927 us/call
Gameplay loop: 3600 frames, 1827 play calls, 0 failed handles
Missed frame budget: 0
Peak active voices: 7
Final active voices: 0
Gameplay underrun delta: 0
Gameplay memory peak-start: 0.000 MB
These numbers are not guaranteed across machines, Office versions, or host load, but they describe the current target behavior: very fast one-shot playback, no voice accumulation, stable memory, low underrun counts, and safe handling of extreme SFX spam.
When a benchmark reports thousands of successful handles during a burst, it means the calls were accepted successfully. It does not necessarily mean Riff created thousands of unique real voices. Under overload, v1.1.3 may coalesce or reuse voices internally to protect the mixer.
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 be treated carefully during development.
The most dangerous situation is editing code while audio is active. The VBE may internally recompile modules, move project state, invalidate procedure addresses, or reset runtime structures while the render callback is still scheduled. In normal Office automation this is unusual, but an audio engine with native callbacks can hit that edge case if the developer changes code while the sound is still playing.
The current stop-safe build includes cleanup for 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;
RiffEditorEmergencyStopis available as a manual recovery helper for development sessions;RiffPrepareForVbeEditandRiffResumeAfterVbeEditprovide a safer manual workflow before and after editing code.
Recommended workflow before changing code while the engine is active:
Public Sub EnterSafeEditMode()
RiffPrepareForVbeEdit
End Sub
Public Sub LeaveSafeEditMode()
RiffResumeAfterVbeEdit
End SubEmergency cleanup during development:
Public Sub DevAudioReset()
RiffEditorEmergencyStop
RiffClose
End SubRiffEditorSafeMode can be enabled for automatic editor protection experiments, and RiffEditorTimerSuspended can be inspected for diagnostics. It is disabled by default so normal playback tests from the VBE do not get paused unexpectedly.
Use these helpers only while developing. Normal applications should call RiffClose from their shutdown path.
| Category | Features |
|---|---|
| Core | Single .bas, dynamic buffers, dynamic voices, dynamic bus state, x86/x64 support |
| I/O | Media Foundation decoding, in-memory loading, WAV fast path, WAV export |
| Asset Management | Asset registry, key-based playback, key-based stop/fade, cooperative preload queue |
| Playback | Unified RiffPlay, RiffPlayOnce, RiffPlayKey, RiffPlayKeyOnce, finite oscillator/noise one-shots, seeking, looping, fades, stop/reset behavior |
| Routing | Bus volume, mute, solo, fade, peak/RMS meters, persistent bus presets, master volume |
| Scenes | Built-in templates for normal, pause, battle, night, cave, underwater, radio, dream, horror, retro, and cinematic states |
| Stability | Adaptive buffering, automatic overload protection, burst coalescing, voice stealing, active voice budgets, dynamic pool growth, idle timer cleanup, Stop/Reset-safe editor cleanup, manual VBE edit guards |
| Presets | Core FX presets, musical preset packs, v1.1.3 expanded presets, persistent bus effects, master presets |
| Master Processing | Soft clip, limiter, low-pass, high-pass, 3-band EQ, tilt EQ, compressor, drive, stereo width, balance, output gain |
| Synthesis | BLEP sine/square/saw, finite oscillator beeps, one-shot and looped white/pink/brown noise |
| Dynamics | Compressor, soft clipping, limiting, 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, master tilt EQ |
| Diagnostics | Underruns, render errors, clipped samples, overload stolen/dropped/coalesced counters, buffer state, active voice, bus voice, buffer voice, RMS, peak, and capacity counters |
| Export | Loaded buffer export and oscillator render to PCM WAV |
- Added the asset registry workflow with
RiffLoadAs,RiffRegisterAsset,RiffAssetExists,RiffAssetHandle,RiffAssetPath,RiffReloadAsset,RiffAssetCount,RiffAssetKey,RiffUnloadKey, andRiffClearAssets. - Added key-based playback with
RiffPlayKey,RiffPlayKeyOnce,RiffPlayKeyLoop,RiffStopKey, andRiffFadeOutKey. - Added cooperative preload helpers:
RiffPreloadAdd,RiffPreloadStart,RiffPreloadUpdate,RiffPreloadUpdateAll,RiffPreloadFinished,RiffPreloadProgress,RiffPreloadLoadedCount,RiffPreloadFailedCount,RiffPreloadTotalCount,RiffPreloadCurrentKey,RiffPreloadItemStatus,RiffPreloadItemKey,RiffPreloadItemHandle, andRiffPreloadClear. - Added scene templates through
RiffApplySceneandRiffScenePresetfor normal, pause menu, battle, night, cave, underwater, radio call, dream, horror, retro, and cinematic states. - Added expanded v1.1.3 presets:
RiffFxCassette,RiffFxOldComputer,RiffFxArcadeCabinet,RiffFxDreamMenu,RiffFxSpaceStation,RiffFxDungeon,RiffFxBossRoom,RiffFxLowHealth,RiffFxMemoryFlashback,RiffFxCutscene, andRiffFxCombatImpact. - Added automatic overload protection that detects extreme play-call spam internally without requiring public
Begin/Endburst calls. - Added internal auto-burst/coalescing behavior so repeated same-tick sounds can be accepted without forcing thousands of redundant real voices into the mixer.
- Added
RiffOverloadProtectionEnabled,RiffLoopCoalescingEnabled,RiffMaxActiveVoices,RiffOverloadDroppedCount,RiffOverloadStolenCount,RiffOverloadCoalescedCount,RiffResetOverloadStats, andRiffApplyStressSafeDefaults. - Added master limiter and safety tools:
RiffMasterSetLimiter,RiffMasterLimiterEnabled,RiffMasterLimiterCeiling,RiffMasterOutputGain, andRiffSoftClipEnabledstress-safe setup. - Added master mix helpers and diagnostics:
RiffMasterBalance,RiffMasterTiltEq,RiffMasterGetRms,RiffMasterGetRmsDb,RiffMasterRmsDb,RiffMasterPeakDb,RiffMasterClipCount, andRiffResetDiagnostics. - Added/expanded bus and voice RMS/clip diagnostics for balancing, visualizers, and stress tests.
- Improved burst benchmarks from the older
~11–13 us/calldry path to around~4–5 us/callin the latest local stress benchmark. - Preserved the normal user-facing workflow: existing code can keep calling
RiffPlay,RiffPlayKey,RiffPlayNoise, andRiffPlayOscillatornormally.
- Reworked buffer and voice storage from fixed-size pools into dynamic
SafeArraypools. - Removed the old hardcoded capacity ceiling for loaded buffers and active voices.
- Added dynamic bus-state capacity while preserving the named bus workflow.
- Added
RiffReserveBuffers,RiffReserveVoices, andRiffReserveBusesfor projects that want to preallocate capacity during startup. - Updated
RiffMaxBuffers,RiffMaxVoices, andRiffMaxBusesso they describe current capacity instead of an old fixed limit. - Kept handle-based compatibility so existing code can continue using numeric buffer and voice handles.
- Added manual editor-safe development helpers:
RiffPrepareForVbeEditandRiffResumeAfterVbeEdit. - Added
RiffEditorSafeModeandRiffEditorTimerSuspendedfor optional automatic protection and diagnostics. - Improved safety around timer/callback state when the developer edits code in the VBE while audio is active.
- Changed default burst-cap behavior so
0means no cap when a project intentionally wants unrestricted layering. - Preserved the v1.0.9 playback, DSP, presets, adaptive buffering, WAV fast path, and editor emergency cleanup features.
- 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
durationSecsupport for generated oscillator and noise playback. - Added explicit
RiffPlayNoiseLoophelpers for continuous procedural ambience. - Added
RiffVoiceSetFilterHzfor frequency-based filter setup. - Added editor-safe timer cleanup and
RiffEditorEmergencyStopfor development recovery. - Improved
RiffPlayand 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.
- 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.
- WASAPI Shared Mode playback.
- Media Foundation decoding.
- WAV fast path loader.
- Unified playback API.
- Dynamic SafeArray buffer pool.
- Dynamic polyphonic mixer with expandable voice capacity.
- Manual capacity reservation helpers.
- Named bus routing system backed by dynamically sized bus state.
- Bus mute, solo, fade, peak meters, RMS meters, and clip counters.
- Persistent bus effect presets.
- Full per-voice Studio DSP pipeline.
- Core effect presets.
- Musical preset packs.
- Expanded v1.1.3 effect presets.
- Master bus processors.
- Master processor presets.
- Master limiter, output headroom, balance, tilt EQ, RMS, peak, and clip diagnostics.
- 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.
- Automatic overload protection and internal auto-burst coalescing.
- Stress-safe defaults for SFX-heavy projects.
- Lazy temporal-buffer preparation for faster
Playand preset setup. - Asset registry and key-based playback.
- Cooperative preload helpers.
- Built-in scene templates.
- VBE-safe idle, Stop/Reset timer cleanup, and manual safe-edit guards.
- x86/x64 native thunk driver.
- Offline WAV export.
- Diagnostics, render counters, overload counters, meters, and current capacity counters.
- Optional native decode backend for faster MP3/OGG loading.
- Optional priority groups for more explicit music/voice/SFX stealing rules.
- More musical preset packs and scene templates.
- Additional visualizer helpers for PowerPoint/Excel UI meters.
- macOS support through CoreAudio/AudioToolbox if the project expands beyond Windows.
For best performance in Office:
- Prefer WAV for short SFX and UI sounds.
- Preload assets at startup with
RiffLoadorRiffLoadAs. - Use the asset registry for large projects so gameplay code can call
RiffPlayKeyinstead of carrying many handle variables. - Use cooperative preload when a project needs a visible loading screen or a staged asset-loading flow.
- For large projects, reserve capacity at startup with
RiffReserveBuffers,RiffReserveVoices, andRiffReserveBuses. - For SFX-heavy games, call
RiffApplyStressSafeDefaults 64, 6, 32as a strong baseline. - Avoid decoding MP3/OGG during interaction-heavy moments.
- Use
RiffPlayOnceorRiffPlayKeyOncefor music and ambience. - Use
RiffPlayNoiseLoopfor continuous procedural ambience instead of looping a defaultRiffPlayNoiseone-shot. - Use
durationSecfor short oscillator beeps and procedural noise hits. - Route audio to meaningful buses such as
RiffBusMusic,RiffBusSfx,RiffBusUi, andRiffBusVoice; this makes scene templates and bus effects much more useful. - Use
RiffApplyScenefor broad scene states instead of manually rebuilding the same bus/master settings everywhere. - 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 overload counters to inspect heavy scenes:
RiffOverloadStolenCount,RiffOverloadDroppedCount, andRiffOverloadCoalescedCount. - Use
RiffMaxVoicesPerBufferandRiffMaxVoicesPerBusto prevent repeated button clicks or collision sounds from stacking too many copies, or set them to0when you intentionally want no explicit cap. - Use the master limiter/headroom path for loud scenes:
RiffMasterSetLimiter,RiffMasterOutputGain, andRiffSoftClipEnabled. - Use
RiffVoiceSetFilterHzwhen frequency-based filtering is clearer than normalized filter values. - Keep
RiffAutoSuspendTimerenabled for editor stability unless you intentionally need a warm timer during heavy gameplay bursts. - Use
RiffPrepareForVbeEditbefore editing code while audio is active, thenRiffResumeAfterVbeEditwhen finished. - Use
RiffEditorEmergencyStoponly as a development recovery helper if an old session leaves the VBE stuck. - Always call
RiffCloseon 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 and overload protection reduce this, but a fully independent audio thread would require a native backend.
MIT. Designed for freedom, integration, and serious audio experimentation inside Microsoft Office.
