> ## Documentation Index
> Fetch the complete documentation index at: https://developer.audioshake.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Introduction

> API reference and guides for AI source separation, transcription, and content analysis

export const AudioDemoPlayer = () => {
  const WAVEFORM_BARS = 280;
  const TRACK_HEIGHT = 44;
  const END_EPSILON_SECONDS = 0.001;
  const useCases = [{
    id: 'music-stems',
    label: 'Music Stems',
    tracks: [{
      key: 'mix',
      name: 'Mix',
      src: '/audio/shakeitup.mp3'
    }, {
      key: 'vocals',
      name: 'Vocals',
      src: '/audio/shakeitup_vocals.wav'
    }]
  }, {
    id: 'music-removal',
    label: 'Music Removal',
    tracks: [{
      key: 'full-track',
      name: 'Full Track',
      src: '/audio/shakeitup.mp3'
    }, {
      key: 'removed-music',
      name: 'Removed Music',
      src: '/audio/shakeitup_vocals.wav'
    }]
  }, {
    id: 'dialogue-background',
    label: 'Dialogue & Background',
    tracks: [{
      key: 'dialogue',
      name: 'Dialogue',
      src: '/audio/shakeitup_vocals.wav'
    }, {
      key: 'background',
      name: 'Background',
      src: '/audio/shakeitup.mp3'
    }]
  }];
  const makeTracks = useCaseId => {
    const found = useCases.find(u => u.id === useCaseId) || useCases[0];
    return found.tracks.map(t => ({
      id: `${useCaseId}-${t.key}`,
      name: t.name,
      src: t.src,
      muted: false,
      solo: false
    }));
  };
  const formatTime = seconds => {
    if (!seconds || isNaN(seconds)) return '0:00';
    const m = Math.floor(seconds / 60);
    const s = Math.floor(seconds % 60);
    return `${m}:${s.toString().padStart(2, '0')}`;
  };
  const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
  const rootRef = useRef(null);
  const canvasRefs = useRef({});
  const ctxRef = useRef(null);
  const buffersBySrcRef = useRef({});
  const peaksBySrcRef = useRef({});
  const sourcesRef = useRef([]);
  const decodePromisesRef = useRef({});
  const playRequestIdRef = useRef(0);
  const playStartCtxTimeRef = useRef(0);
  const playStartOffsetRef = useRef(0);
  const durationRef = useRef(0);
  const isPlayingRef = useRef(false);
  const [activeUseCaseId, setActiveUseCaseId] = useState(useCases[0].id);
  const [tracks, setTracks] = useState(makeTracks(useCases[0].id));
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [engineStatus, setEngineStatus] = useState('idle');
  const [engineError, setEngineError] = useState('');
  const [assetsVersion, setAssetsVersion] = useState(0);
  const [isInView, setIsInView] = useState(false);
  durationRef.current = duration;
  isPlayingRef.current = isPlaying;
  const hasSolo = tracks.some(t => t.solo);
  const isAudible = track => hasSolo ? track.solo : !track.muted;
  const setCanvasRef = id => el => {
    if (el) canvasRefs.current[id] = el;
  };
  const stopSources = () => {
    sourcesRef.current.forEach(entry => {
      try {
        entry.source.stop();
      } catch (_) {}
      try {
        entry.source.disconnect();
      } catch (_) {}
      try {
        entry.gain.disconnect();
      } catch (_) {}
    });
    sourcesRef.current = [];
  };
  const getDurationForTracks = trackList => trackList.reduce((max, t) => {
    const b = buffersBySrcRef.current[t.src];
    return b ? Math.max(max, b.duration) : max;
  }, 0);
  const updateDurationForTracks = trackList => {
    const d = getDurationForTracks(trackList);
    setDuration(d);
    return d;
  };
  const ensureAudioContext = () => {
    if (ctxRef.current) return ctxRef.current;
    const ctx = new (window.AudioContext || window.webkitAudioContext)();
    ctxRef.current = ctx;
    return ctx;
  };
  const decodeSource = async (ctx, src) => {
    const res = await fetch(src);
    if (!res.ok) throw new Error(`Failed to fetch ${src}`);
    const arr = await res.arrayBuffer();
    const buffer = await ctx.decodeAudioData(arr);
    buffersBySrcRef.current[src] = buffer;
    const channel = buffer.getChannelData(0);
    const step = Math.max(1, Math.floor(channel.length / WAVEFORM_BARS));
    const peaks = [];
    for (let i = 0; i < WAVEFORM_BARS; i++) {
      let min = 0;
      let max = 0;
      const start = i * step;
      const end = Math.min(start + step, channel.length);
      for (let j = start; j < end; j++) {
        const v = channel[j];
        if (v < min) min = v;
        if (v > max) max = v;
      }
      peaks.push({
        min,
        max
      });
    }
    peaksBySrcRef.current[src] = peaks;
    setAssetsVersion(v => v + 1);
  };
  const ensureTracksReady = async trackList => {
    try {
      setEngineError('');
      const ctx = ensureAudioContext();
      const needed = [...new Set(trackList.map(t => t.src))];
      const decodeJobs = needed.map(src => {
        if (buffersBySrcRef.current[src] && peaksBySrcRef.current[src]) return Promise.resolve();
        if (decodePromisesRef.current[src]) return decodePromisesRef.current[src];
        setEngineStatus('loading');
        const job = decodeSource(ctx, src).finally(() => {
          delete decodePromisesRef.current[src];
        });
        decodePromisesRef.current[src] = job;
        return job;
      });
      await Promise.all(decodeJobs);
      setEngineStatus('ready');
      updateDurationForTracks(trackList);
      return true;
    } catch (err) {
      setEngineStatus('error');
      setEngineError(err instanceof Error ? err.message : 'Failed to load audio assets');
      return false;
    }
  };
  const getTimelineTime = ctx => playStartOffsetRef.current + (ctx.currentTime - playStartCtxTimeRef.current);
  const startPlayback = async fromTime => {
    const requestId = ++playRequestIdRef.current;
    const trackSnapshot = tracks;
    const ready = await ensureTracksReady(trackSnapshot);
    if (!ready) return;
    const ctx = ctxRef.current;
    if (!ctx) return;
    await ctx.resume();
    if (requestId !== playRequestIdRef.current) return;
    const timelineDuration = updateDurationForTracks(trackSnapshot);
    if (timelineDuration <= 0) return;
    const startAt = clamp(fromTime !== undefined ? fromTime : currentTime, 0, timelineDuration);
    stopSources();
    const when = ctx.currentTime;
    playStartCtxTimeRef.current = when;
    playStartOffsetRef.current = startAt;
    const nextSources = [];
    trackSnapshot.forEach(t => {
      const buffer = buffersBySrcRef.current[t.src];
      if (!buffer || startAt >= buffer.duration) return;
      const source = ctx.createBufferSource();
      const gain = ctx.createGain();
      source.buffer = buffer;
      gain.gain.value = isAudible(t) ? 1 : 0;
      source.connect(gain);
      gain.connect(ctx.destination);
      source.start(when, startAt, buffer.duration - startAt);
      nextSources.push({
        trackId: t.id,
        source,
        gain
      });
    });
    if (nextSources.length === 0) return;
    sourcesRef.current = nextSources;
    setCurrentTime(startAt);
    setIsPlaying(true);
  };
  const pausePlayback = () => {
    const ctx = ctxRef.current;
    if (!ctx) return;
    const d = durationRef.current;
    const t = clamp(getTimelineTime(ctx), 0, d || Number.MAX_SAFE_INTEGER);
    stopSources();
    setCurrentTime(d > 0 ? Math.min(t, d) : t);
    setIsPlaying(false);
  };
  const seekTo = targetTime => {
    const d = durationRef.current;
    const t = clamp(targetTime, 0, d || targetTime);
    if (isPlayingRef.current) {
      startPlayback(t);
    } else {
      setCurrentTime(t);
    }
  };
  const handleSeekFromEvent = e => {
    const d = durationRef.current;
    if (d <= 0) return;
    const rect = e.currentTarget.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const pct = clamp(x / rect.width, 0, 1);
    seekTo(pct * d);
  };
  const togglePlay = () => {
    if (isPlayingRef.current) {
      pausePlayback();
      return;
    }
    const d = durationRef.current;
    const atEnd = d > 0 && currentTime >= d - END_EPSILON_SECONDS;
    startPlayback(atEnd ? 0 : currentTime);
  };
  const switchUseCase = useCaseId => {
    if (useCaseId === activeUseCaseId) return;
    playRequestIdRef.current += 1;
    stopSources();
    setIsPlaying(false);
    setCurrentTime(0);
    setDuration(0);
    setActiveUseCaseId(useCaseId);
    const nextTracks = makeTracks(useCaseId);
    setTracks(nextTracks);
    updateDurationForTracks(nextTracks);
    ensureTracksReady(nextTracks);
  };
  const toggleMute = id => {
    setTracks(prev => prev.map(t => t.id === id ? {
      ...t,
      muted: !t.muted
    } : t));
  };
  const toggleSolo = id => {
    setTracks(prev => prev.map(t => t.id === id ? {
      ...t,
      solo: !t.solo
    } : t));
  };
  useEffect(() => {
    if (sourcesRef.current.length === 0) return;
    sourcesRef.current.forEach(entry => {
      const t = tracks.find(x => x.id === entry.trackId);
      entry.gain.gain.value = t && isAudible(t) ? 1 : 0;
    });
  }, [tracks, hasSolo]);
  useEffect(() => {
    const el = rootRef.current;
    if (!el) return;
    const observer = new IntersectionObserver(entries => {
      const entry = entries[0];
      setIsInView(Boolean(entry && entry.isIntersecting));
    }, {
      rootMargin: '200px'
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);
  useEffect(() => {
    if (!isInView || engineStatus !== 'idle') return;
    let cancelled = false;
    let timeoutId;
    const run = () => {
      if (!cancelled) ensureTracksReady(tracks);
    };
    if (typeof window.requestIdleCallback === 'function') {
      const idleId = window.requestIdleCallback(run, {
        timeout: 1000
      });
      return () => {
        cancelled = true;
        window.cancelIdleCallback(idleId);
      };
    }
    timeoutId = window.setTimeout(run, 120);
    return () => {
      cancelled = true;
      window.clearTimeout(timeoutId);
    };
  }, [isInView, engineStatus, tracks]);
  useEffect(() => {
    const dpr = window.devicePixelRatio || 1;
    const progress = duration > 0 ? currentTime / duration : 0;
    const isDark = typeof document !== 'undefined' && document.documentElement.classList.contains('dark');
    const unplayedColor = isDark ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.12)';
    const mutedPlayedColor = isDark ? 'rgba(255,255,255,0.35)' : 'rgba(120,120,120,0.55)';
    const mutedUnplayedColor = isDark ? 'rgba(255,255,255,0.15)' : 'rgba(120,120,120,0.22)';
    tracks.forEach(track => {
      const canvas = canvasRefs.current[track.id];
      if (!canvas) return;
      const peaks = peaksBySrcRef.current[track.src];
      const rect = canvas.getBoundingClientRect();
      const w = rect.width;
      const h = TRACK_HEIGHT;
      canvas.width = w * dpr;
      canvas.height = h * dpr;
      const ctx = canvas.getContext('2d');
      ctx.scale(dpr, dpr);
      if (!peaks) {
        ctx.fillStyle = unplayedColor;
        ctx.fillRect(0, h / 2 - 1, w, 2);
        return;
      }
      const barW = Math.max(1, w / WAVEFORM_BARS - 1);
      const gap = 1;
      const halfH = h / 2;
      const playheadX = progress * w;
      for (let i = 0; i < peaks.length; i++) {
        const x = i / peaks.length * w;
        const {min, max} = peaks[i];
        const barH = Math.max(2, (max - min) * halfH);
        const isPlayed = x + barW < playheadX;
        const shouldRenderMutedStyle = !isAudible(track);
        if (shouldRenderMutedStyle) {
          ctx.fillStyle = isPlayed ? mutedPlayedColor : mutedUnplayedColor;
        } else {
          ctx.fillStyle = isPlayed ? '#C938FF' : unplayedColor;
        }
        ctx.fillRect(x, halfH - barH / 2, barW + gap, barH);
      }
    });
  }, [currentTime, duration, tracks, assetsVersion]);
  useEffect(() => {
    if (!isPlaying) return;
    let rafId = 0;
    let intervalId = 0;
    const updatePlayhead = () => {
      const ctx = ctxRef.current;
      if (!ctx) return true;
      const end = durationRef.current;
      const t = getTimelineTime(ctx);
      if (end > 0 && t >= end - END_EPSILON_SECONDS) {
        stopSources();
        setCurrentTime(end);
        setIsPlaying(false);
        return false;
      }
      setCurrentTime(t);
      return true;
    };
    const tick = () => {
      const shouldContinue = updatePlayhead();
      if (!shouldContinue) return;
      rafId = requestAnimationFrame(tick);
    };
    rafId = requestAnimationFrame(tick);
    intervalId = window.setInterval(() => {
      updatePlayhead();
    }, 100);
    return () => {
      cancelAnimationFrame(rafId);
      window.clearInterval(intervalId);
    };
  }, [isPlaying]);
  useEffect(() => () => {
    stopSources();
    const ctx = ctxRef.current;
    if (ctx) ctx.close();
    ctxRef.current = null;
  }, []);
  return <div ref={rootRef} className="not-prose my-6 w-full rounded-xl border border-zinc-200 bg-zinc-50/80 p-3 dark:border-white/20 dark:bg-zinc-900/50">
      <div className="mb-2 flex flex-wrap items-center gap-2">
        <button type="button" onClick={togglePlay} className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full transition-colors hover:opacity-90" style={{
    backgroundColor: '#C938FF',
    color: 'white'
  }} aria-label={isPlaying ? 'Pause' : 'Play'} title={isPlaying ? 'Pause' : 'Play'}>
          {isPlaying ? <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <rect x="6" y="4" width="4" height="16" rx="1" />
              <rect x="14" y="4" width="4" height="16" rx="1" />
            </svg> : <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M8 5v14l11-7L8 5z" />
            </svg>}
        </button>
        {useCases.map(u => <button key={u.id} type="button" onClick={() => switchUseCase(u.id)} className={`rounded-lg border px-3 py-1.5 text-sm transition-colors ${u.id === activeUseCaseId ? 'border-[#C938FF] bg-[#C938FF]/10 text-[#C938FF]' : 'border-zinc-300 bg-white text-zinc-700 hover:bg-zinc-100 dark:border-white/20 dark:bg-white/5 dark:text-white/80 dark:hover:bg-white/10'}`}>
            {u.label}
          </button>)}
        <div className="ml-auto text-xs text-zinc-500 dark:text-white/60">
          {formatTime(currentTime)} / {formatTime(duration)}
        </div>
      </div>

      {engineStatus === 'error' && <div className="mb-3 rounded-lg border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-900/50 dark:bg-red-900/20 dark:text-red-300">
          {engineError || 'Audio assets failed to load.'}
        </div>}
      {engineStatus === 'loading' && <div className="mb-3 text-xs text-zinc-500 dark:text-white/60">Loading audio assets...</div>}

      <div className="space-y-1">
        {tracks.map(track => <div key={track.id} className="flex items-center gap-2 rounded-lg border border-zinc-200/80 bg-white/60 py-1 dark:border-white/10 dark:bg-black/20">
            <div className="flex w-20 shrink-0 items-center justify-end gap-1">
              <button type="button" onClick={() => toggleMute(track.id)} className={`flex h-8 w-8 items-center justify-center rounded border text-xs font-medium transition-colors ${track.muted ? 'border-red-300 bg-red-100 text-red-700 dark:border-red-800 dark:bg-red-900/40 dark:text-red-300' : 'border-zinc-300 bg-zinc-100 text-zinc-600 hover:bg-zinc-200 dark:border-white/20 dark:bg-white/10 dark:text-white/80'}`} title={track.muted ? 'Unmute' : 'Mute'} aria-label={track.muted ? 'Unmute' : 'Mute'}>
                M
              </button>
              <button type="button" onClick={() => toggleSolo(track.id)} className={`flex h-8 w-8 items-center justify-center rounded border text-xs font-medium transition-colors ${track.solo ? 'border-amber-400 bg-amber-100 text-amber-800 dark:border-amber-600 dark:bg-amber-900/40 dark:text-amber-300' : 'border-zinc-300 bg-zinc-100 text-zinc-600 hover:bg-zinc-200 dark:border-white/20 dark:bg-white/10 dark:text-white/80'}`} title={track.solo ? 'Unsolo' : 'Solo'} aria-label={track.solo ? 'Unsolo' : 'Solo'}>
                S
              </button>
            </div>
            <div className="w-28 shrink-0 text-sm font-medium text-zinc-700 dark:text-white/90">
              {track.name}
            </div>
            <div onClick={handleSeekFromEvent} className="min-w-0 flex-1 cursor-pointer overflow-hidden rounded" style={{
    height: TRACK_HEIGHT
  }} aria-label="Seek">
              <canvas ref={setCanvasRef(track.id)} className="block h-full w-full" style={{
    height: TRACK_HEIGHT,
    width: '100%'
  }} />
            </div>
          </div>)}
      </div>

    </div>;
};

AudioShake is a platform for state-of-the-art AI source separation, transcription, and content analysis — powering post-production workflows, karaoke, dubbing, speech enhancement, music copyright compliance, improving ASR accuracy, and AI training data preparation — at scale, and in production.

<CardGroup cols={2}>
  <Card title="Get started" icon="rocket" href="/quickstart">
    Make your first API call in under 5 minutes.
  </Card>

  <Card title="Create an account" icon="user-plus" href="https://dashboard.audioshake.ai/auth/sign-up/">
    Start building for free — 10 credits included on sign up.
  </Card>
</CardGroup>

## Your first API call

Separate a track into vocals and instrumental in a single request:

```bash theme={null}
curl -X POST "https://api.audioshake.ai/tasks" \
  -H "Content-Type: application/json" \
  -H "x-api-key: your_api_key" \
  -d '{
    "url": "https://demos.audioshake.ai/demo-assets/shakeitup.mp3",
    "targets": [
      { "model": "vocals", "formats": ["wav"] },
      { "model": "instrumental", "formats": ["wav"] }
    ]
  }'
```

## What you can build

<CardGroup cols={2}>
  <Card title="Remove vocals from songs" icon="waveform-lines" href="/separate-stems">
    Isolate vocals, drums, bass, guitar, and more for remixing, karaoke, and sampling.
  </Card>

  <Card title="Isolate dialogue for dubbing" icon="film" href="/remove-dialogue-for-dubbing">
    Separate speech from music and effects for localization and post-production.
  </Card>

  <Card title="Transcribe and sync lyrics" icon="file-lines" href="/transcribe-lyrics">
    Generate word- and line-level timestamped lyrics from any song.
  </Card>

  <Card title="Separate speakers" icon="users" href="/multi-speaker-separation">
    Isolate individual speakers from multi-speaker recordings, even with overlapping speech.
  </Card>

  <Card title="Detect and identify music" icon="magnifying-glass" href="/detect-music-in-content">
    Find where music appears in podcasts, video, and broadcast content for compliance.
  </Card>

  <Card title="Clean up noisy speech" icon="microphone" href="/speech-denoising">
    Remove background noise from recordings for clearer speech and better transcription.
  </Card>
</CardGroup>

## Who this is for

* **AI companies** training speech, music, or multimodal models with clean, labeled data
* **Media companies** processing archives for dubbing, compliance, and cataloging
* **Developers** building karaoke apps, remix tools, practice platforms, and audio experiences

## Why AudioShake

* **State-of-the-art quality** — purpose-built models for music and speech, trained on licensed data
* **Production-ready** — async processing, webhooks, and batch support for any scale
* **Simple API** — one endpoint, multiple models per request, results in minutes
* **Audio + video** — process MP4 and MOV files directly, no pre-processing needed
* **On-device option** — run the same models locally with the [Local Inference SDK](/sdk/overview)

## Reference

<CardGroup cols={2}>
  <Card title="Models" icon="grid-2" href="/models">
    Browse all available models with pricing.
  </Card>

  <Card title="API Reference" icon="terminal" href="/api-reference/authentication">
    Full endpoint reference.
  </Card>

  <Card title="Tutorials" icon="graduation-cap" href="/separate-stems">
    Step-by-step guides for every workflow.
  </Card>

  <Card title="Billing & Credits" icon="credit-card" href="/billing">
    How pricing works.
  </Card>
</CardGroup>
