The audiomath.PsychToolboxInterface sub-module

This sub-module provides an alternative back-end for high- precision playback, based on the PsychToolbox project’s reworking of the PortAudio binaries. It requires the third-party psychtoolbox module to be installed (you must do this yourself, for example by running python -m pip install psychtoolbox, because psychtoolbox is not a hard/automatically-installed dependency of audiomath.

This alternative back-end can be enabled by calling audiomath.BackEnd.Load('PsychToolboxInterface'). This removes the default audiomath.PortAudioInterface.* symbols from the top-level audiomath.* namespace and replaces them with audiomath.PsychToolboxInterface.* symbols, most of which have names and prototypes that are familiar from the default back-end (Player, Stream, FindDevices, etc…)

Extra features:

  • The latencyClass input parameter to Player and Stream constructors. This is only respected for the first such construction call, since the same Stream will be shared by all Player instances). The supported settings are all attributes of the LATENCY_CLASS enumerator. The higher the number, the more aggressively (and less cooperatively) the host API will drive the sound device, and the lower the latency is likely to get.

  • The ability to pre-schedule sounds with the when argument, e.g. now = Seconds(); p.Play(when=now + 0.5)

Limitations, relative the the default PortAudioInterface:

  • Player objects cannot Seek(t) to any position except the beginning t=0 (likewise Play(t), Pause(t) or Set(head=t) will only work if t is 0)

  • no on-the-fly resampling or speed control

  • no dynamic properties

  • no on-the-fly sound synthesis (Synth data get frozen and truncated to 10 seconds when passed into a Player).

  • no Recorder implementation

Notes

  • on Windows, PsychToolbox uses the WASAPI host API by default. For best performance, and especially if you want to use latencyClass < 2 (such that other processes can access the sound device at the same time) then you should ensure your sounds are sampled at 48000 Hz, not the usual 44100 Hz.

  • some host APIs may be unable to be driven with certain latency classes (for example, on Windows most of the soundcard drivers we have tried cannot use WDM-KS with a latency class higher than 1)

audiomath.PsychToolboxInterface.FindDevice(id=None, mode=None, apiPreferences=None, _candidates=None)

Returns the first device matched by FindDevices() (plural) according to the specified criteria. Raises an exception if there are no matches.

audiomath.PsychToolboxInterface.FindDevices(id=None, mode=None, apiPreferences=None, _candidates=None)

Calls GetDeviceInfo(), filtering and reordering the outputs according to the specified criteria. You can print() the result, or otherwise convert it to str(), to see a pretty tabulated summary.

You can use FindDevice() (singular) to return just the top-ranking result (and to assert that at least one result can be found).

Parameters:
  • id (int, dict, str, None) –

    • None: matches all devices;

    • int: matches only the device whose index field matches id;

    • dict (including the objects returned by this function, which are dict subclassses): matches only the device whose index field matches id['index']

    • str: matches any device with the specified word or phrase in its name field (you can also match hostApi.name and name simultaneously if you delimit them with '//'—for example, 'WDM-KS//Speakers');

  • mode (str, tuple, None) – May be either a two-element tuple such as mode=(minInputChannels,minOutputChannels), or a string containing a number of 'o' and/or 'i' characters. In either case, devices are only matched if they provide at least the specified number of input and output channels. For example, FindDevices(mode='oo') matches all devices that provide two or more output channels.

  • apiPreferences (str, None) – If this is left at None, it defaults to the current value of PORTAUDIO.DEFAULT_INPUT_API_PREFERENCE_ORDER (if the mode argument requests any inputs) or PORTAUDIO.DEFAULT_OUTPUT_API_PREFERENCE_ORDER otherwise. The string '*' matches all host APIs. Host APIs may be comma-delimited. For example, apiPreferences='DirectSound,*' means “give first priority to devices hosted by the DirectSound API, and then, failing that, match all other APIs”. If '*' is not included in the specification, this argument may limit the number of records returned. In any case it will affect the ordering of the returned records.

audiomath.PsychToolboxInterface.GetDeviceInfo()

Returns a list of records corresponding to PortAudio’s Pa_GetDeviceInfo() output for every available combination of host API and device. You can print() the result, or otherwise convert it to str(), to see a pretty tabulated summary.

audiomath.PsychToolboxInterface.GetHostApiInfo()

Returns a list of records corresponding to PortAudio’s Pa_GetHostApiInfo() output for every available host API. You can print() the result, or otherwise convert it to str(), to see a pretty tabulated summary.

class audiomath.PsychToolboxInterface.LATENCY_CLASS

This class is an enum container: a namespace of values that can be passed as the latencyClass constructor argument when creating a PsychToolboxInterface.Player instance.

RELAXED (0):

indicates that latency is not a priority—latencies are then unpredictable and may be in the hundreds of milliseconds.

SHARED (1):

applies the most aggressive latency settings that still allow other processes to play sound at the same time.

EXCLUSIVE (2):

takes exclusive control of the sound card.

AGGRESSIVE (3):

takes exclusive control of the sound card, with even more aggressive low-latency settings than EXCLUSIVE mode.

CRITICAL (4):

is the same as AGGRESSIVE mode, but raises an exception if the settings cannot be achieved.

class audiomath.PsychToolboxInterface.Player(sound, device=None, stream=None, latencyClass='DEFAULT', bufferLengthMsec=None, minLatencyMsec=None, fs=None, resample=True, verbose=None, **kwargs)

A Player provides a persistent connection to the chosen playback hardware, allowing a Sound instance to be played.

A Player instance can only play one Sound at a time, but it can optionally maintain a list of Sound instances in a Queue and switch between them. For overlapping sounds, use multiple Player instances.

A Player instance can be created directly from a filename, list of filenames or filename glob pattern. The current Sound instance is available in the sound property and the complete Queue is available in the queue property.

It is better to create a Player instance than to rely on the quick-and-dirty Play() methods of the Sound and Queue classes—these methods just create a Player (entailing some computational overhead), wait synchronously for it to finish, and then destroy it again. Creating your own Player instance provides much greater flexibility and performance.

Parameters:
  • sound (str, Sound, Queue, None) – Sound instance to play (or sequence of Sound instances in a list or Queue). Alternatively, supply any argument that is also a valid input to the Sound or Queue constructors (e.g. a filename, list of filenames, or file glob pattern).

  • device (int, str, dict, Stream, None) – Optionally use this argument to specify the device/stream to use for playback—as an integer index, a device name, a full device record from FindDevice(), or (fastest of all) an already-open Stream instance.

  • stream (int, str, dict, Stream, None) – Synonymous with device, for compatibility.

  • latencyClass (int, str) – specify the PsychPortAudio latency mode (use one of the names or integer values defined in the enumerator LATENCY_CLASS)

  • bufferLengthMsec (float, None) – Optionally specify a buffer length in milliseconds when creating your first Player or first Stream. (Future Player instances will all use the same setting.)

  • minLatencyMsec (float, None, ‘auto’) – Optionally specify the value to use as “suggested latency” when creating your first Player or first Stream. (Future Player instances will all use the same setting.)

  • fs (float, None) – Optionally specify the sampling frequency, in Hz, when creating your first Player or first Stream (after that, Player instances may share an open Stream instance so it is possible that only the first call will make any difference).

  • resample (bool) – This should be left as True because there is no on-the-fly resampling capability in the PsychToolboxInterface back-end, The parameter is included purely for compatibility with code written for the default PortAudioInterface back-end.

  • verbose (bool, None) – Verbosity for debugging. If None, inherit from the setting specified by SetDefaultVerbosity(), if any.

  • **kwargs – passed through to Set() to initialize properties of the Player instance.

property levels

This is an optional sequence of multipliers applied to each channel in turn. It is independent of (i.e. its effects simply get multiplied by the effects of) other properties that affect intensity, such as volume and pan

levels is a dynamic property (see dynamics).

property muted

This is a boolean property that, if true, causes the Player to be silent, independent of the current volume and/or levels.

muted is a dynamic property (see dynamics).

property norm

For a Player instance p, if you set p.pan to a scalar value between -1 and +1, the relative levels of left and right channels are computed such that:

left ** p.norm + right ** p.norm = 1

The value p.norm=2 produces a natural-sounding pan but it means that stereo sounds are reduced to 70.71% of their maximum amplitude by default. So instead, the default is to use the infinity-norm, p.norm='inf', which ensures that the larger of the two sides is always 1.0

property pan

A value of -1 means left channels only, right channels silent. A value of 0 means left and right channels at equal volume. A value of +1 means right channels only, left channels silent.

The way levels are computed from scalar values between -1 and +1 depends on norm. Alternatively, you can supply a two- element sequence that explicitly specifies [left, right] levels.

Note that these levels are independent from (i.e. they are simply multiplied by) the overall volume and channel-by- channel levels settings.

pan is a dynamic property (see dynamics).

property sound

A reference to the Sound instance that the Player is currently playing, or None. You can change it on-the-fly. It may also change automatically when the current sound finishes playing, if you have appended additional Sound instances the queue and have set the autoAdvance property.

property vol

This floating-point property is a multiplier for the amplitude of the sound waveforms. It is independent of (i.e. its effect simply gets multiplied by the effects of) other properties that affect channel-to- channel level differences, such as levels and pan

volume is a dynamic property (see dynamics).

property volume

This floating-point property is a multiplier for the amplitude of the sound waveforms. It is independent of (i.e. its effect simply gets multiplied by the effects of) other properties that affect channel-to- channel level differences, such as levels and pan

volume is a dynamic property (see dynamics).

audiomath.PsychToolboxInterface.Record(*p, **k)

TODO: no Record() implementation yet in this back-end

class audiomath.PsychToolboxInterface.Recorder(*p, **k)

TODO: no Recorder implementation yet in the audiomath.PsychToolboxInterface back-end

classmethod MakeRecording(*p, **k)

TODO: no Record() implementation yet in this back-end

audiomath.PsychToolboxInterface.Seconds()

An alias for PsychToolbox’s clock, psychtoolbox.GetSecs()

class audiomath.PsychToolboxInterface.Stream(device=(), mode=None, sampleRate=None, latencyClass='DEFAULT', verbose=False, bufferLengthMsec=None, minLatencyMsec=None)

A persistent connection to an audio driver. Multiple output callbacks (player objects) and/or multiple input callbacks (recorder objects) may share the same Stream, or under some conditions it may be possible for each callback (or object) to have its own Stream.

Typically, objects that play or record will automatically create a Stream or add themselves to an existing Stream, so you will not usually need to construct a Stream instance yourself. The exception is when you need to ensure that the construction of such objects is itself fast: then, creating a Stream in advance, and keeping a reference to it to use in initializing the other objects, will speed things up.

The device, mode and apiPreferences arguments are the same as id, mode and apiPreferences in FindDevice().

The latencyClass argument is unique to the PsychToolboxInterface back-end. It specifies the PsychPortAudio latency mode (use one of the names or integer values defined in the enumerator LATENCY_CLASS).