From d3ff1f1477b8e8ee7e87ac00598c72da99b634de Mon Sep 17 00:00:00 2001 From: Nuwan Date: Wed, 14 Jan 2026 21:20:46 +0530 Subject: [PATCH] docs(04-01): create jamClient JamTrack API reference - Documented all 15+ JamTrack-specific jamClient methods with signatures - Covered playback, track data, download/sync, and JMEP methods - Added comparison table: JamTrack vs Backing Track methods - Included usage examples for load flow, download/sync, monitoring - Noted critical implementation details (fqId format, async patterns, string returns) --- .planning/codebase/JAMTRACK_API.md | 569 +++++++++++++++++++++++++++++ 1 file changed, 569 insertions(+) create mode 100644 .planning/codebase/JAMTRACK_API.md diff --git a/.planning/codebase/JAMTRACK_API.md b/.planning/codebase/JAMTRACK_API.md new file mode 100644 index 000000000..ee98a93e3 --- /dev/null +++ b/.planning/codebase/JAMTRACK_API.md @@ -0,0 +1,569 @@ +# JamTrack jamClient API Reference + +Complete reference for jamClient JamTrack-related methods discovered in `web/app/assets/javascripts/bridge_api.es6` and usage patterns from legacy code. + +**Source:** Lines 1035-1093 in bridge_api.es6 + +## 1. Playback Methods + +### JamTrackPlay(fqId) + +**Purpose:** Load JamTrack into session (despite name, this is "load", not "play") + +**Signature:** +```javascript +context.jamClient.JamTrackPlay(fqId) +``` + +**Parameters:** +- `fqId` (string): Fully-qualified ID format: `{jamTrackId}-{sampleRate}` + - Example: `'12345-48'` or `'12345-44'` + - Sample rate: '44' for 44.1kHz, '48' for 48kHz + - Must match client's audio sample rate (from `GetSampleRate()`) + +**Returns:** Boolean +- `true`: JamTrack loaded successfully +- `false`: Failed to load (show error to user) + +**Usage pattern** (session.js:2760): +```javascript +var sampleRate = context.jamClient.GetSampleRate(); // 44 or 48 +var sampleRateForFilename = sampleRate == 48 ? '48' : '44'; +var fqId = jamTrack.id + '-' + sampleRateForFilename; + +var result = context.jamClient.JamTrackPlay(fqId); +if (!result) { + app.notify({ + title: "JamTrack Can Not Open", + text: "Unable to open your JamTrack. Please contact support@jamkazam.com" + }, null, true); +} +``` + +**Notes:** +- Must be called AFTER JamTrack is synchronized (on disk with keys) +- Loads all stem tracks into session mixer +- Call `JamTrackLoadJmep(fqId, jmepData)` BEFORE this if tempo/pitch modifications needed + +--- + +### JamTrackStopPlay() + +**Purpose:** Stop JamTrack playback and unload from session + +**Signature:** +```javascript +context.jamClient.JamTrackStopPlay() +``` + +**Parameters:** None + +**Returns:** Void + +**Usage pattern** (session.js:2743): +```javascript +// Always stop before loading new JamTrack +context.jamClient.JamTrackStopPlay(); +``` + +**Notes:** +- Cleans up current JamTrack resources +- Should be called before loading a new JamTrack +- Safe to call even if no JamTrack is loaded + +--- + +### JamTrackIsPlaying() + +**Purpose:** Query whether a JamTrack is currently playing + +**Signature:** +```javascript +context.jamClient.JamTrackIsPlaying() +``` + +**Parameters:** None + +**Returns:** Boolean +- `true`: JamTrack is actively playing +- `false`: No JamTrack playing or paused + +**Notes:** +- Use for playback state checks in UI +- Similar to `isSessionTrackPlaying()` for Backing Tracks + +--- + +### JamTrackIsPlayable() + +**Purpose:** Check if JamTrack can be played (loaded and ready) + +**Signature:** +```javascript +context.jamClient.JamTrackIsPlayable() +``` + +**Parameters:** None + +**Returns:** Boolean +- `true`: JamTrack is loaded and can be played +- `false`: No JamTrack loaded or not ready + +**Notes:** +- Use to enable/disable play button +- Checks if JamTrack was successfully loaded via `JamTrackPlay()` + +--- + +## 2. Track Data Methods + +### JamTrackGetTracks() + +**Purpose:** Get list of stem tracks in currently loaded JamTrack + +**Signature:** +```javascript +context.jamClient.JamTrackGetTracks() +``` + +**Parameters:** None + +**Returns:** Array of track objects (format TBD - not shown in legacy code) + +**Notes:** +- Called after JamTrack is loaded +- Returns individual stems (drums, bass, guitar, vocals, etc.) +- Used to populate stem mixer UI + +--- + +### JamTrackGetTrackDetail(fqId) + +**Purpose:** Get synchronization state and version for a JamTrack + +**Signature:** +```javascript +context.jamClient.JamTrackGetTrackDetail(fqId) +``` + +**Parameters:** +- `fqId` (string): Fully-qualified ID `{jamTrackId}-{sampleRate}` + +**Returns:** Object +```javascript +{ + key_state: string, // Encryption key status + version: number // JamTrack version number +} +``` + +**Usage pattern** (download_jamtrack.js.coffee state machine): +- Queries client to determine current sync state +- Used by state machine to decide next action (packaging, downloading, keying, synchronized) +- `key_state` values determine if encryption keys are available + +**Notes:** +- Called repeatedly during download/sync process +- Return value drives state machine transitions +- Critical for determining when JamTrack is ready to play + +--- + +### JamTrackGetImage() + +**Purpose:** Get album art image for currently loaded JamTrack + +**Signature:** +```javascript +context.jamClient.JamTrackGetImage() +``` + +**Parameters:** None + +**Returns:** Image data (format TBD - possibly base64 or file path) + +**Notes:** +- Display album art in player UI +- Similar functionality to potential Backing Track cover art + +--- + +### GetJamTrackTimeline() + +**Purpose:** Get timeline/waveform data for visualization + +**Signature:** +```javascript +context.jamClient.GetJamTrackTimeline() +``` + +**Parameters:** None + +**Returns:** Timeline data structure (format TBD) + +**Notes:** +- Used for waveform visualization in UI +- May include beat markers, section markers +- Advanced feature for timeline scrubbing + +--- + +## 3. Download/Sync Methods + +### JamTrackDownload(jamTrackId, mixdownId, userId, progressCallback, successCallback, failCallback) + +**Purpose:** Download JamTrack package from server to local disk + +**Signature:** +```javascript +context.jamClient.JamTrackDownload( + jamTrackId, // JamTrack ID (integer) + mixdownId, // Selected mixdown package ID (integer) + userId, // Current user ID (integer) + progressCallback, // Function name (string) for progress updates + successCallback, // Function name (string) for completion + failCallback // Function name (string) for errors +) +``` + +**Parameters:** +- All parameters required +- Callbacks are **function names as strings**, not function references +- Callbacks are called by native client with download progress data + +**Returns:** Void (callbacks handle results) + +**Usage pattern** (download_jamtrack.js.coffee downloading state): +```coffeescript +context.jamClient.JamTrackDownload( + @jamTrack.id, + @jamTrack.selectedMixdown.id, + @app.currentUser.id, + 'onDownloadProgress', # String, not function ref + 'onDownloadSuccess', + 'onDownloadFail' +) +``` + +**Callback signatures:** +- `progressCallback(percentComplete, bytesDownloaded, totalBytes)` +- `successCallback()` +- `failCallback(errorMessage)` + +**Notes:** +- Called when state machine reaches `downloading` state +- Must be preceded by mixdown packaging on server +- After success, call `JamTrackKeysRequest()` to fetch encryption keys +- Callbacks must be global functions accessible to native client + +--- + +### JamTrackKeysRequest() + +**Purpose:** Request encryption keys for downloaded JamTrack + +**Signature:** +```javascript +context.jamClient.JamTrackKeysRequest() +``` + +**Parameters:** None + +**Returns:** Void (asynchronous operation) + +**Usage pattern** (download_jamtrack.js.coffee keying state): +- Called after successful download +- Native client fetches keys from server +- Poll `JamTrackGetTrackDetail()` to check when keys are available +- State machine has 10-second timeout for keying operation + +**Notes:** +- Required for encrypted JamTrack playback +- Separate step from download +- Can fail with timeout if server doesn't respond +- After success, JamTrack is "synchronized" and ready to play + +--- + +### InvalidateJamTrack(fqId) + +**Purpose:** Clear cached JamTrack version, force re-download + +**Signature:** +```javascript +context.jamClient.InvalidateJamTrack(fqId) +``` + +**Parameters:** +- `fqId` (string): Fully-qualified ID `{jamTrackId}-{sampleRate}` + +**Returns:** Void + +**Notes:** +- Used when new JamTrack version is available +- Forces download of updated package +- Clears local cache and encryption keys + +--- + +## 4. JMEP (Jam Enhancement Package) Methods + +### JamTrackLoadJmep(fqId, jmepData) + +**Purpose:** Load tempo/pitch modifications for JamTrack playback + +**Signature:** +```javascript +context.jamClient.JamTrackLoadJmep(fqId, jmepData) +``` + +**Parameters:** +- `fqId` (string): Fully-qualified ID `{jamTrackId}-{sampleRate}` +- `jmepData` (object): JMEP data structure with tempo/pitch settings + +**Returns:** Void + +**Usage pattern** (session.js:2749-2757): +```javascript +if(jamTrack.jmep) { + logger.debug("setting jmep data"); + context.jamClient.JamTrackLoadJmep(fqId, jamTrack.jmep); +} else { + logger.debug("no jmep data for jamtrack"); +} + +// Load JamTrack AFTER setting JMEP +context.jamClient.JamTrackPlay(fqId); +``` + +**JMEP data structure:** +```javascript +{ + tempo: number, // Tempo modification (BPM or percentage) + pitch: number, // Pitch shift (semitones) + // Additional fields TBD +} +``` + +**Notes:** +- **Must be called BEFORE `JamTrackPlay()`** +- Optional feature - check `if(jamTrack.jmep)` before calling +- Allows practicing at different tempos without re-encoding +- Pitch shift for transposition to different keys + +--- + +## 5. Playback Control Methods + +### SessionJamTrackSeekMs() + +**Purpose:** Seek to specific position in JamTrack playback + +**Signature:** +```javascript +context.jamClient.SessionJamTrackSeekMs(positionMs) +``` + +**Parameters:** +- `positionMs` (number): Position in milliseconds + +**Returns:** Void (or boolean - TBD) + +**Notes:** +- Similar to `SessionTrackSeekMs()` for Backing Tracks +- Use for seek bar / scrubbing functionality +- Requires JamTrack to be loaded and playing/paused + +--- + +### SessionCurrrentJamTrackPlayPosMs() + +**Purpose:** Get current playback position in milliseconds + +**Signature:** +```javascript +context.jamClient.SessionCurrrentJamTrackPlayPosMs() +``` + +**Parameters:** None + +**Returns:** Number (milliseconds) + +**Notes:** +- Poll at 500ms intervals for playback monitoring +- Similar to `SessionCurrentPlayPosMs()` for Backing Tracks +- Use for time display and seek bar position +- Returns string - use `parseInt()` before math operations + +--- + +### SessionGetJamTracksPlayDurationMs() + +**Purpose:** Get total duration of loaded JamTrack in milliseconds + +**Signature:** +```javascript +context.jamClient.SessionGetJamTracksPlayDurationMs() +``` + +**Parameters:** None + +**Returns:** Number (milliseconds) + +**Notes:** +- **Note plural "JamTracks"** in method name (differs from singular in other methods) +- Call after JamTrack is loaded to get duration +- Use for time display and seek bar maximum +- Returns string - use `parseInt()` before math operations + +--- + +## 6. Query Methods + +### GetSampleRate() + +**Purpose:** Get audio interface sample rate (44.1 or 48 kHz) + +**Signature:** +```javascript +context.jamClient.GetSampleRate() +``` + +**Parameters:** None + +**Returns:** Number +- `44` for 44.1 kHz +- `48` for 48 kHz + +**Usage pattern** (session.js:2745): +```javascript +var sampleRate = context.jamClient.GetSampleRate(); +var sampleRateForFilename = sampleRate == 48 ? '48' : '44'; +var fqId = jamTrack.id + '-' + sampleRateForFilename; +``` + +**Notes:** +- **Critical for fqId construction** +- Sample rate determined by user's audio interface configuration +- JamTrack packages are encoded at specific sample rates +- Must match package sample rate for playback + +--- + +## 7. Comparison: JamTrack vs Backing Track Methods + +| Operation | JamTrack Method | Backing Track Method | Notes | +|-----------|-----------------|----------------------|-------| +| **Select file** | jQuery dialog | `ShowSelectBackingTrackDialog()` | JamTrack: purchased list; Backing Track: native file picker | +| **Open/Load** | `JamTrackPlay(fqId)` | `SessionOpenBackingTrackFile(path)` | JamTrack: ID-based; Backing Track: file path | +| **Stop** | `JamTrackStopPlay()` | `SessionCloseBackingTrack(path)` | JamTrack: no params; Backing Track: needs path | +| **Check playing** | `JamTrackIsPlaying()` | `isSessionTrackPlaying()` | Same concept | +| **Get position** | `SessionCurrrentJamTrackPlayPosMs()` | `SessionCurrentPlayPosMs()` | Same concept | +| **Get duration** | `SessionGetJamTracksPlayDurationMs()` | `SessionGetTracksPlayDurationMs()` | Both plural "Tracks" | +| **Seek** | `SessionJamTrackSeekMs(ms)` | `SessionTrackSeekMs(ms)` | Same concept | + +**Shared methods** (used by both): +- `SessionStartPlay()` - Start playback +- `SessionStopPlay()` - Stop playback +- `SessionPausePlay()` - Pause playback +- `GetSampleRate()` - Audio interface sample rate + +**Key differences:** +1. **Identification:** JamTrack uses fqId (`id-sampleRate`), Backing Track uses file path +2. **Selection UI:** JamTrack has server-side catalog, Backing Track is local files +3. **Synchronization:** JamTrack requires download/keys, Backing Track is immediate +4. **Stems:** JamTrack has multiple stem tracks, Backing Track is single file +5. **JMEP:** JamTrack supports tempo/pitch modification, Backing Track does not + +--- + +## Usage Examples + +### Example 1: Complete JamTrack Load Flow + +```javascript +// 1. Get sample rate +const sampleRate = jamClient.GetSampleRate(); // 44 or 48 +const sampleRateStr = sampleRate == 48 ? '48' : '44'; +const fqId = `${jamTrack.id}-${sampleRateStr}`; + +// 2. Stop any existing playback +jamClient.JamTrackStopPlay(); + +// 3. Load JMEP if present (tempo/pitch modifications) +if (jamTrack.jmep) { + jamClient.JamTrackLoadJmep(fqId, jamTrack.jmep); +} + +// 4. Load JamTrack +const result = jamClient.JamTrackPlay(fqId); +if (!result) { + console.error('Failed to load JamTrack'); + return; +} + +// 5. Start playback +jamClient.SessionStartPlay(); +``` + +### Example 2: Download and Sync + +```javascript +// 1. Start download +jamClient.JamTrackDownload( + jamTrackId, + mixdownId, + userId, + 'onProgress', // Callback names as strings + 'onSuccess', + 'onFail' +); + +// 2. On download success, request encryption keys +function onSuccess() { + jamClient.JamTrackKeysRequest(); + + // Poll for key availability + const checkKeys = setInterval(() => { + const detail = jamClient.JamTrackGetTrackDetail(fqId); + if (detail.key_state === 'AVAILABLE') { + clearInterval(checkKeys); + // Now can call JamTrackPlay(fqId) + } + }, 500); +} +``` + +### Example 3: Playback Monitoring + +```javascript +// Poll every 500ms for position updates +const pollInterval = setInterval(async () => { + if (!jamClient.JamTrackIsPlaying()) { + clearInterval(pollInterval); + return; + } + + const position = parseInt(jamClient.SessionCurrrentJamTrackPlayPosMs()); + const duration = parseInt(jamClient.SessionGetJamTracksPlayDurationMs()); + + updateUI(position, duration); +}, 500); +``` + +--- + +## Critical Notes for React Implementation + +1. **fqId format is mandatory:** Always `{jamTrackId}-{sampleRate}` +2. **Sample rate matters:** Must match package sample rate (44 or 48) +3. **Load order:** JMEP → JamTrackPlay → SessionStartPlay +4. **String returns:** All position/duration methods return strings, use `parseInt()` +5. **Async operations:** Download and key fetching are asynchronous with callbacks +6. **Callback names:** Pass callback names as strings, not function references +7. **Global scope:** Callback functions must be accessible globally to native client +8. **Shared playback controls:** Use Session* methods (Start/Stop/Pause) like Backing Track + +## Method Availability + +All methods assume native client is available (`gon.isNativeClient === true`). In browser mode, show appropriate fallback UI directing users to download the native client.