.. _soundAPI:

:mod:`psychopy.sound` - play various forms of sound
==============================================================================

.. module:: psychopy.sound

:class:`Sound`
-------------------

PsychoPy currently supports a choice of sound engines: PTB, pyo, sounddevice or pygame. You can select which will be
used via the :ref:`audioLib<hardwareSettings>` preference. `sound.Sound()` will then refer to one of
:class:`~psychopy.sound.backend_ptb.SoundPTB`,
:class:`~psychopy.sound.backend_sounddevice.SoundDevice`,
:class:`~psychopy.sound.backend_pyo.SoundPyo` or
:class:`~psychopy.sound.backend_pygame.SoundPygame`. This preference can be set on a per-experiment basis by importing
preferences, and :doc:`setting the audioLib option</api/preferences>` to use.

- The `PTB` library has by far the lowest latencies and is strongly recommended (requires 64 bit Python3)
- The `pyo` library is, in theory, the highest performer, but in practice it has ften had issues (at least on macOS) with crashes and freezing of experiments, or causing them not to finish properly. If those issues aren't affecting your studies then this could be the one for you.
- The `sounddevice` library looks like the way of the future. The performance appears to be good (although this might be less so in cases where you have complex rendering being done as well because it operates from the same computer core as the main experiment code). It's newer than `pyo` and so more prone to bugs and we haven't yet added microphone support to record your participants.
- The `pygame` backend is the oldest and should work without errors, but has the least good performance. Use it if latencies foryour audio don't mattter.

Sounds are actually generated by a variety of classes, depending on which "backend" you use (like pyo or sounddevice) and these different backends can have slightly different attributes, as below.

The user should typically do::

    from psychopy.sound import Sound

but the class that gets imported will then be an alias of one of the :ref:`soundClasses` described below.

.. _PTB_audio_latency:

PTB audio latency
-------------------------

PTB brings a number of advantages in terms of latency.

The first is that is has
been designed specifically with low-latency playback in mind (rather than, say,
on-the-fly mixing and filtering capabilities). Mario Kleiner has worked very hard
get the best out of the drivers available on each operating system and, as a
result, with the most aggressive low-latency settings you can get a sound to
play in "immediate" mode with typically in the region of 5ms lag and maybe 1ms
precision. That's pretty good compared to the other options that have a lag of
20ms upwards and sevral ms variability.

BUT, on top of that, PTB allows you to :ref:`ptbPreschedule` your sound to occur at a
particular point in time (e.g. when the trigger is due to be sent or when the
screen is due to flip) and the PTB engine will then prepare all the buffers
ready to go and will also account for the known latencies in the card. With this
method the PTB engine is capable of sub-ms *precision* and even sub-ms *lag*!

Of course, *capable* doesn't mean it's happening in your case. It can depend on
many things about the local operating system and hardware. You should test it
yourself for your kit, but here is an example of a standard Win10 box using
buit-in audio (not a fancy audio card):

.. figure:: /images/audioScope_Win10_PTB_mode3.png
    :align: center

    Sub-ms audio timing with standard audio on Win10. Yellow trace is a 440
    Hz tone played at 48 kHz with PTB engine. Cyan trace is the trigger
    (from a Labjack output). Gridlines are set to 1 ms.


.. _ptbPreschedule:

Preschedule your sound
-----------------------

The most precise way to use the PTB audio backend is to preschedule the 
playing of a sound. By doing this PTB can actually take into account both the 
time taken to load the sound (it will preload ready) and also the time taken
by the hardware to start playing it.

To do this you can call `play()` with an argument called `when`. The `when` argument
needs to be in the PsychToolBox clock timebase which can be accessed by using 
`psychtoolbox.GetSecs()` if you want to play sound at an arbitraty time (not in sync
with a window flip) 

For instance::

    import psychtoolbox as ptb
    from psychopy import sound

    mySound = sound.Sound('A')
    now = ptb.GetSecs()
    mySound.play(when=now+0.5)  # play in EXACTLY 0.5s

or using `Window.getFutureFlipTime(clock='ptb')` if you want a
synchronized time::

    import psychtoolbox as ptb
    from psychopy import sound, visual

    mySound = sound.Sound('A')

    win = visual.Window()
    win.flip()
    nextFlip = win.getFutureFlipTime(clock='ptb')

    mySound.play(when=nextFlip)  # sync with screen refresh

The precision of that timing is still dependent on the :ref:`_PTB_latency_modes`
and can obviously not work if the delay before the requested time is not long 
enough for the requested mode (e.g. if you request that the sound starts on the
next refresh but set the latency mode to be `0` (which has a lag of around 300 ms)
then the timing will be very poor.

.. _PTB_latency_modes:

PTB Audio Latency Modes
--------------------------

When using the PTB backend you get the option to choose the Latency Mode,
referred to in PsychToolBox as the `reqlatencyclass`.

PsychoPy uses Mode 3 in as a default, assuming that you want low latency and you
don't care if other applications can't play sound at the same time (don't listen
to iTunes while running your study!)

The modes are as follows:

**0 : Latency not important**
    For when it really doesn't matter. Latency can easily be in the region of
    300ms!

**1 : Share low-latency access**
    Tries to use a low-latency setup in combination
    with other applications. Latency usually isn't very good and in MS Windows
    the sound you play must be the same sample rate as any other application
    that is using the sound system (which means you usually get restricted to
    exactly 48000 instead of 44100).

**2 : Exclusive mode low-latency**
    Takes control of the audio device you're using and dominates it. That can
    cause some problems for other apps if they're trying to play sounds at the
    same time.

**3 : Aggressive exclusive mode**
    As Mode 2 but with more aggressive settings to prioritise our use of the
    card over all others. **This is the recommended mode for most studies**

**4 : Critical mode**
    As Mode 3 except that, if we fail to be totally dominant, then raise an error rather
    than just accepting our slightly less dominant status.


.. _soundClasses:

Sound Classes
-------------------

`PTB` Sound
~~~~~~~~~~~~~~~~~

.. autoclass:: psychopy.sound.backend_ptb.SoundPTB
    :members:


`SoundDevice` Sound
~~~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: psychopy.sound.backend_sounddevice.SoundDeviceSound
    :members:

`Pyo` Sound
~~~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: psychopy.sound.backend_pyo.SoundPyo
    :members:


`pygame` Sound
~~~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: psychopy.sound.backend_pygame.SoundPygame
    :members:
