Sound

Technical overview. The Game Boy audio processing unit produces digital audio while the CPU and LCD advance. PyBoy collects those samples in a stereo buffer for each frame; the number of valid samples can vary when frame timing changes.

Using the API. Sound exposes the sample rate, raw buffer format, valid buffer length, and the current samples as either a memory view or a NumPy array. Read the buffer head each frame so consumers process only the samples generated for that frame.

This class gives access to the sound buffer of PyBoy.

class pyboy.api.sound.Sound(mb)[source]

Bases: object

As part of the emulation, we generate a sound buffer for each frame on the screen. This class has several helper methods to make it possible to read this buffer out.

When the game enables/disables the LCD, the timing will be shorter than 70224 emulated cycles. Therefore the sound buffer will also be shorter than a normal 16.74ms frame.

Because a Game Boy frame is about 59.73Hz, the number of samples per frame fluctuates with the configured sample rate. At 24,000Hz, a normal frame contains about 402 samples. When the LCD enables or disables, a frame can be shorter, with perhaps 30, 143, or 200 samples. This timespan represents what the real hardware would have shown.

If you’re working with encoding the screen and sound in a video stream, you could drop these shorter frames, if they cause problems. They usually only happen in transitions from menu to game or similar.

sample_rate

Read-only. Changing this, will not change the sample rate. See PyBoy constructor instead.

The sample rate is reported per second, while a Game Boy frame is about 59.73Hz. The sample count per frame fluctuates; see the explanation above.

>>> pyboy.sound.sample_rate # in Hz
48000
>>> (pyboy.sound.sample_rate * 70224 + 4194304 - 1) // 4194304
804
>>> (804+1) * 2 # Stereo buffer capacity, including one extra sample
1610
>>> 1610 == pyboy.sound.raw_buffer_length
True
Returns:

The sample rate in Hz (samples per second)

Return type:

int

raw_buffer_format

Returns the sample format of the raw sound buffer. This format is subject to change.

See how to interpret the format on: https://docs.python.org/3/library/struct.html#format-characters

Example:

>>> pyboy.sound.raw_buffer_format
'b'
Returns:

Struct format of the raw sound buffer. E.g. ‘b’ for signed 8-bit

Return type:

str

raw_buffer_length

Read-only. Changing this, will not change the buffer length.

This is the total length of the allocated raw buffer. Use this only to allocate an appropriate buffer in your script. The length of the valid data in the buffer is found using Sound.raw_buffer_head.

Returns:

Total raw buffer length

Return type:

int

raw_buffer

Provides a raw, unfiltered memoryview object with the data from sound buffer. Check Sound.raw_buffer_format to see which dataformat is used. The returned type and dataformat are subject to change.

The sound buffer is interleaved stereo: Even indexes are the left channel and odd indexes are the right channel.

Use this, only if you need to bypass the overhead of Sound.ndarray.

Use Sound.raw_buffer_head as not all frames have the same number of samples.

Example:

>>> from array import array
>>> sound_buffer = array(pyboy.sound.raw_buffer_format, pyboy.sound.raw_buffer[:pyboy.sound.raw_buffer_head])
>>> sound_buffer
array('b', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...])
Returns:

memoryview of sound data.

Return type:

memoryview

property raw_buffer_head

Number of valid interleaved channel values in raw_buffer. There are two values per stereo sample frame.

Use this as the stop index when slicing raw_buffer.

Returns:

Number of valid channel values in the raw buffer.

Return type:

int

property ndarray

References the valid sound data in NumPy format. Remember to copy this object if you intend to store it. The backing buffer will update, but it will be the same ndarray object.

The format is given by pyboy.api.sound.Sound.raw_buffer_format. The sound buffer is in stereo format, so the first column is the left channel and the second column is the right channel.

The first dimension contains only the sample frames generated since the last call to PyBoy.tick. If sound emulation is disabled, accessing this property raises PyBoyFeatureDisabledError.

Example:

>>> pyboy.sound.ndarray.shape[1] # Number of stereo channels
2
>>> pyboy.sound.ndarray
array([[0, 0],
       [0, 0],
       ...
       [0, 0],
       [0, 0]], dtype=int8)
Returns:

Signed 8-bit samples with shape (valid_sample_frames, 2).

Return type:

numpy.ndarray