Sound

A world that makes no noise feels like a screenshot. One line of Python fixes that, and nothing about a sound can be got wrong the way a misplaced block can — it plays, it ends, and the map is exactly as it was.

Start here

>>> lt.sound.play("miney_power_up_1")
<Luanti PlayingSound "sound-1">

Everybody hears it, equally loudly, wherever they are. Give it a place and it behaves like a real sound instead — quieter the further away you stand:

lt.sound.play("miney_power_up_1", point=Point(10, 20, 30))

Sounds Miney brings along

miney_power_up_1 works on any server, in any game, because Miney’s mod ships it. 47 of them come along: lasers, zaps, power-ups, beeps and chimes, all named miney_ something and all listed under lt.assets.sounds like every other sound the server knows.

lt.sound.play(lt.assets.sounds.miney.laser_3)
lt.sound.play(lt.assets.sounds.miney.zap_two_tone)
lt.sound.play(lt.assets.sounds.miney.three_tone_1)

Nine lasers, twelve power-ups and five of most other things are numbered, so picking one at random is a line of ordinary Python:

import random

lt.sound.play(f"miney_laser_{random.randint(1, 9)}")

Credit

These are Digital Audio by Kenney Vleugels, from kenney.nl, released under CC0 — public domain, free to use in anything you build. Kenney asks for nothing and gets a thank you anyway.

Finding a name

Every game brings its own sounds too, and there is no way to guess one of those: "mcl_portals_open" exists in VoxeLibre and nowhere else. lt.assets.sounds has every name this server knows, sorted by the mod it came from and discoverable with TAB:

lt.sound.play(lt.assets.sounds.mcl_portals.open)

Note

A sound name is not a file name. default_dig_stone is played by that name, while the game may hold it as default_dig_stone.ogg or as a whole set of default_dig_stone.0.ogg to .9.ogg — one of which is picked at random each time, so the same footstep never sounds quite the same twice.

Who hears it, and where it comes from

Two parameters sound alike and are not:

  • playerwho hears it. Only that one person, and nobody standing next to them.

  • followwhere it comes from. The sound travels with that player, and everybody in earshot hears it move.

lt.sound.play("miney_zap_1", player=lt.players.Steve)   # in Steve's ears
lt.sound.play("miney_zap_1", follow=lt.players.Steve)   # from Steve

They combine, so one player can hear a sound that another player carries.

Music

loop=True is what turns a sound into a soundtrack — and it is the one thing here that keeps going after your script has stopped:

music = lt.sound.play("miney_low_random", loop=True, gain=0.4)
# ... the whole show ...
music.fade_out(3)

A looped miney_ sound makes a hum or a pulse, because they are all short effects. Real music is a file of your own, and that is the next section.

Important

Somebody has to end a loop: fade_out(), stop(), or lt.sound.stop_all() for everything at once. Miney’s mod stops what your session left playing when the session ends, so a forgotten loop does not haunt the world forever — but inside a long script it is yours to stop.

fade_out() is the kinder one. Cutting music off dead is something a player notices.

Your own music

Any Ogg file on your computer goes into the world the same way a picture does:

from pathlib import Path

fanfare = lt.assets.upload(Path("fanfare.ogg"))
lt.sound.play(fanfare)

lt.assets.upload() hands back miney_3f9a1c7b2e04.ogg and play() drops the .ogg for you, so the name goes straight from one to the other. It also waits until the file has really reached the players, so the next line can use it.

A moment worth hearing

Everything together:

import miney
import time

lt = miney.Luanti()
point = lt.players[0].position

music = lt.sound.play(lt.assets.sounds.miney.low_random, loop=True, gain=0.3)

for i in range(5):
    lt.sound.play("miney_power_up_1", point=point, pitch=1 + i / 4, distance=16)
    lt.particles.spawn(point + miney.Point(0, 2, 0), color="#ffcc00", amount=200)
    time.sleep(0.5)

music.fade_out(2)
class miney.Sound(luanti: Luanti)[source]

Noise: a click, a chime, or music that plays until you stop it.

Reached through lt.sound and never created directly:

>>> lt.sound.play("miney_power_up_1")
<Luanti PlayingSound "sound-1">

A sound is named, not filed: "default_dig_stone" is a name the game answers to, and lt.assets.sounds is where to find one with TAB. Given a Point it comes from that place and fades with distance; without one it is equally loud everywhere, which is what music wants.

Every name beginning with miney_ comes with Miney’s own mod and works in any game - 47 CC0 effects by Kenney Vleugels, so an example has something to play before you have gone looking for a name.

Important

A sound with loop=True keeps playing after your script has ended. Somebody has to stop it - PlayingSound.stop(), stop_all(), or your session ending, because Miney’s mod clears out what a connection left behind.

play(name: str, *, gain: float = 1.0, pitch: float = 1.0, loop: bool = False, point: Point | None = None, player: Player | str | None = None, follow: Player | str | None = None, fade: float = 0.0, start: float = 0.0, distance: float | None = None, **extra: Any) PlayingSound[source]

Play a sound.

  1. Everywhere, once:

    >>> lt.sound.play("miney_power_up_1")
    
  2. At a place, so it gets quieter the further away you are:

    >>> lt.sound.play("miney_power_up_1", point=Point(10, 20, 30))
    
  3. Music, until you stop it:

    >>> music = lt.sound.play("miney_low_random", loop=True, gain=0.4)
    >>> music.fade_out(3)
    
  4. In one player’s ears only, half as deep:

    >>> lt.sound.play("miney_power_up_1", player=lt.players.Steve, pitch=0.5)
    
  5. Travelling with a player, for everybody to hear:

    >>> lt.sound.play("miney_power_up_1", follow=lt.players.Steve)
    
  6. Your own:

    >>> lt.sound.play(lt.assets.upload(Path("fanfare.ogg")))
    

player and follow sound alike and are not: player is who hears it, follow is where it comes from. They combine, so one player can hear a sound that another player carries.

Parameters:
  • name – The sound, from lt.assets.sounds or from lt.assets.upload(). A trailing .ogg is dropped for you.

  • gain – How loud, where 1 is the sound’s own level. Above 1 a placed sound does not get louder, it gets further - the volume is measured three blocks away.

  • pitch – How deep. 0.5 is an octave down, 2 an octave up.

  • loop – Start again at the end, for ever. Needs PlayingSound.stop().

  • point – Where it comes from. Without one it is equally loud everywhere and follows nobody, which is what music wants.

  • player – Only this player hears it. A Player or a name.

  • follow – The sound travels with this player. A Player or a name. Cannot be combined with point.

  • fade – Fade in over this long, in gain per second. 0.5 takes two seconds to reach gain.

  • start – Start this many seconds into the sound instead of at the beginning.

  • distance – How far away it can still be heard, in blocks. Luanti’s own answer is 32. Needs point or follow.

  • extra – Anything else Luanti’s sound parameter table takes, passed straight through - exclude_player, for example. A name the engine does not know is ignored without a word, so check it against Luanti’s lua_api.md if nothing happens.

Returns:

A PlayingSound, to stop it with.

Raises:
  • TypeError – If a value is of the wrong kind.

  • ValueError – If a number is out of range, if point and follow are given together, if distance has no place to measure from, or if extra repeats something a parameter above already says.

stop_all() None[source]

Stop every sound this session started.

The one line to put at the end of a show, so nothing keeps playing after it.

lt.sound.stop_all()
class miney.PlayingSound(sound: Sound, key: str, gain: float)[source]

A sound that is playing, and the way to end it.

You never make one of these - Sound.play() hands it back:

>>> music = lt.sound.play("miney_low_random", loop=True)
>>> music.fade_out(3)

It is also a context manager, which is the tidier way to say the same thing:

>>> with lt.sound.play("miney_low_random", loop=True):
...     build_the_thing()
...                                 # silent again on the way out, come what may
fade_out(seconds: float = 1.0) None[source]

Turn it down to nothing over this many seconds, then stop it.

Kinder than stop() for anything a player has been listening to:

>>> music = lt.sound.play("miney_low_random", loop=True, gain=0.5)
>>> music.fade_out(3)
Parameters:

seconds – How long the fade takes. Must be more than 0 - to end it at once use stop().

Raises:

ValueError – If seconds is 0 or less.

gain

How loud it was started.

key

How the server knows this sound.

stop() None[source]

Stop it now.

Stopping one twice is not an error, and neither is stopping one that finished on its own - only a sound made with loop=True really needs this.

music = lt.sound.play("miney_low_random", loop=True)
# ... a while later ...
music.stop()