Skip to content

Sound

Play sounds from your script.

What you get

class Bell
  def on_button(btn)
    if btn == "select" && !sound.playing()
      sound.play({'rtttl': 'bell:d=4,o=5,b=100:e,c'})
    end
  end

  def draw()
    clear()
    text(1, 6, "Bell", 0xFFFFFF)
  end
end

return Bell()

Press select while the app is shown, and the clock plays a short melody. sound.playing() keeps a quick double press from playing it twice.

How it behaves

  • sound.play() returns at once, and the sound plays while your script goes on.
  • true means the clock took the sound, not that you hear it. A name that is not stored plays nothing, and the volume may be 0. false means this clock can play none of it. What the answer means lists every case.
  • Your script plays one sound at a time. Another sound replaces it.
  • An alert is never cut off by a script. The sound of a notification plays over your script's sounds.
  • Start sounds in a button handler or in loop(), never in draw(), which runs about 40 times a second.

What to play

sound.play(x) takes a sound, the same one /api/v1/audio/play takes. In Berry it is a string, a map or a list:

sound.play("doorbell")                           # a stored sound, by name
sound.play({'rtttl': 'beep:d=16,o=6,b=200:c'})   # a melody in the call
sound.play({'track': 3})                         # a DFPlayer track
sound.play({'file': 'siren', 'loop': true})      # repeats until you stop it

A map has exactly one of these keys. The key says what plays:

Key Value Plays
'file' a name such as 'doorbell' a stored melody
'rtttl' an RTTTL melody the melody
'track' a whole number from 1 to 2999 a track from the DFPlayer

Next to the key, a map may have:

  • 'loop': true to repeat the sound until it is stopped.

A plain name is short for {'file': name}. A list holds 1 to 4 sounds, and the clock plays the first one it can play.

One sound after another

sound.playing() lets you wait for one sound to finish before you start the next. A button that plays a sound needs this, or a quick double press plays it twice. See What you get.

Play sounds one after another in loop(), never in draw():

  var queue

  def init()
    self.queue = ["chime", "alarm"]     # sounds waiting to play
  end

  def loop()
    if size(self.queue) > 0 && !sound.playing()
      sound.play(self.queue.pop(0))
    end
  end

Use notify() when the sound belongs to an event that should also interrupt the rotation and show something. Use sound when you only want the sound. A notify() sound is an alert, so it plays over your script's sounds. See Notifications.

Different clocks

Clocks have different sound hardware. Ask sound.can() before you play something only some clocks have:

  def on_button(btn)
    if btn == "select" && !sound.playing()
      if sound.can('track') sound.play({'track': 1})
      else sound.play({'rtttl': 'bell:d=4,o=5,b=100:e,c'}) end
    end
  end

A list does the same in one call. The clock plays the first entry it can play:

sound.play(["doorbell", {'rtttl': 'bell:d=4,o=5,b=100:e,c'}])

sound.can() answers these keys, among others. They are the audio flags of GET /api/v1/capabilities:

Key true when the clock can
'rtttl' play melodies
'track' play DFPlayer tracks

A script that cannot work without one of them says so in its header, for example # @needs audio.rtttl. See Sharing.

A sound that repeats

Your script plays one sound at a time. A sound with 'loop': true repeats until it is stopped or another sound replaces it, an alert too.

Good to know

  • A quick double press plays a sound twice. Check !sound.playing() before you play, as in What you get.
  • Start sounds in a button handler or in loop(), never in draw(). draw() runs about 40 times a second.
  • A sound that repeats keeps playing when your app leaves. Stop it in on_hide() when it belongs to what your app shows.

Details

Calls

Call Does Returns
sound.play(x) plays a sound once. With 'loop': true it repeats until it is stopped true when the clock took it, false when it can play none of it
sound.stop() stops every sound your script plays nil
sound.stop('loop') stops your script's sound that repeats with 'loop': true nil
sound.playing() whether a sound from your script plays right now true or false
sound.can() what this clock can play a map such as {'mp3': true, 'rtttl': true, ...}
sound.can(key) one entry of that map, for example sound.can('track') true or false

What the answer means

true means the clock took the request, and the sound starts right away. true does not mean you hear it:

  • A name that is not stored plays nothing.
  • While an alert plays, for example the sound of a notification, a single sound from your script is not played. An alert is never cut off by a script.
  • The volume may be 0.

false means this clock cannot play any of it, for example {'track': 3} on a clock without a DFPlayer. It never stops your script.

A mistake in the sound raises a value_error at the call, with the key and the reason:

sound.play({'rtttl': 'd=4,o=5,b=120:c,e,g'})
# value_error: rtttl: missing ':' (at offset 19)

A map with two sound keys, an unknown key, a bad name or a track out of range raise the same way. The reasons are listed in When it goes wrong.

Every sound limit is in Limits.