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:

  1. High-Level APIs (MCI and sndPlaySound): Programs like Windows Media Player or simple system events used the Media Control Interface (MCI) or the sndPlaySound() function. MCI acted as a high-level command interpreter, allowing programs to send human-readable string commands (such as play 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.
  2. 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 as waveOutOpen(), waveOutPrepareHeader(), and waveOutWrite().

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 the GMEM_FIXED or GMEM_SHARE flags, ensuring the memory would not be paged out or relocated by the memory manager.
  • Headers: Each data chunk was assigned a WAVEHDR structure, 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:

  1. 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.
  2. Analog Output: The DAC converted the discrete digital values into continuous voltage variations, which were filtered, amplified, and sent to the speaker output.
  3. 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).
  4. 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.
  5. Buffer Cycling: MMSYSTEM.DLL posted 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.