import React, { useCallback, useEffect, useRef, useState } from 'react';
import {
deleteMusicTrack,
generateMusic,
generateMusicLyrics,
getMusicLibrary,
getMusicProviders,
getMusicStatus,
getMusicModels,
getMusicCredits,
extendMusicTrack,
removeVocals,
} from '../../api';
import './MusicStudio.css';
/* ─────────────────────────────────────────────
데이터 상수
───────────────────────────────────────────── */
const GENRES = [
{ id: 'lofi', label: 'Lo-Fi', icon: '📻', color: '#f5a623', desc: 'Warm · Nostalgic' },
{ id: 'electronic', label: 'Electronic', icon: '⚡', color: '#60a5fa', desc: 'Pulse · Synth' },
{ id: 'jazz', label: 'Jazz', icon: '🎷', color: '#c084fc', desc: 'Smooth · Soul' },
{ id: 'classical', label: 'Classical', icon: '🎻', color: '#f9b6b1', desc: 'Orchestral · Grand' },
{ id: 'ambient', label: 'Ambient', icon: '🌊', color: '#4aad8b', desc: 'Space · Float' },
{ id: 'hiphop', label: 'Hip-Hop', icon: '🎤', color: '#f472b6', desc: 'Beat · Urban' },
{ id: 'rock', label: 'Rock', icon: '🎸', color: '#e85c3a', desc: 'Raw · Drive' },
{ id: 'cinematic', label: 'Cinematic', icon: '🎬', color: '#fbbf24', desc: 'Epic · Sweeping' },
];
const MOODS = [
{ id: 'energetic', label: 'Energetic', color: '#e85c3a' },
{ id: 'chill', label: 'Chill', color: '#60a5fa' },
{ id: 'dark', label: 'Dark', color: '#9333ea' },
{ id: 'uplifting', label: 'Uplifting', color: '#f5a623' },
{ id: 'romantic', label: 'Romantic', color: '#f472b6' },
{ id: 'epic', label: 'Epic', color: '#fbbf24' },
{ id: 'melancholic', label: 'Melancholic', color: '#4aad8b' },
];
const INSTRUMENTS = [
{ id: 'piano', label: 'Piano', freq: '261Hz' },
{ id: 'guitar', label: 'Guitar', freq: '82Hz' },
{ id: 'drums', label: 'Drums', freq: '60Hz' },
{ id: 'synth', label: 'Synth', freq: '440Hz' },
{ id: 'bass', label: 'Bass', freq: '41Hz' },
{ id: 'strings', label: 'Strings', freq: '196Hz' },
{ id: 'brass', label: 'Brass', freq: '146Hz' },
{ id: 'flute', label: 'Flute', freq: '523Hz' },
{ id: 'violin', label: 'Violin', freq: '659Hz' },
{ id: 'choir', label: 'Choir', freq: '330Hz' },
];
const DURATIONS = [
{ id: '30s', label: '0:30', sec: 30 },
{ id: '60s', label: '1:00', sec: 60 },
{ id: '90s', label: '1:30', sec: 90 },
{ id: '2m', label: '2:00', sec: 120 },
{ id: '3m', label: '3:00', sec: 180 },
{ id: '5m', label: '5:00', sec: 300 },
];
const BPM_PRESETS = [
{ label: 'Slow', bpm: 70 },
{ label: 'Mid', bpm: 100 },
{ label: 'Fast', bpm: 130 },
{ label: 'EDM', bpm: 160 },
];
const KEYS = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
const SCALES = ['Major','Minor','Dorian','Phrygian','Lydian','Mixolydian'];
/* 시뮬레이션 폴백용 단계 메시지 */
const SIM_STEPS = [
{ msg: 'Analyzing musical patterns…', pct: 16 },
{ msg: 'Building harmonic structure…', pct: 32 },
{ msg: 'Rendering instruments…', pct: 52 },
{ msg: 'Mixing and mastering…', pct: 72 },
{ msg: 'Applying final polish…', pct: 90 },
{ msg: 'Track ready!', pct: 100 },
];
/* ─────────────────────────────────────────────
유틸
───────────────────────────────────────────── */
const pad = (n) => String(Math.floor(n)).padStart(2, '0');
const fmtTime = (s) => `${pad(s / 60)}:${pad(s % 60)}`;
/* ─────────────────────────────────────────────
Loading Skeleton
───────────────────────────────────────────── */
const SkeletonCard = () => (
);
/* ─────────────────────────────────────────────
Waveform Canvas
───────────────────────────────────────────── */
const WaveformCanvas = ({ isGenerating, accentColor }) => {
const canvasRef = useRef(null);
const rafRef = useRef(null);
const phaseRef = useRef(0);
const intensityRef = useRef(0);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio || 1;
const resize = () => {
dpr = window.devicePixelRatio || 1;
canvas.width = canvas.offsetWidth * dpr;
canvas.height = canvas.offsetHeight * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
resize();
const ro = new ResizeObserver(resize);
ro.observe(canvas);
const draw = () => {
const w = canvas.offsetWidth, h = canvas.offsetHeight;
ctx.clearRect(0, 0, w, h);
const target = isGenerating ? 1.0 : 0.25;
intensityRef.current += (target - intensityRef.current) * 0.04;
const intensity = intensityRef.current;
const layers = [
{ amp: h * 0.38 * intensity, freq: 1.6, speed: 0.022, alpha: 0.9, lw: 2 },
{ amp: h * 0.22 * intensity, freq: 2.8, speed: 0.034, alpha: 0.5, lw: 1.2 },
{ amp: h * 0.12 * intensity, freq: 4.5, speed: 0.055, alpha: 0.3, lw: 0.8 },
];
layers.forEach(({ amp, freq, speed, alpha, lw }, li) => {
ctx.beginPath();
ctx.strokeStyle = accentColor;
ctx.globalAlpha = alpha;
ctx.lineWidth = lw;
ctx.shadowBlur = isGenerating ? 16 : 6;
ctx.shadowColor = accentColor;
for (let x = 0; x <= w; x += 2) {
const t = (x / w) * Math.PI * 2 * freq + phaseRef.current * (1 + li * speed * 10);
const h2 = Math.sin(t * 2.1 + li) * 0.35;
const h3 = Math.sin(t * 0.45 + li * 0.7) * 0.18;
const y = h / 2 + (Math.sin(t) + h2 + h3) * amp;
x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
}
ctx.stroke();
});
ctx.globalAlpha = 1;
ctx.shadowBlur = 0;
phaseRef.current += isGenerating ? 0.055 : 0.018;
rafRef.current = requestAnimationFrame(draw);
};
draw();
return () => {
ro.disconnect();
cancelAnimationFrame(rafRef.current);
};
}, [isGenerating, accentColor]);
return ;
};
/* ─────────────────────────────────────────────
Sonic Radar (헤더 비주얼 — 모듈 로드 시 1회 계산)
───────────────────────────────────────────── */
const RADAR_N = 48;
const RADAR_INNER = 26; // 중심~바 시작 거리(SVG unit)
const RADAR_DATA = Array.from({ length: RADAR_N }, (_, i) => ({
angle: `${((i / RADAR_N) * 360).toFixed(2)}deg`,
delay: `${((i / RADAR_N) * 1.8).toFixed(2)}s`,
rnd: `${(0.22 + Math.random() * 0.78).toFixed(2)}`,
}));
const SonicRadar = ({ isGenerating, accentColor }) => (
{/* SVG — 링·크로스헤어·스윕 */}
{/* CSS 방사형 바 (HTML div) */}
{RADAR_DATA.map((bar, i) => (
))}
{/* 센터 도트 */}
{/* 상태 레이블 */}
{isGenerating ? 'GENERATING' : 'STANDBY'}
);
/* ─────────────────────────────────────────────
Progress Bar
───────────────────────────────────────────── */
const GenerationProgress = ({ progress, stepMsg }) => (
);
/* ─────────────────────────────────────────────
Audio Player (실제