# 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.
