How Windows 3.11 Processed WAV Audio Files
In Microsoft Windows 3.11, processing and playing a standard waveform
audio (WAV) file required a cooperative sequence between high-level
applications, the 16-bit multimedia subsystem, specialized device
drivers, and PC hardware. When a program requested sound playback,
Windows routed the request through the Windows Multimedia architecture
(MMSYSTEM.DLL). This subsystem translated the
command—whether a simple system alert or streaming audio—into queued
blocks of raw Pulse Code Modulation (PCM) data, transferred those blocks
via Direct Memory Access (DMA) to the sound card, and handled hardware
interrupts to maintain continuous, stutter-free playback in a
cooperative multitasking environment.
The Application Layer and Audio APIs
When an application initiated WAV playback, it interacted with one of two API layers:
- High-Level APIs (MCI and
sndPlaySound): Programs like Windows Media Player or simple system events used the Media Control Interface (MCI) or thesndPlaySound()function. MCI acted as a high-level command interpreter, allowing programs to send human-readable string commands (such asplay sound.wav) or programmatic messages. The system located the file, parsed the RIFF-WAVE header to identify the sample rate, bit depth, and channel count, and forwarded the raw audio data down the stack. - Low-Level Audio APIs (
waveOut): Programs requiring precise control, such as Sound Recorder, bypassed MCI and communicated directly with the low-level waveform audio interface. This API included functions such aswaveOutOpen(),waveOutPrepareHeader(), andwaveOutWrite().
The Core Subsystem: MMSYSTEM.DLL
The central engine for Windows 3.11 audio was
MMSYSTEM.DLL, introduced originally with the Windows 3.0
Multimedia Extensions. This dynamic link library managed audio device
abstraction and hardware independence.
Once an application specified a target audio format,
MMSYSTEM.DLL queried the active waveform driver to
determine if the hardware could natively support parameters like 22 kHz,
8-bit mono, or 44.1 kHz, 16-bit stereo. If compatible, the subsystem
negotiated the opening of an audio output channel, establishing a
communication handle between the software and the hardware driver.
Memory Allocation and Double-Buffering
Because Windows 3.11 operated on 16-bit cooperative multitasking (where a hung application could freeze the entire operating system), audio could not be streamed sample-by-sample. Instead, it relied on strict memory buffering:
- Buffer Locking: The operating system allocated
memory blocks for chunks of audio data using
GlobalAlloc()with theGMEM_FIXEDorGMEM_SHAREflags, ensuring the memory would not be paged out or relocated by the memory manager. - Headers: Each data chunk was assigned a
WAVEHDRstructure, which tracked the buffer length, playback progress, and flags indicating whether the buffer was queued, in progress, or finished. - Queuing: Windows used double-buffering or circular
ring-buffering. Applications typically queued at least two audio buffers
via
waveOutWrite(). While the sound card processed the first buffer, the application or multimedia system prepped the next buffer in memory, preventing dropouts.
The Hardware Driver Layer
Below MMSYSTEM.DLL sat the hardware-specific driver (for
example, SB16.DRV for a Creative Sound Blaster 16 or
SNDBLST.DRV for original Sound Blaster models), specified
in the SYSTEM.INI file.
The driver received the prepared buffers from
MMSYSTEM.DLL. Rather than using the CPU to push every
individual byte of sound data to the card, the driver configured the
PC's Direct Memory Access (DMA) controller (typically DMA Channel 1 for
8-bit audio or DMA Channel 5 for 16-bit audio). The driver passed the
physical memory address of the locked audio buffer and the total byte
count to the DMA controller.
Hardware Execution and Interrupts
Once the sound card's Digital Signal Processor (DSP) received the command to play, the process moved to the physical hardware:
- DMA Transfer: The sound card asserted DMA requests (DRQ). The DMA controller moved data directly from the system RAM buffer to the sound card's internal digital-to-analog converter (DAC) at the sample rate defined in the WAV file.
- Analog Output: The DAC converted the discrete digital values into continuous voltage variations, which were filtered, amplified, and sent to the speaker output.
- Interrupt Handling (IRQ): When the DMA transfer for a buffer completed, the sound card generated a hardware interrupt (such as IRQ 5 or IRQ 7).
- Callback Notification: The driver’s Interrupt
Service Routine (ISR) trapped this signal, acknowledged the interrupt to
the Programmable Interrupt Controller (PIC), and notified
MMSYSTEM.DLL. - Buffer Cycling:
MMSYSTEM.DLLposted a message (MM_WOM_DONE) back to the originating application or triggered a callback function. This alerted the application that the buffer was spent, freeing the memory and allowing the next buffer in the queue to immediately begin processing without interrupting the audio stream.