The Player class

class audiomath.Player(sound, device=None, stream=None, bufferLengthMsec=None, minLatencyMsec=None, fs=None, resample=False, verbose=None, **kwargs)

Bases: GenericPlayer

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.

  • bufferLengthMsec (float, None, ‘auto’) – Optionally specify a buffer length in milliseconds 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). Larger buffer lengths lead to higher playback latencies. None means use whatever is currently globally configured in PORTAUDIO.DEFAULT_BUFFER_LENGTH_MSEC. 'auto' or 'pa-default' means use the default supplied by the PortAudio library.

  • minLatencyMsec (float, None, ‘auto’) – Use this setting to override the PortAudio default for “suggested” latency when creating a Stream. The values supplied here typically undershoot the empirically measurable latency (in a non-linear fashion) but larger values mean greater robustness (less crackle/stutter susceptibility) at the cost of longer latencies and higher jitter. None means use whatever is currently globally configured in PORTAUDIO.DEFAULT_MIN_LATENCY_MSEC. 'auto' means use certain defaults that we have empirically derived to balance these factors. 'pa-default' means use the defaults supplied by the PortAudio library.

  • 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) – Specifies how to handle potential mismatch between the sampling frequency of the sound data self.sound.fs and the sampling frequency of the output stream self.stream.fs. If true, replace self.sound with a copy resampled to the stream’s preferred rate. If false, simply adjust playback speed accordingly (at a small, ongoing, computational cost).

  • 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.

GetDynamic(name)

For dynamic properties, return the actual callable object that generates property values, rather than the current static value.

Parameters:

name (str) – Name of the property

Returns:

Callable object responsible for generating values for the named property (or None if there is no such dynamic).

Example:

import audiomath as am, numpy as np
p = am.Player(am.TestSound('12'), loop=True, playing=True)
p.volume = lambda t: 0.5+0.5*np.sin(2.0*np.pi*t*0.25)
v = p.volume                # this retrieves a float
f = p.GetDynamic('volume')  # this retrieves the lambda
NextTrack(count=1, raiseException=True)

Jump to the beginning of the next track in the queue and return the corresponding Sound instance.

If this entails going beyond the last track, raise an exception unless raiseException=False (in which case go to the beginning of the last track, stop playing, and return None).

Pause(position=None, **kwargs)

Stop playing. Optionally change the head position to the specified absolute position in seconds, ready for subsequent playback. Optionally, use keyword arguments to set additional properties (e.g. volume, pan, levels, speed, loop, etc) at the same time.

Play(position=None, wait=False, **kwargs)

Start playing. Optionally use position to specify the exact head position from which to start playing (in seconds from the beginning of the current sound). Otherwise playack starts from wherever it last ended up (or at the beginning, if the sound previously ran to the end).

If you call p.Play() without an explicit position on an instance p that is already playing, nothing changes—playback simply continues. In this sense it is equivalent to manipulating the playing property by saying p.playing = True.

If wait is true, immediately call Wait() so that playback is synchronous.

Optionally, use further keyword arguments to set additional properties (e.g. volume, pan, levels, speed, loop, track, etc) at the same time.

PreviousTrack(count=1, raiseException=True)

Jump to the beginning of the track prior to the current one in the queue and return the corresponding Sound instance.

If this entails going back beyond the beginning, raise an exception unless raiseException=False (in which case go to the beginning of the first track, stop playing, and return None).

Unlike the “back” button on your music player, this does not go to the beginning of the current track before skipping to the previous track. To restart the current track, use p.Play(0) or p.Seek(0) or p.head = 0

Resample(newFs, wholeQueue=True)

Replace self.sound with a copy resampled at the new sampling rate newFs. Also adjust the Player’s internal speed compensation factor, to ensure things sound the same on the existing stream. The most common usage will be to match the sound sample rate to the sample rate of the hardware (so the Player’s compensation factor becomes 1.0) to ensure more efficient playback.

ResetDynamics(synchronized=None)

To restart the clock from which the dynamic properties of a Player instance p are computed, resetting its “time zero” to the current time:

p.ResetDynamics()

To set the synchronizeDynamics policy at the same time, pass True or False as the input argument.

To synchronize p to a global “time zero” that multiple Player instances can share (hence, necessarily, unsynchronized with respect to the playing/pausing of p itself):

p.ResetDynamics('global')

See also: dynamics, synchronizeDynamics

Seek(position, relative=False)

Move the playback head to the specified position in seconds. Negative values count back from the end of self.sound. If relative is true, position is interpreted as an offset relative to the current head position. The following are equivalent:

p.Seek( 5.0, relative=False )  # more efficient
p.head = 5.0

And the following are also equivalent:

p.Seek( -2.0,  relative=True ) # more efficient
p.head -= 2.0
Set(**kwargs)

Set the values of multiple attributes or properties in one call. An error will be raised if you try to set the value of a non-existent attribute.

Note that the Play() and Pause() methods also allow you to set arbitrary properties prior to their manipulation of playing status.

Returns:

self

Example:

p.Set( head=0, loop=True, volume=0.5 )
SetDynamic(name, func, order=1)

Associate a “dynamic” (i.e. a function that can be called repeatedly to set an attribute) with the name of an attribute.

For example:

foo.Set( 'bar',  lambda t: t ** 2 )

This will set foo.bar to t ** 2 every time the method foo._RunDynamics( t ) is called (this call will happen automatically from the API user’s point of view, in the object’s infrastructure implementation).

If the bar attribute happens to have been already configured as a “dynamic property” this means it supports “dynamic value assignment”, i.e. you can do:

foo.bar = lambda t: t ** 2

as a syntactic shorthand for SetDynamic()—the setter will detect the fact that the value is callable, and divert it to the register of dynamics rather than assigning it directly (so the actual static value you get from querying foo.bar will not immediately change).

Parameters:
  • name (str) – name of the attribute

  • func – callable object, or None to remove any dynamic that may already be associated with name

  • order (int, float) – optional numeric ordering key that allows you to control the serial order in which dynamics are evaluated

Returns:

self

See also: GetDynamic(), GetDynamics(), ClearDynamics()

Stop(position=None, **kwargs)

Stop playing. Optionally change the head position to the specified absolute position in seconds, ready for subsequent playback. Optionally, use keyword arguments to set additional properties (e.g. volume, pan, levels, speed, loop, etc) at the same time.

SwitchToTrack(position, raiseException=True, allowNegative=True)

Immediately jump to the sound at the given position in the queue (equivalent to setting the track property).

Wait()

Sleep until self stops playing, or until the user presses ctrl-C to interrupt.

WaitFor(condition, finalPlayingStatus=None)

Sleep until condition is fulfilled. The condition argument may be:

a Recorder instance:

in which case the method waits until the recorder is no longer recording;

a Player instance:

in which case the method waits until the player is no longer playing;

a string:

in which case the method will print the string as a prompt and wait for the user to enter press ctrl-C on the console;

any callable:

in which case the method will repeatedly call condition() between 1-ms sleeps until it returns anything other than None.

If finalPlayingStatus is not None, then self.playing is set to its value when the wait is over. So for example, you can prompt a user to end ongoing playback as follows:

p.WaitFor('Press ctrl-C to stop playing: ', finalPlayingStatus=False)
property autoAdvance

This property may take the following values:

False:

when the current sound finishes playing, rewind the head to the start of the current sound but do not advance through the queue.

True or 'play':

when the current sound finishes playing, continue to the next Sound instance in the queue, if any.

'pause':

when the current sound finishes playing, advance to the next Sound instance in the queue, if any, but pause playback.

autoAdvance is a dynamic property (see dynamics).

property dynamics

Many Player properties, such as volume, speed, pan, levels and even playing, support “dynamic value assignment”. This means you can do something like the following:

import audiomath as am
p = am.Player(am.TestSound('12'), loop=True)

p.volume = lambda t: min( 1.0, t / 5.0 )

p.Play()

When queried, p.volume still appears to be a floating-point number, but this value changes as a function of time t in seconds—in this example, the volume fades up from 0.0 to 1.0 over the course of five seconds.

Dynamic functions are called automatically in the streaming callback. You should take care to make them as computationally lightweight and efficient as possible. Your functions may accept 0, 1 or 2 arguments. If they accept 1 argument, it is always time in seconds. If they accept 2 arguments, these will be the Player instance first, followed by time in seconds.

Time t may be synchronized with, or independent of, the playing/pausing status of the Player itself, according to the synchronizeDynamics property. “Time zero” may be reset, or synchronized with other players, using ResetDynamics().

Dynamic functions may choose to terminate and remove themselves, by saying raise StopIteration() or even raise StopIteration(final_property_value)

property head

Current playback position, in seconds relative to the beginning.

You can change the playback position by assigning to head, but note that it is fractionally faster/more efficient to do this by calling the Seek() method.

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 loop

This boolean-valued property determines whether the current sound should wrap around to the beginning when it finishes playing (NB: if you are looping, you will stay on the current sound and never advance through the queue).

loop 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 playing

This boolean property reflects, and determines, whether the Player is currently playing or paused.

playing is a dynamic property (see dynamics).

property queue

This special sequence of class Queue provides a list of Sound instances between which the Player may switch.

The Player instance’s repeatQueue and autoAdvance properties allow you to specify how the queue is used when a given Sound finishes playing. The track property allows you to jump immediately to a given place in the queue.

NB: direct manipulation of the queue, for example using its own methods self.queue.Forward() or self.queue.Back(), will not affect whichever self.sound is currently loaded into the Player, but they will affect what happens when that sound has finished playing. If you want to change tracks with immediate effect, you should instead use corresponding Player methods self.NextTrack() or self.PreviousTrack(), or manipulate the property self.track.

property repeatQueue

This boolean property is a shortcut to the sub-property self.queue.loop. If true, then moving beyond either end of the queue causes a wrap-around to the other end.

repeatQueue 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 speed

This floating-point property determines the speed at which the sound should be played. Normal speed is 1.0

speed is a dynamic property (see dynamics).

property synchronizeDynamics

Many Player properties, such as volume, speed, pan, levels and even playing, can be dynamic, i.e. they can be set to functions of time t in seconds.

If p.synchronizeDynamics is true, then the dynamic clock pauses whenever playback pauses and resumes only when playing becomes true again. Hence, if playing itself is dynamic and becomes false, it will not be able to become true again on its own. So this example will only play once:

import audiomath as am
p = am.Player( am.TestSound( '12' ) )
p.Play(
        0,
        loop = True,
        synchronizeDynamics = True,
        playing = lambda t: (t / p.sound.duration) % 2 < 1,
)

If p.synchronizeDynamics is false, then the dynamic clock continues even when playback is paused. Hence, if playing is dynamic, the Player can stop and resume on a schedule. So the same example, with only a change to the synchronizeDynamics setting, will play repeatedly, with pauses:

import audiomath as am
p = am.Player( am.TestSound( '12' ) )
p.Play(
        0,
        loop = True,
        synchronizeDynamics = False,
        playing = lambda t: (t / p.sound.duration) % 2 < 1,
)

A separate but related issue is that of the “time zero” from which dynamic time t is measured. By default, time zero for a given Player instance p is the time at which p was created. The clock can be reset by using the ResetDynamics() method. You can also synchronize p with a global clock, available to all Player instances, by saying p.ResetDynamics('global')

See also: dynamics, ResetDynamics()

property track

If there are multiple items in the queue, this property allows you to switch between them. You may use an integer zero-based index, a Sound instance, or the string label of that instance (provided the instance is already in the queue)

track is a dynamic property (see dynamics).

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).