{"_id":"@3sv1n/multitrack","_rev":"3-d481e8d0dfe1b8c3652ad8ca3bbbd03e","name":"@3sv1n/multitrack","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@3sv1n/multitrack","version":"0.1.0","keywords":["multitrack","audio","stem","player","sync"],"author":{"name":"3sv1n"},"license":"MIT","_id":"@3sv1n/multitrack@0.1.0","maintainers":[{"name":"esvinjoshuab","email":"joshua.esvin312@gmail.com"}],"homepage":"https://github.com/esvinjoshua/stem-packages/tree/main/packages/multitrack#readme","bugs":{"url":"https://github.com/esvinjoshua/stem-packages/issues"},"dist":{"shasum":"3a7fd3ad84976b85430ad20deda0d0da1829e378","tarball":"https://registry.npmjs.org/@3sv1n/multitrack/-/multitrack-0.1.0.tgz","fileCount":8,"integrity":"sha512-UEL/a3anvAl/+jdoy2aCyIUjQUNya3Q4r2vsTUIE8mqLBAS6WLwkN5TkZFUeHgUWaIjldPkwUUvYOApGWyvnZg==","signatures":[{"sig":"MEQCIACU8mfxdHZNCTWYQ8W4phj5Z4+J5HpNvsN0yOC9a2lIAiAP5S0d3biY2Pvc51wkcZ8dq4qS3dbhftU9DZSW47yq3w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":95468},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"379be8a373efec3ee77f8e9088f4faa0682c4fe7","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup"},"_npmUser":{"name":"esvinjoshuab","email":"joshua.esvin312@gmail.com"},"repository":{"url":"git+https://github.com/esvinjoshua/stem-packages.git","type":"git","directory":"packages/multitrack"},"_npmVersion":"11.8.0","description":"Framework-agnostic audio synchronization and playback control","directories":{},"_nodeVersion":"25.5.0","dependencies":{"@soundtouchjs/audio-worklet":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.2","typescript":"^5.4.5","@types/react":"^18.2.79"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/multitrack_0.1.0_1780649070618_0.7634560686849943","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@3sv1n/multitrack","version":"0.1.1","keywords":["multitrack","audio","stem","player","sync"],"author":{"name":"3sv1n"},"license":"MIT","_id":"@3sv1n/multitrack@0.1.1","maintainers":[{"name":"esvinjoshuab","email":"joshua.esvin312@gmail.com"}],"homepage":"https://github.com/esvinjoshua/stem-packages/tree/main/packages/multitrack#readme","bugs":{"url":"https://github.com/esvinjoshua/stem-packages/issues"},"dist":{"shasum":"523d08fadbff79f0b1ee4d6bfe41ed24846e748c","tarball":"https://registry.npmjs.org/@3sv1n/multitrack/-/multitrack-0.1.1.tgz","fileCount":8,"integrity":"sha512-WbPaysITKNsz5BxWX2vLw1Xmgm8k1bmwrRPkat4Tn2/3MK7PFJzo9hLOC8eyesul2Hlom6a1rEMN0NSV5rNLBA==","signatures":[{"sig":"MEYCIQCnYFmqQVOrTh2QrXUDSco+t/KuuHtZIk2XZhUq3wePgAIhAPsIsGztV7xmIL0eWPxDwkKOAry4CiLfnzz9/6dMmW6H","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":95468},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"379be8a373efec3ee77f8e9088f4faa0682c4fe7","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup"},"_npmUser":{"name":"esvinjoshuab","email":"joshua.esvin312@gmail.com"},"repository":{"url":"git+https://github.com/esvinjoshua/stem-packages.git","type":"git","directory":"packages/multitrack"},"_npmVersion":"11.8.0","description":"Framework-agnostic audio synchronization and playback control","directories":{},"_nodeVersion":"25.5.0","dependencies":{"@soundtouchjs/audio-worklet":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.2","typescript":"^5.4.5","@types/react":"^18.2.79"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/multitrack_0.1.1_1780649722831_0.5397893176948403","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@3sv1n/multitrack","version":"0.1.2","description":"Framework-agnostic audio synchronization and playback control","author":{"name":"3sv1n"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/3sv1n/stem-packages.git","directory":"packages/multitrack"},"homepage":"https://github.com/3sv1n/stem-packages/tree/main/packages/multitrack#readme","keywords":["multitrack","audio","stem","player","sync"],"main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"require":"./dist/index.js","import":"./dist/index.mjs","types":"./dist/index.d.ts"}},"publishConfig":{"access":"public"},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run"},"dependencies":{"@soundtouchjs/audio-worklet":"^0.1.2"},"peerDependencies":{"react":">=18"},"devDependencies":{"@types/react":"^18.2.79","tsup":"^8.0.2","typescript":"^5.4.5"},"gitHead":"379be8a373efec3ee77f8e9088f4faa0682c4fe7","_id":"@3sv1n/multitrack@0.1.2","bugs":{"url":"https://github.com/3sv1n/stem-packages/issues"},"_nodeVersion":"25.5.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-OLkWKRo+ovuImU6emdYr43B8+2LAkEsbKtt4jAOHGh5bd1i2yuLFB02Xd9PCBg6kXxwBUJpjTXh6taGwttEBiw==","shasum":"d252ac447b7c95aff70ef9f50d6165ad46aff21f","tarball":"https://registry.npmjs.org/@3sv1n/multitrack/-/multitrack-0.1.2.tgz","fileCount":8,"unpackedSize":95456,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIA1SoT0CxwltwDRqnq7hFeuSuZzHWAhlefS3DDYM/gthAiEA8slArS/2Er+PA2R9EjlTWAg/jcP2W6trX6k21tUNRx4="}]},"_npmUser":{"name":"esvinjoshuab","email":"joshua.esvin312@gmail.com"},"directories":{},"maintainers":[{"name":"esvinjoshuab","email":"joshua.esvin312@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/multitrack_0.1.2_1780650081511_0.3539221187571395"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-05T08:44:30.414Z","modified":"2026-06-05T09:01:21.792Z","0.1.0":"2026-06-05T08:44:30.742Z","0.1.1":"2026-06-05T08:55:22.986Z","0.1.2":"2026-06-05T09:01:21.645Z"},"bugs":{"url":"https://github.com/3sv1n/stem-packages/issues"},"author":{"name":"3sv1n"},"license":"MIT","homepage":"https://github.com/3sv1n/stem-packages/tree/main/packages/multitrack#readme","keywords":["multitrack","audio","stem","player","sync"],"repository":{"type":"git","url":"git+https://github.com/3sv1n/stem-packages.git","directory":"packages/multitrack"},"description":"Framework-agnostic audio synchronization and playback control","maintainers":[{"name":"esvinjoshuab","email":"joshua.esvin312@gmail.com"}],"readme":"# @3sv1n/multitrack\n\nA framework-agnostic audio synchronization and playback control library. It manages WebAudio contexts, multitrack synchronization, seeking, muting, volume controls, and scrubbing.\n\nIt also exports a React hook adapter (`useAudioEngine`) for seamlessly consuming engine state with tear-free concurrent rendering using `useSyncExternalStore`.\n\n## Installation\n\n```bash\nnpm install @3sv1n/multitrack\n# or\nyarn add @3sv1n/multitrack\n# or\npnpm add @3sv1n/multitrack\n```\n\n*Note: This package has a peer dependency on `react >= 18` for the `useAudioEngine` hook. If you use the engine in a non-React environment, you can ignore the React dependency and use the core classes directly.*\n\n## Basic Usage\n\n### Creating an Engine\n\n```typescript\nimport { NativeWebAudioEngine } from '@3sv1n/multitrack';\n\n// Instantiate the engine\nconst engine = new NativeWebAudioEngine();\n\n// Register tracks by ID and URL\nawait engine.registerTrack('vocals', 'https://example.com/vocals.mp3');\nawait engine.registerTrack('instrumental', 'https://example.com/instrumental.mp3');\n\n// Play all tracks synchronously\nengine.play();\n```\n\n### Using in React\n\nThe `useAudioEngine` hook allows you to bind the engine's state to your React components. \n\n```tsx\nimport { useState } from 'react';\nimport { NativeWebAudioEngine, useAudioEngine } from '@3sv1n/multitrack';\n\nfunction Player() {\n  // Keep the engine instance stable across renders\n  const [engine] = useState(() => new NativeWebAudioEngine());\n  \n  // Subscribe to engine state\n  const { state } = useAudioEngine(engine);\n\n  return (\n    <div>\n      <p>Time: {state.currentTime.toFixed(2)} / {state.duration.toFixed(2)}</p>\n      <button onClick={() => engine.togglePlay()}>\n        {state.isPlaying ? 'Pause' : 'Play'}\n      </button>\n      \n      {state.tracks.map(track => (\n        <div key={track.id}>\n          <span>{track.id} (Volume: {track.volume})</span>\n          <button onClick={() => engine.setMuted(track.id, !track.isMuted)}>\n            {track.isMuted ? 'Unmute' : 'Mute'}\n          </button>\n        </div>\n      ))}\n    </div>\n  );\n}\n```\n\n## API Reference\n\n### `IAudioEngine` Interface\nThe `IAudioEngine` defines the core contract for audio manipulation. `NativeWebAudioEngine` is the provided implementation.\n\n#### Core Methods\n- `getState(): PlayerState` - Returns the current snapshot of the player state.\n- `subscribe(listener: () => void): () => void` - Subscribe to state changes.\n- `registerTrack(id: string, url: string): Promise<void>` - Adds an audio track to the engine.\n- `unregisterTrack(id: string): void` - Removes a track and frees its resources.\n- `destroy(): void` - Cleans up the engine, stopping all playback.\n\n#### Playback Controls\n- `play(): Promise<void>`\n- `pause(): void`\n- `togglePlay(): void`\n- `seekTo(time: number): void`\n\n#### Scrubbing\nScrubbing allows UI sliders to pause playback visually, seek smoothly, and then resume if it was playing previously.\n- `startScrub(): void`\n- `updateScrub(time: number): void`\n- `commitScrub(time: number): void`\n- `cancelScrub(): void`\n\n#### Mixing\n- `setVolume(id: string, volume: number): void` (Volume ranges from 0 to 1)\n- `setMuted(id: string, muted: boolean): void`\n- `setPitch(semitones: number): void`\n- `setPlaybackRate(rate: number): void`\n\n### `PlayerState`\nThe object returned by `getState()`.\n\n```typescript\ninterface PlayerState {\n  isPlaying: boolean;\n  isScrubbing: boolean;\n  currentTime: number;\n  duration: number;\n  pitch: number;        // in semitones\n  playbackRate: number; // multiplier (e.g. 1.0)\n  error?: Error;\n  tracks: TrackState[]; // Array of track states\n}\n\ninterface TrackState {\n  id: string;\n  volume: number;\n  isMuted: boolean;\n  isReady: boolean;\n  error?: Error;\n}\n```\n\n## Advanced Usage\n\nUnder the hood, this package utilizes `@soundtouchjs/audio-worklet` for high-quality pitch and tempo shifting. The engine handles the setup automatically when instantiated. \n","readmeFilename":"README.md"}