The tone manager

The tone manager module provides some tone management functionality. Specifically, it can be used to create custom tones, for example a custom ringback tone.

The tone manager is also necessary for some types of call transfer.

To use the tone manager module in an application import it as follows:

from prosody.uas import ToneManager

Create a tone manager and pass it a reference to the logger, for example:

tone_manager = ToneManager(my_log)

The ToneManager can be used to create new tones and add them to the list of known tones. It can also be used to set a new default ringtone.

To set a new default ringtone use the set_default function. It takes a pre-defined tone as the argument:

ToneManager.set_default(ToneManager.PredefinedTones.RINGBACK_EU)

The tone manager has three pre-defined tones RINGBACK_EU, RINGBACK_UK and RINGBACK (which is the US ringtone).

To add to the list of pre-defined tones use the create_tone function. This function allows the user to create a custom tone sequence and add it to the tone manager. The function takes two arguments:

  • tone_info

    a list of tone descriptions.

  • tone_name

    a name to give the new tone sequence

The argument tone_info is a list of tones to play. Each tone in the list is actually a pair of tones that will be combined by either summation or modulation. The pair of tones is described in a tuple that contains the definition for two tones, the duration and the way they are to be added together. And each tone definition is a dictionary containing the information for that tone. See below for an illustration of tone_info:

[
   (
     {
       'frequency' : value,  # frequency in Hz, 0 to 9999
       'amplitude' : value   # negative amplitude in dBm0, 0 to 99
     },
     {
       'frequency' : value,
       'amplitude' : value
     },
     {
       'duration'  : value,  # duration in milliseconds, 0 to 9999
       'add'       : method  # the method used to add the tones, 'summation' or 'modulation'
     }
   ),
   (
      .. next tone pair
   )
]

The following will play a single 400 Hz tone at -10 dBm0 for 1.65 seconds followed by silence for 3.35 seconds:

# create the tone
new_tone_sequence = [ ( { 'frequency' : 400,
                          'amplitude' : 10 },
                        { 'frequency' : 0,
                          'amplitude' : 0  },
                        { 'duration'  : 1650,
                          'add'       : 'summation' } ),
                      ( { 'frequency' : 0,
                          'amplitude' : 0 },
                        { 'frequency' : 0,
                          'amplitude' : 0 },
                        { 'duration'  : 3350,
                          'add'       : 'modulation' } ) ]

# add the tone to the list of known tones
ToneManager.create_tone(new_tone_sequence, 'MY_RINGBACK')

# set the new tone to be the default tone
ToneManager.set_default('MY_RINGBACK')

The tone player

For a channel to be able to use the tones that are configured on the tone manager, it must create a tone player. The create_tone_player function on the call channel will take a reference to the tone manager and create a tone player. This will create the public property TonePlayer on the channel object.

The tone player is used automatically for certain types of call transfer.

class TonePlayer

A class that provides an API for playing tones.

class Cause

Once a play job has ended and the play state has returned to idle or error, the cause will be one of these.

Play termination causes are:

ERROR

play has stopped due to an error.

NORMAL

play has stopped normally.

ABORTED

play has been aborted (probably because stop was called).

HANGUP

play ended due to a call hangup.

TIMEOUT

play failed because a timer has expired.

NONE

play is in progress, or no play has occurred.

Usage example:

state = channel.TonePlayer.state()
if state != channel.TonePlayer.State.PLAYING:
    cause = channel.TonePlayer.cause()
    if cause == channel.TonePlayer.Cause.NORMAL:
        # Tone replay finished normally
        pass
class State

The play state can be checked to determine whether a tone play job is in progress. When a play job ends, the play termination cause can be checked to find the reason why.

Play states are:

PLAYING

a play job is in progress.

IDLE

no play job is in progress.

ERROR

a play job failed.

Usage example:

state = channel.TonePlayer.state()
if state == channel.TonePlayer.State.ERROR:
    # a tone replay job failed due to an error.
    pass
play(tone)

This function will play a tone.

Required argument:
  • tone

    the tone to play.

This function will play a tone, the tone is one of a pre-defined set.

If the call state is IDLE, this function will raise a Hangup exception. If the call state is not IDLE but also not ANSWERED, this function will raise an Error exception.

If the play state is already PLAYING, this function will raise an Error exception.

If the call channel already has an external audio source, e.g, it is connected to another call channel, this function will raise an Error exception.

If the tone provided is not one of the pre-defined set, this function will raise an Error exception. Additional pre-defined tones can be created using the ToneManager API.

The function will block until the tone has finished playing, or a timeout occurs.

This function will return True on success, otherwise False.

Usage example:

cause = channel.TonePlayer.play("RINGBACK_UK")
start(tone, loop=True)

This function will start the playing of a tone.

Required argument:
  • tone

    the tone to play.

  • loop

    if true the tone sequence will loop until told to stop.

This function will start playing a tone, the tone is one of a pre-defined set.

If the call state is IDLE, this function will raise a Hangup exception. If the call state is not IDLE but also not ANSWERED, this function will raise an Error exception.

If the play state is already PLAYING, this function will raise an Error exception.

If the call channel already has an external audio source, e.g, it is connected to another call channel, this function will raise an Error exception.

If the tone provided is not one of the pre-defined set, this function will raise an Error exception. Additional pre-defined tones can be created using the ToneManager API.

The function will block until confirmation has been received that the tone has begun to play, or a timeout occurs.

This function will return True on success, otherwise False.

Usage example:

if channel.TonePlayer.start("RINGBACK_UK") == True:
    # tone playing has begun
    pass
stop()

Stop playback of a tone to the current channel.

Calling this method will abort any current tone playback. However, it is unnecessary to stop any media operation upon call hang-up, as that is done automatically.

If the call state is IDLE, this function will raise a Hangup exception. If the call state is not IDLE but also not ANSWERED, this function will raise an Error exception.

If the play state is not PLAYING, this function will simply return True. Otherwise, this call will block until the playback has stopped or a timeout has expired.

Upon return, this method will return True for success, otherwise False.

Usage example:

# Start playing a tone and then stop it.
if channel.TonePlayer.start("RINGBACK_UK") == True:
    # while the tone is playing, we can do something else
    # ...
    # then stop it.
    channel.TonePlayer.stop()