How to Add a Sound Browser¶
Add a filterable, tag-driven sound browser so users can search, filter, and preview sounds from within your product's UI.
Prerequisites¶
- A working IVLS product with sounds to browse
- UI widgets created in your performance view: entry buttons, tag buttons, a scrollbar XY pad, and a scrollbar thumb label
Stl.Scrollbarincluded viaIvls.STL(part of the standard library bundle)- Familiarity with sound browser concepts
Steps¶
1. Instantiate the browser template¶
Create a node that inherits from Stl.Browsers.Template. The template takes four parameters:
| Parameter | Purpose |
|---|---|
#name# |
Namespace prefix for all browser state and functions |
#max_items# |
Maximum number of browsable items |
#num_tags# |
Number of filter tags |
#num_entries# |
Number of visible rows in the browser list |
define NUM_SOUNDS := 200
define NUM_TAGS := 16
define NUM_ENTRIES := 12
node MyProduct.Browser from Stl.Browsers.Template(my.browser, NUM_SOUNDS, NUM_TAGS, NUM_ENTRIES):
cb Init:
{ Declare any product-specific browser state here }
family my
family browser
declare entry_btn[NUM_ENTRIES]
declare tag_btn[NUM_TAGS]
end family
end family
end node2. Implement required hooks¶
Override the hook functions inside your browser node's cb Functions to connect the browser to your product's data and UI.
Load item -- called when the user selects a sound:
function my.browser.hooks.load_item(selected_item) override
{ Load the sound at index selected_item }
my.current_sound := my.sound_ids[selected_item]
call uir.update()
end functionTag state -- reads/writes the tag button UI state:
function my.browser.hooks.get_tag_state(idx) -> state override
state := my.browser.tag_btn[idx] -> value
end function
function my.browser.hooks.set_tag_state(idx, state) override
my.browser.tag_btn[idx] -> value := state
end functionRender entry -- updates how each row looks in the browser list:
function my.browser.ui.hooks.render_entry(entry_idx, state, visible, item_id) override
if item_id > -1
my.browser.entry_btn[entry_idx] -> text := my.sound_names[item_id]
my.browser.entry_btn[entry_idx] -> value := state
else
my.browser.entry_btn[entry_idx] -> text := ""
my.browser.entry_btn[entry_idx] -> value := OFF
end if
my.browser.entry_btn[entry_idx] -> hide := HIDE_WHOLE_CONTROL * (1 - visible)
end functionRender tag -- updates tag button appearance based on availability:
function my.browser.ui.hooks.render_tag(tag_idx, tag_available) override
my.browser.tag_btn[tag_idx] -> text := my.tag_names[tag_idx]
if _true(tag_available)
my.browser.tag_btn[tag_idx] -> picture := "btn-tag"
else
my.browser.tag_btn[tag_idx] -> picture := "btn-tag-dim"
end if
end functionPreview (optional) -- play/stop sound previews:
function my.browser.hooks.play_preview(selected_item, key_modifiers) override
{ Trigger a preview of the sound }
end function
function my.browser.hooks.stop_preview() override
{ Stop any active preview }
end function3. Create the scrollbar node¶
Instantiate Stl.Scrollbar.Template with matching namespace. The scrollbar tracks the browser's filtered item count automatically.
node MyProduct.BrowserScrollbar from Stl.Scrollbar.Template( ...
my.browser.scrollbar, ...
scroll.orient.VERTICAL, ...
my.ui.Browser.ScrollXY, ...
my.ui.Browser.ScrollThumb, ...
NUM_ENTRIES, ...
my.browser.current_items.count):
{ No additional code needed }
end node4. Register both nodes in IVLS_NODES¶
5. Bind UI widgets¶
In your browser node's cb Init or cb PostInit, bind the entry and tag buttons to the browser's built-in UI callbacks:
cb Init:
declare entry
for entry := 0 to NUM_ENTRIES - 1
my.browser.entry_btn[entry] := get_ui_id(my.ui.Browser.Entry[entry])
uicb.Bind(my.browser.entry_btn[entry], my.browser.cb.entry)
end for
declare tag
for tag := 0 to NUM_TAGS - 1
my.browser.tag_btn[tag] := get_ui_id(my.ui.Browser.Tag[tag])
uicb.Bind(my.browser.tag_btn[tag], my.browser.cb.tag)
end for6. Initialize item data¶
In cb FirstLoad, populate the browser's sorted_items and masks arrays with your sound data:
cb FirstLoad:
declare i
for i := 0 to NUM_SOUNDS - 1
my.browser.sorted_items[i] := i
{ Create a Mask for each item's tags }
declare mask := Mask.new()
Mask[mask].part[0] := my.sound_tag_bits[i]
my.browser.masks[i] := mask
end forHook summary¶
| Hook | Required | Purpose |
|---|---|---|
#name#.hooks.load_item |
Yes | Load the selected sound |
#name#.hooks.get_tag_state |
Yes | Read tag button state |
#name#.hooks.set_tag_state |
Yes | Write tag button state |
#name#.ui.hooks.render_entry |
Yes | Draw each browser row |
#name#.ui.hooks.render_tag |
Yes | Draw each tag button |
#name#.hooks.play_preview |
No | Play a sound preview |
#name#.hooks.stop_preview |
No | Stop active preview |
#name#.hooks.fallback_load |
No | Restore cancelled selection |
#name#.hooks.on_entry_dbl_click |
No | Handle double-click on entry |
Verify¶
- Open the browser UI -- entry rows should populate with sound names
- Click a tag button -- the list should filter to matching sounds
- Click an entry -- the sound should load (and preview, if implemented)
- Scroll -- the scrollbar thumb should track the list position
- Click multiple tags -- only items matching all active tags should appear
Further reading¶
- Guide: Sound Browsers for the architecture and filter system
- Guide: UI Callbacks for the
uicb.Bindsystem