Skip to content

The media system

Schedule skybox and music @media labels defined in MAST.

Overview

Media labels are declared in MAST with the @media/kind/path "Display" syntax and discovered at runtime. media_schedule and media_schedule_random look up registered labels by kind ("skybox" or "music") and apply them via the engine's set_sky_box / set_music_folder calls, then run the label as a sub-task.

The ID parameter targets a specific ship or client; 0 (the default) applies the change globally on the server.

Use skybox_schedule / music_schedule as convenient wrappers when you already know the media name; use the _random variants to pick from all registered labels of that kind automatically.

A skybox no longer picks the music

It used to. Scheduling a media label runs its body, and nothing else ever chose a track, so every @media/skybox label ended in if client_id==0: music_schedule_random() — copied into thirty A28 labels, eight LegendaryMissions ones, and every mission that inlined them. A skybox label with an empty body left a game silent.

The two are now independent. A skybox sets the sky; music is selected on its own, and a skybox label body is free for things that are genuinely sky-specific.

Choosing the music

MUSIC_SELECT names a bank — a bare folder name, an @media/music label's display name, or "random". It resolves strongest-first:

Source Beats
var.MUSIC_SELECT= on the engine command line everything
COSMOS_SETTINGS environment JSON the profile and below
profiles/<name>.yaml the mission and below
the mission's settings.yaml mods and the built-in
a mod's settings_set_mod_default("MUSIC_SELECT", ...) the built-in
the library built-in, "random"

At runtime the operator's console dropdown and a map's Defaults: MUSIC_SELECT outrank the value above, in that order.

A mission that pins music in its own settings.yaml locks every mod out of that key — that is what "explicit" means here. Leave it unset unless you mean it.

Discovering what is available

music_get_list() / skybox_get_list() return the labels that are actually usable: a missing folder or a false if condition drops the label, so a picker can never offer something scheduling would refuse. This is what the LegendaryMissions server console builds its Music dropdown from, which is why a mod's banks appear there without that console knowing the mod exists.

music_find(spec) resolves an index, a path, a display name, or an unambiguous substring — the same matcher maps_find uses, so a name means the same thing on a command line, in a settings file and in a dropdown. An ambiguous spec returns None rather than guessing.

End-of-game stings

A bank is not one track: it holds start, main, victory and failure alongside the low/ medium/ high/ tiers. music_play_sting(name) plays one out of the bank that is currently playing:

music_play_sting("victory")

Missions used to write sbs.play_music_file(0, "music/default/victory") — the literal appeared about forty times across the mission repos — and the default in it was hardcoded, so a game scored to Artemis2 or to a mod's soundtrack still ended on the stock sting. The bank was never the mission's to know.

The fallback is per file, not per bank: a mod's bank may legitimately ship its own main and no victory, and losing its whole soundtrack over one missing file would be the wrong trade — so only that one file falls back to default.

Music must be a bare name, and that is the engine's rule

set_sky_box takes a path in any spelling. set_music_folder does not: it resolves a bare name under data/audio/music/, and handing it a path does not fail — it hangs the engine. The call never returns (measured in missions/music_probe, engine 1.3.6).

So a bank shipped in a mod's media pack is found by sbs_utils, and then deliberately not handed over: the label logs a warning naming the folder it found and plays default. Copy the folder into data/audio/music/ to use it today. When an engine build is measured to survive a path, set MUSIC_ENGINE_ACCEPTS_PATHS: true and packs work directly.

Quick example

@media/skybox/nebula "Nebula"
@media/skybox/deep_space "Deep Space"
@media/music/battle "Battle Music"

== setup ==
skybox_schedule_random()
# "", None or "random" picks at random; anything else is resolved by name,
# and a spec that matches nothing warns and falls back rather than going silent.
music_schedule_select(MUSIC_SELECT)
from sbs_utils.procedural.media import (
    skybox_schedule, skybox_schedule_random,
    music_schedule, music_schedule_random, music_schedule_select,
    music_get_list, music_find, music_current,
)

# Pick a specific skybox
skybox_schedule("nebula")

# Pick a random skybox
skybox_schedule_random()

# Whatever MUSIC_SELECT asked for, including "random"
music_schedule_select(settings_get_defaults().get("MUSIC_SELECT"))

# Ship-specific music (pass ship ID)
music_schedule("battle", ID=ship_id)

# What a picker offers, and what is playing now
[(m.path, m.display_name) for m in music_get_list()]
music_current()          # -> the bank name, "default" until something schedules one

API

media_find(kind, spec)

Find one @media label from a loose spec - an index, a path, a display name, or an unambiguous substring of either.

Uses the same matcher as maps_find (maps.label_find_by_spec) so a name typed into a settings file, a launch argument or a dropdown resolves the one way everywhere. An AMBIGUOUS spec returns None rather than guessing.

Parameters:

Name Type Description Default
kind str

"skybox" or "music".

required
spec

index, path, display name, or substring.

required

Returns:

Type Description

MediaLabel | None

media_get_list(kind)

Every usable @media label of a kind, for a picker or a report.

"Usable" is the point: labels whose art or audio folder is missing are dropped, and so are labels whose if condition is false - the same test the random pick applies, so a dropdown can never offer something scheduling would refuse. Sorted by declaration order so a list is stable between runs.

This is the function every mod has been hand-rolling. a28_skyboxes.py, venus_skies.py and a28_verify.py each reach into MediaLabel.folders directly and re-implement the filtering, which is how one of them can quietly disagree with what the game will actually pick.

Parameters:

Name Type Description Default
kind str

"skybox" or "music".

required

Returns:

Name Type Description
list

MediaLabel objects, each with .path and .display_name.

media_play_audio(file, ids_or_obj=0, volume=1.0, pitch=1.0)

Play an audio file NOW - a stinger, a voice line, an alarm.

Promoted out of HereThereBeMonsters, which called sbs.play_audio_file raw in seven places behind its own enable flag. The engine call needs a path relative to the Artemis audio directory, which is what get_mission_audio_file builds - a mission should name its file the way it stores it (audio/briefing_01) and never have to know that.

Parameters:

Name Type Description Default
file str

the file, relative to the mission folder.

required
ids_or_obj int

a client id, or 0 (the default) for everyone.

0
volume float

0-1.

1.0
pitch float

1.0 is unshifted.

1.0

Returns:

Name Type Description
bool

whether the engine was asked to play it. Silent - not an exception - when

there is no engine or the mission disabled audio, because a missing sound must

never end the task that was telling the story.

media_read_relative_file(file)

Read a file sitting beside the .mast that is running - from the addon's zip when that .mast came from a mastlib, else from its folder.

EVERY failure is logged and named. It returns None on failure, and a None flows straight into document_get_amd_file(content=None), which yields an empty tree that renders as a flat, contentless page - a screen that looks broken while saying nothing about why. Reported as: a document whose headings "stopped being recognized", running a mission that gets this addon from a mastlib.

media_schedule(kind, name, ID=0)

Schedule a named @media label of the given kind.

Parameters:

Name Type Description Default
kind str

Media kind, e.g. "skybox" or "music".

required
name str | MediaLabel

Media path name or a MediaLabel object.

required
ID int

Ship or client ID; 0 targets the server. Defaults to 0.

0

Returns:

Type Description

Label | None: The scheduled label, or None if not found.

media_schedule_random(kind, ID=0)

Schedule a randomly chosen @media label of the given kind.

Parameters:

Name Type Description Default
kind str

Media kind, e.g. "skybox" or "music".

required
ID int

Ship or client ID; 0 targets the server. Defaults to 0.

0

Returns:

Type Description

Label | None: The scheduled media label, or None if none exist.

music_bank_has(bank, stinger)

Whether a bank carries a named one-shot ("victory", "failure", ...).

A bank is conventionally start/main/victory/failure.ogg plus low/ medium/ high/, but nothing enforces it, so a mod's bank may legitimately omit one. Asking lets a caller fall back to default for that ONE file instead of abandoning the mod's music.

Parameters:

Name Type Description Default
bank str

a bank name, e.g. from :func:music_current.

required
stinger str

the file, without .ogg.

required

Returns:

Type Description

bool

music_current(ID=0)

The music bank currently playing - the bare folder name last given to the engine.

"default" until something schedules music, because that is what the engine plays.

Parameters:

Name Type Description Default
ID int

ship or client id; 0 (the default) is the server.

0

Returns:

Name Type Description
str

the bank name.

music_find(spec)

Find one music @media label. See :func:media_find.

music_get_list()

Every usable music @media label. See :func:media_get_list.

music_play_sting(name, ids_or_obj=0)

Play a one-shot out of the bank that is CURRENTLY PLAYING - victory, failure, start, main.

Missions had no way to say this. sbs.play_music_file(0, "music/default/victory") is the literal that appears ~40 times across the mission repos, and the default in it is hardcoded - so a game scored to Artemis2, or to a mod's own soundtrack, still ended on the stock sting. The bank was never the mission's to know; it is whatever MUSIC_SELECT, a map, or the operator chose.

Parameters:

Name Type Description Default
name str

the one-shot, without .ogg - "victory", "failure", ...

required
ids_or_obj int

a ship or client id, or 0 (the default) for everyone.

0

Returns:

Name Type Description
bool

whether the engine was asked to play it.

Falls back to the default bank PER FILE, not per bank. A bank is conventionally start/main/victory/failure.ogg plus low/ medium/ high/, but nothing enforces that, so a mod's bank may legitimately ship its own main and no victory - and losing the mod's whole soundtrack over one missing file would be the wrong trade.

music_reset()

Forget which bank is playing, and re-arm the empty-media warning. Called from reset_mission_state - run 2 has its own labels and deserves its own warning.

music_schedule(name, ID=0)

Schedule a specific music track by name.

Parameters:

Name Type Description Default
name str

Music media path name.

required
ID int

Ship or client ID; 0 targets the server. Defaults to 0.

0

music_schedule_random(ID=0)

Schedule a randomly chosen music @media label.

Parameters:

Name Type Description Default
ID int

Ship or client ID; 0 targets the server. Defaults to 0.

0

music_schedule_select(spec, ID=0)

Schedule the music a setting, a map or an operator ASKED for.

This is what replaced "the skybox label picks the music". Every skybox label used to end in if client_id==0: music_schedule_random() - copied into thirty A28 labels, eight LM ones and every mission that inlined them - because scheduling a skybox ran its body and nothing else ever chose a track. A skybox now sets the sky and nothing else.

Parameters:

Name Type Description Default
spec

"", None or "random" picks at random; anything else is resolved by :func:music_find.

required
ID int

ship or client id; 0 (the default) targets the server.

0

Returns:

Type Description

MediaLabel | None: what was scheduled, or None when there is no music at all.

A spec that matches nothing WARNS BY NAME and falls back to random. Silence there was the tempting choice and the wrong one: MUSIC_SELECT: Artmeis2 would play a random track, which is indistinguishable from working.

skybox_find(spec)

Find one skybox @media label. See :func:media_find.

skybox_get_list()

Every usable skybox @media label. See :func:media_get_list.

skybox_schedule(name, ID=0)

Schedule a specific skybox by name.

Parameters:

Name Type Description Default
name str

Skybox media path name.

required
ID int

Ship or client ID; 0 targets the server. Defaults to 0.

0

skybox_schedule_random(ID=0)

Schedule a randomly chosen skybox @media label.

Parameters:

Name Type Description Default
ID int

Ship or client ID; 0 targets the server. Defaults to 0.

0