Summary
Let users browse and download .nam models directly from TONE3000 (formerly ToneHunt) inside the app, instead of downloading files manually in a browser and dropping them into the NAM models folder. TONE3000 hosts the largest community library of NAM captures and now exposes an official REST API (v1) for exactly this.
This is a feature for the NAM stage (shipped in #243). Models would be fetched into the existing NAM models folder, then picked up by the existing rescan/registry path — so once a file lands on disk, everything downstream already works.
Why
- The NAM stage already loads models from a user folder, but acquiring models is fully manual (browse tone3000.com, download, move file, rescan).
- An in-app browser turns "find a tone" into a first-class flow and showcases the NAM feature.
- Fits the v0.6.0 "Neural & Community" roadmap milestone.
TONE3000 API (v1) — what we'd integrate
Base URL: https://www.tone3000.com/api/v1. Integration guide / demo app: https://github.com/tone-3000/api
Relevant endpoints:
GET /tones/search — search & filter tones (params: query, gears, sort e.g. Trending). Heavily rate-limited.
GET /tones/{id} — single tone metadata.
GET /models?tone_id={id} — list models for a tone; each model object carries a model_url for the actual file download.
GET /tones/created, GET /tones/favorited — the logged-in user's own/favorited tones.
GET /user — authenticated profile.
Auth:
- API keys — publishable key (
t3k_pub_…, client id) and a server-only secret key (t3k_cs_…).
- OAuth 2.0 + PKCE for user-facing access, with three flows: Select (user browses & picks a tone in TONE3000's own UI via
oauth/authorize?prompt=select_tone), Load Tone, and Full API Access.
- Token exchange:
POST /api/v1/oauth/token (grant_type=authorization_code, code, code_verifier, redirect_uri, client_id).
- Rate limit: 100 req/min default; search lower. Higher limits via support@tone3000.com.
The v1 API is described primarily as "users log into their TONE3000 accounts and access tones they've created or favorited." Open question: whether public tone search + public .nam download works with an app key alone, or whether every download requires an end-user OAuth login. This needs confirmation with TONE3000 before committing to a UX (see Open Questions).
What's required in this codebase
Current state: there is no HTTP/networking dependency anywhere in the workspace, and no OAuth/browser-redirect handling. This is net-new surface area.
- HTTP client dependency — add
reqwest (rustls, async, pairs with the tokio feature iced already enables) or ureq (blocking, lighter) to a non-core crate. Keep rustortion-core GUI/network-free; networking belongs in rustortion-standalone (and later plugin) or a new small crate.
- TONE3000 client module — typed wrappers over the search/tones/models endpoints + model download, with API-key handling and (likely) the OAuth PKCE Select flow. Needs a registered redirect URI; localhost is allowed in dev. Decide secret-key handling (a desktop app can't hold a true secret — favors the publishable-key + PKCE path).
- Async download service — mirror the existing IR load-service pattern (
rustortion-core/src/ir/load_service.rs: background thread + crossbeam channels) so downloads run off the RT thread and never block the UI. On completion, write the .nam into the configured NAM models dir and call the existing rescan_nam_models() → nam_registry::init_from_loader(); the live registry::available_names() read means the model picker refreshes automatically.
- Browse/download UI — a dialog or panel (likely Cabinet/IO-adjacent or on the NAM stage card) to search, list results with metadata, and trigger download with progress/error feedback. Standalone-only first (
rustortion-standalone/src/gui/components/dialogs/); the plugin's sandboxed/host context makes browser-redirect OAuth and disk writes harder — defer plugin support.
- OAuth redirect handling (if required) — loopback HTTP listener or manual code paste to capture the auth code; token storage/refresh. Only needed if downloads require user login.
- Settings/secrets — store API key + tokens (reuse the standalone settings file; consider not committing/logging secrets). Persist nothing if app-key-only browsing turns out to be possible.
- i18n — all new strings via
tr!(), added to both EN and ZH_CN in rustortion-ui/src/i18n/mod.rs (existing NAM keys already there).
- Error/edge handling — offline, rate-limit (429), auth expiry, sample-rate-mismatch models (already bypassed gracefully by the stage), disk-write failures.
Relevant existing code
- NAM stage:
rustortion-core/src/amp/stages/nam.rs
- NAM loader / registry:
rustortion-core/src/nam/loader.rs, rustortion-core/src/nam/registry.rs
- Rescan path:
rustortion-standalone/src/audio/manager.rs (rescan_nam_models, load_nam_models)
- NAM stage UI + rescan message:
rustortion-ui/src/stages/nam.rs, rustortion-ui/src/app.rs (RescanNamModels)
- Async pattern to copy:
rustortion-core/src/ir/load_service.rs
- Standalone dialogs:
rustortion-standalone/src/gui/components/dialogs/
Open questions
- Does public tone search +
.nam download work with an app key only, or is end-user OAuth login mandatory? (Confirm with TONE3000 / inspect the demo app.)
- Is the desktop redirect-URI / loopback OAuth flow acceptable for a native app, or should we use a manual code-paste fallback?
- Scope to standalone only for v1, or design the client crate so the plugin can reuse it later?
reqwest (async, heavier, rustls) vs ureq (blocking, lighter) — blocking on a background thread may be simpler given we already use the thread+channel pattern.
- Licensing/attribution requirements for redistributing downloaded community tones in-app.
Suggested scope for a first cut
Standalone-only: search by query → list results → download into the NAM models folder → auto-rescan. Start with whatever auth the simplest browse/download path requires. Plugin support, favorites/created tabs, and the in-TONE3000 Select UI flow can follow.
Roadmap: v0.6.0 (Neural & Community).
Summary
Let users browse and download
.nammodels directly from TONE3000 (formerly ToneHunt) inside the app, instead of downloading files manually in a browser and dropping them into the NAM models folder. TONE3000 hosts the largest community library of NAM captures and now exposes an official REST API (v1) for exactly this.This is a feature for the NAM stage (shipped in #243). Models would be fetched into the existing NAM models folder, then picked up by the existing rescan/registry path — so once a file lands on disk, everything downstream already works.
Why
TONE3000 API (v1) — what we'd integrate
Base URL:
https://www.tone3000.com/api/v1. Integration guide / demo app: https://github.com/tone-3000/apiRelevant endpoints:
GET /tones/search— search & filter tones (params:query,gears,sorte.g.Trending). Heavily rate-limited.GET /tones/{id}— single tone metadata.GET /models?tone_id={id}— list models for a tone; each model object carries amodel_urlfor the actual file download.GET /tones/created,GET /tones/favorited— the logged-in user's own/favorited tones.GET /user— authenticated profile.Auth:
t3k_pub_…, client id) and a server-only secret key (t3k_cs_…).oauth/authorize?prompt=select_tone), Load Tone, and Full API Access.POST /api/v1/oauth/token(grant_type=authorization_code,code,code_verifier,redirect_uri,client_id).The v1 API is described primarily as "users log into their TONE3000 accounts and access tones they've created or favorited." Open question: whether public tone search + public
.namdownload works with an app key alone, or whether every download requires an end-user OAuth login. This needs confirmation with TONE3000 before committing to a UX (see Open Questions).What's required in this codebase
Current state: there is no HTTP/networking dependency anywhere in the workspace, and no OAuth/browser-redirect handling. This is net-new surface area.
reqwest(rustls, async, pairs with thetokiofeature iced already enables) orureq(blocking, lighter) to a non-core crate. Keeprustortion-coreGUI/network-free; networking belongs inrustortion-standalone(and later plugin) or a new small crate.rustortion-core/src/ir/load_service.rs: background thread + crossbeam channels) so downloads run off the RT thread and never block the UI. On completion, write the.naminto the configured NAM models dir and call the existingrescan_nam_models()→nam_registry::init_from_loader(); the liveregistry::available_names()read means the model picker refreshes automatically.rustortion-standalone/src/gui/components/dialogs/); the plugin's sandboxed/host context makes browser-redirect OAuth and disk writes harder — defer plugin support.tr!(), added to both EN and ZH_CN inrustortion-ui/src/i18n/mod.rs(existing NAM keys already there).Relevant existing code
rustortion-core/src/amp/stages/nam.rsrustortion-core/src/nam/loader.rs,rustortion-core/src/nam/registry.rsrustortion-standalone/src/audio/manager.rs(rescan_nam_models,load_nam_models)rustortion-ui/src/stages/nam.rs,rustortion-ui/src/app.rs(RescanNamModels)rustortion-core/src/ir/load_service.rsrustortion-standalone/src/gui/components/dialogs/Open questions
.namdownload work with an app key only, or is end-user OAuth login mandatory? (Confirm with TONE3000 / inspect the demo app.)reqwest(async, heavier, rustls) vsureq(blocking, lighter) — blocking on a background thread may be simpler given we already use the thread+channel pattern.Suggested scope for a first cut
Standalone-only: search by query → list results → download into the NAM models folder → auto-rescan. Start with whatever auth the simplest browse/download path requires. Plugin support, favorites/created tabs, and the in-TONE3000 Select UI flow can follow.
Roadmap: v0.6.0 (Neural & Community).