| Crates.io | tether-soundscape |
| lib.rs | tether-soundscape |
| version | 0.5.5 |
| created_at | 2024-11-19 16:33:27.443932+00 |
| updated_at | 2025-02-27 16:09:07.079052+00 |
| description | A remote-controllable audio sequencer |
| homepage | https://github.com/RandomStudio/tether-soundscape-rs |
| repository | https://github.com/RandomStudio/tether-soundscape-rs |
| max_upload_size | |
| id | 1453505 |
| size | 354,214 |
A multi-layered audio sequencer, remote-controllable via Tether, to create soundscapes. Runs in a full GUI mode or headless - even on a Raspberry Pi!

Install:
cargo install tether-soundscape
Run, pointing to your sample bank JSON:
tether-soundscape mysoundbank.json
If you have tether-egui installed (cargo install tether-egui), you can test remote control:
tether-egui egui-demo.json
Currently, the Sample Bank JSON files are created "by hand". Later versions will allow creation, editing and saving of these via the GUI. See ./soundbank-demo.json file for an example.
Clips in the Sample Bank may optionally be given a volume and/or panning setting.
If an incoming clipCommands message specifies volume or panning values, then these will override any defaults specified in the JSON.
If neither a JSON-specified value nor a message-specified override is available for one or both of these, a default will be applied (full volume and centred panning).
See Conventions for more detail on how these values are intended to be used.
On the topic +/+/clipCommands
Has the following fields
command (required): one of the following strings: "hit", "add", "remove"
clipName (required): string name for the targetted clipfadeDuration (optional): an integer value for milliseconds to fade in or out (command-dependent)panPosition, panSpread (both optional): if panPosition is specified, this will override any per-clip panning specified in the Sample Bank JSON
panSpread on its own will be ignoredpanPosition on its own will apply a default spread value (0.0)See the Conventions section for more detail on how these values are defined.
On the topic +/+/scenes
Has the following fields
mode (optional, default is "loopAll"): one of the following strings: "loopAll", "onceAll", "onceRandom",clipNames (required): zero or more clip names; if zero are provided, the system will transition to an empty scene (silence all clips)fade_duration (optional): an integer value for milliseconds to transition from current scene to the new oneOn the topic +/+/globalControls
Has the following fields:
command: one of the following:
volume: only used when command is "masterVolume"A project file for Tether Egui is provided in ./egui-demo.json for easy testing of the remote control functions.
Alternatively, use the tether send commands below if using Tether Utils.
Single clip hit:
tether send --plug.name clipCommands --message \{\"command\":\"hit\"\,\"clipName\":\"frog\"\}
Single clip hit, specify panning (ignored if in Stereo Mode):
tether send --plug.name clipCommands --message \{\"command\":\"hit\"\,\"clipName\":\"frog\"\,\"panPosition\":0,\"panSpread\":1\}
Scene with two clips (default mode is "loopAll"):
tether send --plug.name scenes --message \{\"clipNames\":\[\"frog\"\,\"squirrel\"]\}
Scene where system should "pick one random" from the list:
tether send --plug.name scenes --message \{\"mode\":\"random\",\"clipNames\":\[\"frog\"\,\"squirrel\"]\}
Remove single clip
tether send --plug.name clipCommands --message \{\"command\":\"remove\",\"clipName\":\"frog\"\}
Add single clip, custom fade duration
tether send --plug.name clipCommands --message \{\"command\":\"add\",\"clipName\":\"squirrel2\",\"fadeDuration\":5000\}
Scene with zero clips (silence all), custom fade duration:
tether send --plug.name scenes --message \{\"clipNames\":\[\],\"fadeDuration\":500\}
This agent publishes frequently on the topic soundscape/any/state, which can be useful for driving animation, lighting effects, visualisation, etc. in sync with playback. The state messages include the following fields:
isPlaying: whether or not the audio stream is playingclips: an array of currently playing clips (only), with the following information for each:
id (int)name (string)progress (float, normalised to range [0,1])currentVolume (float, normalised to range [0,1])looping (boolean)To minimise traffic, the agent will only publish an empty clip list (clips: []) once and then resume as soon as at least one clip begins playing again.
Discrete events (clip begin/end) are published on the events Plug, e.g. soundscape/any/events. This can be useful for driving external applications that only need to subscribe to significant begin/end events.
volume values are a multiplier, so 0.0 means silence and 1.0 means "full volume". A value > 1.0 will amplify the volume relative to the original source.
panning is separated into two distance keys (in JSON file and/or messages) and a tuple (in Rust, internally) - position followed by spread. These values are meant to be used as follows:
position (panPosition in JSON) is a value in the range [0; output_channel_count - 1]. So, in a 4 channel setup, position 3.0 would be "full right", i.e. loudest in channel 4.
spread (panSpread in JSON) is a multiple of the "width" of a channel. So, 0.0 means that the signal will be as focussed as possible, i.e. "1 channel width".
Minimal memory/CPU footprint for high performance
Cross-platform but without any need to install browser, use Electron, etc.
Full GUI or headless (text-only) modes are possible
Great way to learn about low-level audio sample/buffer control, multi-threading in Rust
+/someGroup/clipCommands rather than the default +/+/clipCommands, and also publish on soundscape/someGroup/stateErr(()) returns with something better, e.g. anyhow crate