Resound API

Developer and Album Author Documentation

Target: Resound 4.1.0

Resound is a Vanilla+-styl e music framework for Vendetta Online. It recreates the game's environment-aware music behavior while allowing other mods to register new albums, define new environmental rules, observe playback, and request playback changes through a public LME interface.

Scope: This document describes the public interfaces and album format exposed by Resound 4.1.0. Internal tables and module files are implementation details and should not be hooked, replaced, or accessed directly.


Contents

1. Getting the Resound API

Resound exposes its supported functionality through its LME class. External LME plugins should declare Resound as a dependency and obtain the public class rather than reading or replacing Resound modules.

Dependency declaration

[dependencies]
depid1=resound
depvs1=4.1.0
depmx1=4.9.9

Use a version range appropriate for the API surface your plugin requires.

Obtaining the class

local res_ver = lib.get_latest("resound", "4.1.0", "4.9.9")
local resound = lib.get_class("resound", res_ver)

Boundary: If functionality is not exposed through the returned public table, do not assume Resound's private module layout, load order, filenames, or internal tables are stable API.

2. Creating and Registering Albums

A Resound album is an INI file. The [resound] section describes the album, followed by consecutively numbered song sections beginning at [1]. Resound stops reading songs at the first numbered section that does not contain a file key.

Album INI example

[resound]
api=3
album=my_album
description=Example music album
a
uthor=Example Author

[1]
file=plugins/MyAlbum/music/example.ogg
title=Example Track
artist=Example Composer
tags=ambient
vol=100
length=183000

[2]
file=plugins/MyAlbum/music/danger.ogg
title=Danger Track
artist=Example Composer
tags==danger
length=201000

Album key

Meaning

api

Album format API version. Resound 4.1.x currently uses API 3. This value is reserved for compatibility with future changes to the album registration format.

album

Required unique album ID. Used by Resound's registry and album enable/disable state.

description

Optional description.

author

Optional album author/maintainer.

Song key

Meaning

file

Required. Path passed to Vendetta Online's music playback system.

title

Optional display title. If omitted, Resound uses the file path as the song name.

artist

Optional per-track metadata retained in the album song table.

tags

Optional Resound tag expression. Empty expressions are valid everywhere.

vol

Optional playback volume, interpreted as 0-100.

length

Optional track duration in milliseconds. Used to schedule end-of-track behavior.

Contiguous numbering: Song sections must be [1], [2], [3], and so on. A missing file entry ends album ingestion; later numbered sections will not be discovered.

Registering an album

local ok, err = resound.new_album("plugins/MyAlbum/MyAlbum.ini")
if not ok then
print("Resound album registration failed: " .. tostring(err))
end

new_album(path) returns true on success. On failure it returns false plus a short error string, such as a missing album key or an album with zero songs.

If you are publishing a standard Resound album, the registration code above and the tag behavior described in Chapter 3 are normally all you need. The remaining public API is primarily intended for album management, playback integration, observers, or cust om tag behavior.

Album management and inspection

Function

Returns / behavior

toggle_album(album_id, state)

Enables/disables an album. state may be boolean or YES/NO-like text. Rebuilds the active playlist.

get_album_state(album_id)

Returns the stored enabled state, normally "YES" or "NO".

get_album_list()

Returns an array of registered album IDs.

get_album_data(album_id)

Returns the album table, including path, metadata, enabled state, track_count, and songs.

get_album_song_list(album_id)

Returns the raw song table array for an album.

get_song_data(album_id, song_id)

Returns one raw registered song table.

find_song(album_id, song_name)

Finds a raw song table by exact title within an album.

Raw versus compiled entries: Album/song inspection functions return registration data. The playback engine uses a separate compiled playlist entry table.

3. Understanding Tag Expressions

Tags decide whether a song is valid in the current environment. Resound compiles each song's tags string into one or more groups, then evaluates those groups against the current tag state.

Basic token syntax

Form

Meaning

tag or =tag

Required. The tag/function must evaluate true.

-tag

Excluded. The group fails if this tag/function evaluates true.

+tag

Optional. If true, it can make the group valid.

tag:arg1:arg2

Argument-based tag function. Prefix rules apply the same way.

Commas, semicolons, and whitespace can separate tokens. Tag names are normalized to lowercase alphanumeric/underscore form.

Groups are OR branches

tags=[ambient -danger] [station]

The complete expression is true if any bracketed group is true. If no brackets are used, the entire string is treated as one group.

Required, excluded, and optional behavior

Within one group, all required tags must pass and no excluded tag may be active. A passing required tag makes the group meaningfully valid. Optional tags provide an alternative way for the group to become valid.

Negative-only groups: A group containing only excluded (-) conditions never becomes valid by itself; it needs a required or passing optional condition.

Forced tags

Some environmental rules use require_tag(). A forced tag is not merely active: every candidate group's expression must structurally mention that tag name. This is how Resound can require a class of music during conditions such as danger or combat.

-- If danger is forced, a group that never mentions danger cannot match.
tags=ambient

-- This group can participate while danger is forced.
tags=+danger,=ambient

O ptional +tag entries normally matter only when they are the condition that makes a tag group valid. If a group already contains passing required =tag conditions, ordinary +tag entries usually have no additional effect. The important exception is a tag that Resound has environmentally forced with require_tag(): the group must structurally mention that forced tag, and a +tag entry is enough to satisfy that structural requirement. +danger can matter beside =ambient not because optional tags are generally additive, but because forced-tag presence is checked separately from normal truth evaluation.

4. Built-in Tags

Resound 4.1.0 provides a vanilla-style core tag set. Optional non-vanilla tags are loaded when the adv_tags configuration value is YES.

Vanilla-style tags

Tag

Type

Meaning

alignment tag

Entry

The current system alignment returned by Resound's alignment helper is enabled as a tag name.

station

Entry

Active when GetSectorMonitoredStatus() is greater than 2.

ambient

Entry

Active when GetSectorMonitoredStatus() is less than 3.

distant

Scanning

Active when the nearest high-mass object is farther than 3000m, or the reported distance is negative.

danger

Scanning / forced

Hostile player/NPC detected by FriendlyStatus. While active, danger is structurally required.

sector:<id>

Tag function

True when the current sector ID matches the supplied ID.

system:<id>

Tag function

True when the current system ID matches the supplied ID.

never

Tag function

Always false.

Optional non-vanilla tags

Tag

Type

Current 4.1.0 behavior

combat

Scanning / forced

Hostile within configured combat distance, or recent combat within the reset window. PLAYER_GOT_HIT refreshes recent-combat state.

inner

Entry

Current system ID <= 30.

outer

Entry

Current system ID > 30.

capital

Entry

True in system IDs 1, 9, 19, 22, or 31.

deneb

Entry

True in system ID 4.

playerfound:<name>

Tag function

True if a sensed player exactly matches the supplied name.

crowded:<n>

Tag function

True when player count in a sector is above n.

random:<n>

Tag function

True on a 1-in-n random roll each time it is evaluated.

mining

Tag function

Reserved/TODO in 4.1.0; currently always false.

docked

Tag function

Reserved/TODO in 4.1.0; currently always false.

racing

Tag function

Reserved/TODO in 4.1.0; currently always false.

usingship:<name>

Tag function

Reserved/TODO in 4.1.0; currently always false.

5. Extending the Tag System

Lua extensions can add environment rules without replacing Resound's tag engine. Prefer a unique tag name when your semantics differ from Resound's built-in tags.

Reading and changing live tags

Function

Behavior

tags.get_tag(name)

Returns whether a tag is currently active. Forced tags report true.

tags.toggle_tag(name, status)

Sets or toggles ordinary live tag state. A real state change schedules later current-song revalidation.

tags.require_tag(name)

Activates and structurally forces a tag. Songs must mention the forced tag in a candidate group.

tags.unrequire_tag(name)

Removes forced status. It does not automatically turn the ordinary live tag off.

tags.clear_all_tags()

Clears ordinary and forced tag state. This is global/destructive; use sparingly.

tags.list_current_tags()

Returns the live sector-tag table.

Entry and scanning callbacks

resound.tags.register_entry_tag(function()
-- Runs when Resound rebuilds environment tags on
sector entry.
resound.tags.toggle_tag("my_region", some_test())
end)

resound.tags.register_scanning_tag(function()
-- Runs periodically as part of Resound's scanner rotation.
resound.tags.toggle_tag("my_dynamic_state", some_other_test())
end)

No unregister API: 4.1.0 does not expose an unregister function for entry/scanning callbacks. If your behavior may be disabled at runtime, register one stable callback and gate its work internally.

Argument-based tag functions

resound.tags.register_tag_func("hascargo", function(args)
local item = args and args[1]
return item and my_plugin_has_cargo(item) or false
end)

-- Album usage:
-- tags=hascargo:PremiumWidget

register_tag_func(tagname, handler) installs one handler for the normalized tag name. Registering another handler under the same name replaces the previous function. unregister_tag_func(tagname) removes it.

Immediate revalidation

Normal tag mutations mark the current playback state dirty; Resound consumes that dirty state after the scanning cycle. Event-driven extensions that need an immediate reaction can call:

resound.revalidate_current_song()

The function returns true if the current song remains valid. If there is no current song or the current song is invalid, Resound requests a seek and returns false.

6. Playback API

Function

Behavior

request_seek()

Requests the next song valid under the current playlist and tags. If nothing is valid, Resound waits and retries.

revalidate_current_song()

Immediately tests the current compiled tag expression and seeks only if the current song is invalid.

get_currently_playing()

Returns the current compiled playlist entry, or nil.

get_playhead()

Returns timing information for the current song, or false if nothing is current.

stop()

Stops Resound playback by switching to its silent track.

play(entry)

Directly passes a playlist entry to the engine. Intended for a compiled entry, not raw get_song_data() output.

Compiled playback entry

{
album_id = "album_id",

id = "1",

song_id = "1",

name = "Track title",
file = "path/to/music.ogg",
compiled = <compiled tag expression>,
length_ms = 183000
}

Observer/song ID: Compiled playback entries expose both id and song_id, which contain the same registered song ID. id is shared with raw album song entries, while song_id is retained as a descriptive alias for compiled entries.

Playhead table

{
started_at = <game time in ms>,
elapsed = <milliseconds>,
song_length = <milliseconds>,
has_valid_len = true/false,
remaining = <milliseconds>,
percentage = <elapsed percentage>
}

Unk nown or non-positive registered song lengths are treated as having no valid duration; Resound does not schedule an automatic end for those tracks. Playback will loop based on standard game client rules defined by the functionality of gksound.GKPlayMusic().

7. Observer API

Observers let another plugin receive selected Resound events without hooking the engine or notification module.

Registering an observer

local function on_resound_event(event_trigger, data)
if event_trigger ~= "track_started" then
return
end

print("Resound started: " .. tostring(data.name))
end

resound.register_observer("my_plugin_resound_observer", on_resound_event)

register_observer(id, func) returns true on success. Registering the same ID again replaces the prior observer.

Releasing an observer

resound.release_observer("my_plugin_resound_observer")

Observer contract

track_started

C urrently the only trigger exposed in v4.1.0, but more will be implemented later. Fired after Resound commits the new current song, begins GK music playback, and schedules its end timer. data is the compiled playback entry shown in the Playback API section.

local function credits(event_trigger, data)
if event_trigger ~= "track_started" then
return
end

local album = resound.get_album_data(data.album_id)
local ini_path = album and album.path
local song_id = data.id

if ini_path then
local artist = gkini.ReadString2(
song_id, "artist", "unknown artist", ini_path
)
print("\tCreated by: " .. artist)
end
end

8. Configuration API

Resound's configuration values are exposed for inspection and controlled changes. Values are stored as strings.

Function

Behavior

get_config_list()

Returns an array containing the available configuration keys.

get_config(key)

Returns the current value for a key.

set_config(key, value)

Updates an existing key in memory. Does not save it by itself.

save_config()

Writes all current Resound configuration values to gkini.

load_config()

Reloads all known Resound configuration values from gkini.

4.1.0 configuration keys

Key

Default

Meaning

enable_resound

YES

Allows Resound to handle background music playback.

tick_time

10

Seconds between full scanner rotations.

adv_tags

YES

Loads Resound's optional non-vanilla tag module. Requires reload to change loaded module behavior.

do_notifs

NO

Enables Resound's LME notification output.

do_chat

YES

Prints Resound track-change messages in chat.

repeat_songs

NO

Repeats known-length songs instead of seeking when their registered duration ends.

shuffle_every

5

Rebuilds/shuffles the flattened playlist after this many seek attempts.

sector_change_behavior

change

change = vanilla-like reroll on sector change; continue = preserve playback and revalidate in the new sector.

9. Extension Recipes

Recipe: register a simple album plugin

local res_ver = lib.get_latest("resound", "4.1.0", "4.9.9")
local resound
= lib.get_class("resound", res_ver)

local my_path = lib.get_path() or "plugins/MyAlbum/"
local ok, err = resound.new_album(my_path .. "MyAlbum.ini")

if not ok then
lib.log_error("[MyAlbum] Unable to register Resound album: " .. tostring(err), 3)
end

Recipe: custom scanning tag

resound.tags.register_scanning_tag(function()
local active = my_environment_test()
resound.tags.toggle_tag("mytag", active)
end)

-- Album:
-- tags==mytag

Recipe: event-driven tag with immediate playback reaction

RegisterEvent(function()
resound.tags.toggle_tag("mycombat", true)
resound.tags.require_tag("mycombat")
resound.revalidate_current_song()
end, "PLAYER_GOT_HIT")

When the condition ends, remove forced status and explicitly turn off ordinary state if that is your desired semantics:

resound.tags.unrequire_tag("mycombat")
resound.tags.toggle_tag("mycombat", false)

Recipe: add extra track metadata in chat

local function extra_track_info(event_trigger, data)
if event_trigger ~= "track_started" then
return
end
if resound.get_config("do_chat") ~= "YES" then
return
end

local album = resound.get_
album_data(data.album_id)
if not album or not album.path then
return
end

local artist = gkini.ReadString2(
data.song_id, "artist", "unknown artist", album.path
)
local game = gkini.ReadString2(
data.song_id, "ga
me", "", album.path
)

if game == "" then
print("\tCreated by: " .. artist)
else
print("\tRemix by: " .. artist .. " | Game: " .. game)
end
end

resound.register_observer(
"my_extended_track_info",
extra_track_info
)

Custom INI keys: Resound only ingests its known song keys. An extension may still read additional known keys directly from album.path with gkini.ReadString2(). Vendetta Online's INI API requires the caller to know the key name.

10. API Summary and Compatibility Notes

Top-level public API

Category

Functions

Album

new_album, toggle_album, get_album_state, get_album_list, get_album_data, get_album_song_list, get_song_data, find_song

Playback

request_seek, play, stop, revalidate_current_song, get_currently_playing, get_playhead

Observers

register_observer, release_observer

Config

get_config_list, get_config, set_config, save_config, load_config

resound.tags API

get_tag, toggle_tag, require_tag, unrequire_tag, clear_all_tags, register_entry_tag, register_scanning_tag, register_tag_func, unregister_tag_func, list_current_tags

Observer triggers in 4.1.0

Trigger

data

track_started

Compiled playback entry for the song that has just started.

Compatibility guidance

Version target

This document targets Resound 4.1.0. Future releases may add public observer triggers, tag functions, or API methods. Plugins should declare a compatible version range and should not infer new API from Resound's private implementation.