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 aSound
instance to be played.A
Player
instance can only play oneSound
at a time, but it can optionally maintain a list ofSound
instances in aQueue
and switch between them. For overlapping sounds, use multiplePlayer
instances.A
Player
instance can be created directly from a filename, list of filenames or filename glob pattern. The currentSound
instance is available in thesound
property and the completeQueue
is available in thequeue
property.It is better to create a
Player
instance than to rely on the quick-and-dirtyPlay()
methods of theSound
andQueue
classes—these methods just create aPlayer
(entailing some computational overhead), wait synchronously for it to finish, and then destroy it again. Creating your ownPlayer
instance provides much greater flexibility and performance.- Parameters:
sound (str, Sound, Queue, None) –
Sound
instance to play (or sequence ofSound
instances in alist
orQueue
). Alternatively, supply any argument that is also a valid input to theSound
orQueue
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-openStream
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 firstStream
(after that,Player
instances may share an openStream
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 inPORTAUDIO.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 inPORTAUDIO.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 firstStream
(after that,Player
instances may share an openStream
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 streamself.stream.fs
. If true, replaceself.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 bySetDefaultVerbosity()
, if any.**kwargs – passed through to
Set()
to initialize properties of thePlayer
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 correspondingSound
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 returnNone
).
- Pause(position=None, **kwargs)
Stop playing. Optionally change the
head
position to the specified absoluteposition
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 exacthead
position from which to start playing (in seconds from the beginning of the currentsound
). 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 explicitposition
on an instancep
that is already playing, nothing changes—playback simply continues. In this sense it is equivalent to manipulating theplaying
property by sayingp.playing = True
.If
wait
is true, immediately callWait()
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 correspondingSound
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 returnNone
).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)
orp.Seek(0)
orp.head = 0
- Resample(newFs, wholeQueue=True)
Replace
self.sound
with a copy resampled at the new sampling ratenewFs
. 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
instancep
are computed, resetting its “time zero” to the current time:p.ResetDynamics()
To set the
synchronizeDynamics
policy at the same time, passTrue
orFalse
as the input argument.To synchronize
p
to a global “time zero” that multiplePlayer
instances can share (hence, necessarily, unsynchronized with respect to the playing/pausing ofp
itself):p.ResetDynamics('global')
See also:
dynamics
,synchronizeDynamics
- Seek(position, relative=False)
Move the playback
head
to the specifiedposition
in seconds. Negative values count back from the end ofself.sound
. Ifrelative
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()
andPause()
methods also allow you to set arbitrary properties prior to their manipulation ofplaying
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
tot ** 2
every time the methodfoo._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 queryingfoo.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 withname
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 absoluteposition
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 thetrack
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. Thecondition
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 thanNone
.
If
finalPlayingStatus
is notNone
, thenself.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)
- a
- property autoAdvance
This property may take the following values:
False
:when the current
sound
finishes playing, rewind thehead
to the start of the current sound but do not advance through thequeue
.True
or'play'
:when the current
sound
finishes playing, continue to the nextSound
instance in thequeue
, if any.'pause'
:when the current
sound
finishes playing, advance to the nextSound
instance in thequeue
, if any, but pause playback.
autoAdvance
is a dynamic property (seedynamics
).See also
- property dynamics
Many
Player
properties, such asvolume
,speed
,pan
,levels
and evenplaying
, 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 timet
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 thePlayer
itself, according to thesynchronizeDynamics
property. “Time zero” may be reset, or synchronized with other players, usingResetDynamics()
.Dynamic functions may choose to terminate and remove themselves, by saying
raise StopIteration()
or evenraise StopIteration(final_property_value)
See also
synchronizeDynamics
,ResetDynamics()
,GetDynamic()
,SetDynamic()
- 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 theSeek()
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
andpan
- 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 thequeue
).
- property muted
This is a boolean property that, if true, causes the
Player
to be silent, independent of the currentvolume
and/orlevels
.
- property norm
For a
Player
instancep
, if you setp.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- channellevels
settings.
- property playing
This boolean property reflects, and determines, whether the
Player
is currently playing or paused.
- property queue
This special sequence of class
Queue
provides a list ofSound
instances between which thePlayer
may switch.The
Player
instance’srepeatQueue
andautoAdvance
properties allow you to specify how the queue is used when a givenSound
finishes playing. Thetrack
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()
orself.queue.Back()
, will not affect whicheverself.sound
is currently loaded into thePlayer
, 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 correspondingPlayer
methodsself.NextTrack()
orself.PreviousTrack()
, or manipulate the propertyself.track
.See also
autoAdvance
,repeatQueue
,track
,NextTrack()
,PreviousTrack()
- property repeatQueue
This boolean property is a shortcut to the sub-property
self.queue.loop
. If true, then moving beyond either end of thequeue
causes a wrap-around to the other end.repeatQueue
is a dynamic property (seedynamics
).See also
- property sound
A reference to the
Sound
instance that thePlayer
is currently playing, orNone
. You can change it on-the-fly. It may also change automatically when the current sound finishes playing, if you have appended additionalSound
instances thequeue
and have set theautoAdvance
property.
- property speed
This floating-point property determines the speed at which the sound should be played. Normal speed is 1.0
- property synchronizeDynamics
Many
Player
properties, such asvolume
,speed
,pan
,levels
and evenplaying
, can be dynamic, i.e. they can be set to functions of timet
in seconds.If
p.synchronizeDynamics
is true, then the dynamic clock pauses whenever playback pauses and resumes only whenplaying
becomes true again. Hence, ifplaying
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, ifplaying
is dynamic, thePlayer
can stop and resume on a schedule. So the same example, with only a change to thesynchronizeDynamics
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 givenPlayer
instancep
is the time at whichp
was created. The clock can be reset by using theResetDynamics()
method. You can also synchronizep
with a global clock, available to allPlayer
instances, by sayingp.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, aSound
instance, or the stringlabel
of that instance (provided the instance is already in thequeue
)track
is a dynamic property (seedynamics
).See also
autoAdvance
,queue
,repeatQueue
,NextTrack()
,PreviousTrack()
- 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
andpan