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.
1. Getting the Resound API
2. Creating and registering albums
3. Understanding tag expressions
4. Built-in tags
5. Extending the tag system
6. Playback API
7. Observer API
8. Configuration API
9. Extension recipes
10. API summary and compatibility notes
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.
[dependencies]
depid1=resound
depvs1=4.1.0
depmx1=4.9.9
Use a version range appropriate for the API surface your plugin requires.
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.
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.
[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.
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.
|
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.
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.
|
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.
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.
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.
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.
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.
|
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. |
|
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. |
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.
|
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. |
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.
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.
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.
|
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. |
{
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.
{
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().
Observers let another plugin receive selected Resound events without hooking the engine or notification module.
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.
resound.release_observer("my_plugin_resound_observer")
Observers receive every observer trigger Resound chooses to expose. Filter event_trigger before assuming the shape of data.
Callbacks are protected with pcall; a failing observer does not interrupt Resound playback.
An observer is deregistered after more than three lifetime callback failures.
As of 4.1.0, the public trigger is 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
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. |
|
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. |
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
resound.tags.register_scanning_tag(function()
local active = my_environment_test()
resound.tags.toggle_tag("mytag", active)
end)
-- Album:
-- tags==mytag
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)
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.
|
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 |
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
|
Trigger |
data |
|
track_started |
Compiled playback entry for the song that has just started. |
Depend on the Resound class and public API, not internal module filenames or private tables.
Use a custom tag rather than redefining a built-in tag when your behavior has different semantics.
Use observers for reactions to playback instead of replacing Resound's notification or engine callbacks.
Use revalidate_current_song() when an external event changes tag validity and waiting for the normal scanner rotation is undesirable.
Treat undocumented internal return structures and private fields as unstable unless they are described in this document.
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.