杰瑞科技汇

如何在Python中使用DirectSound?

Of course! Let's dive into using DirectSound with Python.

如何在Python中使用DirectSound?-图1
(图片来源网络,侵删)

DirectSound is the legacy audio API for Windows. While it's largely superseded by WASAPI and XAudio2 for modern, low-latency applications, it's still powerful, relatively easy to use for basic playback, and has excellent hardware acceleration and mixing capabilities.

The primary Python library for this is pydirectsound. It's a thin wrapper around the native dsound.dll.


Installation

First, you need to install the pydirectsound library. It's available on PyPI.

pip install pydirectsound

Important Note: This library is Windows-only. It will not work on macOS or Linux.

如何在Python中使用DirectSound?-图2
(图片来源网络,侵删)

Basic Playback Example

This is the simplest possible example: playing a WAV file. We'll create a DirectSound object, load a sound buffer, and play it.

import directsound
import time
# 1. Initialize DirectSound
# This gives us a primary buffer, which represents the sound card's output.
ds = directsound.DirectSound()
# 2. Load a sound file into a secondary buffer
# A secondary buffer is a memory buffer that holds your audio data.
# pydirectsound can load WAV files directly.
# Replace 'my_sound.wav' with the path to your own WAV file.
try:
    sound_buffer = ds.create_buffer_from_file("my_sound.wav")
except FileNotFoundError:
    print("Error: 'my_sound.wav' not found. Please create a test WAV file.")
    exit()
# 3. Play the sound
# The play() method starts the sound from the beginning.
# It can also take a flag like directsound.DSBPLAY_LOOPING to loop the sound.
print("Playing sound...")
sound_buffer.play()
# 4. Wait for the sound to finish
# The `is_playing()` method is very useful for this.
while sound_buffer.is_playing():
    time.sleep(0.1) # Sleep to prevent a tight loop
print("Playback finished.")
# 5. Clean up (optional, but good practice)
# The buffers are automatically released when the object is garbage collected,
# but explicitly releasing them is a good habit.
sound_buffer.release()
ds.release()

To run this, save it as play_sound.py and make sure you have a my_sound.wav file in the same directory.


Key Concepts and Objects

To understand more advanced usage, you need to know the main objects:

  1. DirectSound Object:

    如何在Python中使用DirectSound?-图3
    (图片来源网络,侵删)
    • Represents the main connection to the sound device.
    • Created with directsound.DirectSound().
    • Used to create primary and secondary buffers.
    • Has a set_cooperative_level() method, which is important. ds.set_cooperative_level(directsound.DSSCL_PRIORITY) is a common setting that allows you to change the format of the primary buffer.
  2. DirectSoundBuffer Object:

    • This is where your audio data lives. There are two types:
      • Primary Buffer: Represents the final output that goes to the speakers. You usually don't write data to it directly. Instead, you configure its format (e.g., stereo, 44.1kHz).
      • Secondary Buffer: This is what you'll use 99% of the time. It's a chunk of memory you load with your audio (from a file or by writing raw data). You can play, stop, pause, and loop these buffers independently.
  3. DSBCAPS (Buffer Capabilities):

    • When creating a buffer, you can specify its capabilities using flags. The most common ones are:
      • directsound.DSBCAPS_CTRLVOLUME: Allows you to change the volume.
      • directsound.DSBCAPS_CTRLPAN: Allows you to pan the sound (left/right).
      • directsound.DSBCAPS_CTRLFREQUENCY: Allows you to change the playback rate (pitch).
      • directsound.DSBCAPS_LOCATIONAL: For 3D sound positioning.
      • directsound.DSBCAPS_STATIC: For sounds that are loaded once and played many times (optimization).

Advanced Example: Creating a Buffer from Raw Data

Sometimes you don't have a WAV file. You might generate audio or have it in another format. You can create a buffer from raw PCM data.

This example creates a 1-second, 440Hz sine wave and plays it.

import directsound
import numpy as np
import time
# --- 1. Generate a 1-second sine wave ---
sample_rate = 44100
duration = 1.0  # seconds
frequency = 440  # Hz (A4)
# Generate time points
t = np.linspace(0., duration, int(sample_rate * duration), endpoint=False)
# Generate the sine wave signal (16-bit signed integers)
amplitude = np.iinfo(np.int16).max // 2 # Half the max amplitude to avoid clipping
sine_wave = (amplitude * np.sin(2. * np.pi * frequency * t)).astype(np.int16)
# Convert to bytes, as DirectSound needs raw byte data
raw_data = sine_wave.tobytes()
# --- 2. Initialize DirectSound ---
ds = directsound.DirectSound()
ds.set_cooperative_level(directsound.DSSCL_PRIORITY)
# --- 3. Create a buffer from the raw data ---
# We need to describe the format of our data
buffer_desc = {
    'buffer_bytes': len(raw_data),
    'format': {
        'format_tag': directsound.WAVE_FORMAT_PCM, # PCM format
        'channels': 1, # Mono
        'samples_per_sec': sample_rate,
        'bits_per_sample': 16,
        'block_alignment': 2, # (channels * bits_per_sample) / 8
        'avg_bytes_per_sec': sample_rate * 2
    },
    'caps': directsound.DSBCAPS_CTRLVOLUME # Enable volume control
}
try:
    sound_buffer = ds.create_buffer(buffer_desc)
    print("Buffer created successfully.")
except Exception as e:
    print(f"Failed to create buffer: {e}")
    ds.release()
    exit()
# --- 4. Lock the buffer, write data, and unlock it ---
# Locking gives us direct access to the buffer's memory.
# We need to handle the case where the sound is playing and wraps around.
start_play_offset = 0
bytes_written = 0
# Write the data in chunks
lock_flags = directsound.DSBSYNC_WRITEPRIMARY
ptr1, bytes1, ptr2, bytes2 = sound_buffer.lock(start_play_offset, len(raw_data), lock_flags)
# Copy our data into the locked memory
if ptr1 and bytes1 > 0:
    # ctypes is used to copy the raw bytes into the memory pointer
    import ctypes
    ctypes.memmove(ptr1, raw_data, bytes1)
    bytes_written += bytes1
if ptr2 and bytes2 > 0:
    # This part is for the "wrap-around" case, which we don't need for a short sound
    # but is essential for streaming.
    ctypes.memmove(ptr2, raw_data[bytes_written:], bytes2)
sound_buffer.unlock(ptr1, bytes1, ptr2, bytes2)
# --- 5. Play the sound ---
print("Playing generated sine wave...")
sound_buffer.play()
while sound_buffer.is_playing():
    time.sleep(0.1)
print("Playback finished.")
# --- 6. Clean up ---
sound_buffer.release()
ds.release()

Controlling Playback

You can easily control the playback of a DirectSoundBuffer.

# Assuming 'sound_buffer' is already created and loaded...
# Set volume (range: -10000 (silence) to 0 (full volume))
sound_buffer.set_volume(-2000) # 80% volume
# Set pan (range: -10000 (full left) to 10000 (full right))
sound_buffer.set_pan(0) # Center
# Set frequency (range: 100 to 100000+)
# 10000 is normal, 20000 is double the speed (one octave higher)
sound_buffer.set_frequency(sample_rate * 2)
# Play the sound
sound_buffer.play()
# Pause the sound
# sound_buffer.stop() # Stop stops and resets position
sound_buffer.pause() # Pause keeps the current position
# To resume, you just call play() again
# sound_buffer.play()
# Stop the sound and reset the play cursor to the beginning
# sound_buffer.stop()

Alternatives and When to Use DirectSound

While pydirectsound is great, it's good to know the other options.

Library/API Use Case Pros Cons
pydirectsound Legacy Windows apps, simple playback, hardware mixing. Native Windows speed, good mixing, simple for basic needs. Windows-only, legacy API, less modern than alternatives.
pyaudio Cross-platform, simple audio I/O. Works on Windows, macOS, Linux. Simple interface. Can have higher latency. Not as tightly integrated with the OS as native APIs.
sounddevice Cross-platform, high-performance scientific audio. Very fast, low-latency, NumPy integration. Can be more complex for simple tasks.
winsound Windows-only, simplest possible beeps. Built-in to Python, no installation. Extremely limited. Only for system beeps and simple WAVs.

Recommendation:

  • If you are on Windows and need low-latency audio for music or games, look into pyaudio with the PortAudio backend or a more advanced library like python-xaudio2.
  • If you need cross-platform support and ease of use, sounddevice is an excellent choice.
  • Use pydirectsound if you specifically need DirectSound's features (like its hardware mixing capabilities for a simple app) or are working with legacy Windows codebases.
分享:
扫描分享到社交APP
上一篇
下一篇