Fixes and Changes again
This commit is contained in:
@@ -101,6 +101,8 @@ export default function PlaybackModal({ open, melody, builtMelody, files, archet
|
||||
const info = melody?.information || {};
|
||||
const minSpeed = info.minSpeed || null;
|
||||
const maxSpeed = info.maxSpeed || null;
|
||||
// Note assignments: maps note index → bell number to fire
|
||||
const noteAssignments = melody?.default_settings?.noteAssignments || [];
|
||||
|
||||
const [steps, setSteps] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -152,10 +154,10 @@ export default function PlaybackModal({ open, melody, builtMelody, files, archet
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to binary fetch
|
||||
// Fall back to binary fetch — prefer uploaded file, then legacy melody.url
|
||||
const binaryUrl = builtMelody?.binary_url
|
||||
? `/api${builtMelody.binary_url}`
|
||||
: files?.binary_url || null;
|
||||
: files?.binary_url || melody?.url || null;
|
||||
|
||||
if (!binaryUrl) {
|
||||
setLoadError("No binary or archetype data available for this melody.");
|
||||
@@ -196,7 +198,28 @@ export default function PlaybackModal({ open, melody, builtMelody, files, archet
|
||||
}
|
||||
|
||||
const ctx = ensureAudioCtx();
|
||||
const stepValue = currentSteps[playFrom];
|
||||
const rawStepValue = currentSteps[playFrom];
|
||||
|
||||
// Apply note assignments: each note in the step maps to an assigned bell number
|
||||
// noteAssignments[noteIndex] = bellNumber (1-based). We rebuild the step value
|
||||
// using assigned bells instead of the raw ones.
|
||||
let stepValue = rawStepValue;
|
||||
if (noteAssignments.length > 0) {
|
||||
// Determine which notes (1-based) are active in this step
|
||||
const activeNotes = [];
|
||||
for (let bit = 0; bit < 16; bit++) {
|
||||
if (rawStepValue & (1 << bit)) activeNotes.push(bit + 1);
|
||||
}
|
||||
// For each active note, look up the noteAssignment by note index (note-1)
|
||||
// noteAssignments array is indexed by note position (0-based)
|
||||
stepValue = 0;
|
||||
for (const note of activeNotes) {
|
||||
const assignedBell = noteAssignments[note - 1];
|
||||
const bellToFire = (assignedBell && assignedBell > 0) ? assignedBell : note;
|
||||
stepValue |= 1 << (bellToFire - 1);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentStep(playFrom);
|
||||
playStep(ctx, stepValue, BEAT_DURATION_MS);
|
||||
|
||||
@@ -299,27 +322,65 @@ export default function PlaybackModal({ open, melody, builtMelody, files, archet
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bell visualizer */}
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{Array.from({ length: maxBell }, (_, i) => i + 1).map((b) => {
|
||||
const isActive = currentBells.includes(b);
|
||||
const isUsed = allBellsUsed.has(b);
|
||||
return (
|
||||
<div
|
||||
key={b}
|
||||
className="w-7 h-7 rounded-full flex items-center justify-center text-xs font-bold transition-all"
|
||||
style={{
|
||||
backgroundColor: isActive ? "var(--accent)" : isUsed ? "var(--bg-card-hover)" : "var(--bg-primary)",
|
||||
color: isActive ? "var(--bg-primary)" : isUsed ? "var(--text-secondary)" : "var(--border-primary)",
|
||||
border: `2px solid ${isActive ? "var(--accent)" : "var(--border-primary)"}`,
|
||||
transform: isActive ? "scale(1.2)" : "scale(1)",
|
||||
}}
|
||||
>
|
||||
{b}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* Note + Assignment visualizer */}
|
||||
{noteAssignments.length > 0 ? (
|
||||
<div>
|
||||
<p className="text-xs mb-2" style={mutedStyle}>Note → Assigned Bell</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{noteAssignments.map((assignedBell, noteIdx) => {
|
||||
const noteNum = noteIdx + 1;
|
||||
// A note is active if the current step has this note bit set (raw)
|
||||
const isActive = currentStep >= 0 && Boolean(steps[currentStep] & (1 << (noteNum - 1)));
|
||||
return (
|
||||
<div
|
||||
key={noteIdx}
|
||||
className="flex flex-col items-center rounded-md border transition-all"
|
||||
style={{
|
||||
minWidth: "36px",
|
||||
padding: "4px 6px",
|
||||
backgroundColor: isActive ? "var(--accent)" : "var(--bg-card-hover)",
|
||||
borderColor: isActive ? "var(--accent)" : "var(--border-primary)",
|
||||
transform: isActive ? "scale(1.1)" : "scale(1)",
|
||||
}}
|
||||
>
|
||||
<span className="text-xs font-bold leading-tight" style={{ color: isActive ? "var(--bg-primary)" : "var(--text-secondary)" }}>
|
||||
{noteNum}
|
||||
</span>
|
||||
<div className="w-full my-0.5" style={{ height: "1px", backgroundColor: isActive ? "rgba(255,255,255,0.4)" : "var(--border-primary)" }} />
|
||||
<span className="text-xs leading-tight" style={{ color: isActive ? "var(--bg-primary)" : "var(--text-muted)" }}>
|
||||
{assignedBell > 0 ? assignedBell : "—"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="flex gap-3 mt-1">
|
||||
<span className="text-xs" style={mutedStyle}>Top = Note, Bottom = Bell</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* Fallback: simple bell circles when no assignments */
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{Array.from({ length: maxBell }, (_, i) => i + 1).map((b) => {
|
||||
const isActive = currentBells.includes(b);
|
||||
const isUsed = allBellsUsed.has(b);
|
||||
return (
|
||||
<div
|
||||
key={b}
|
||||
className="w-7 h-7 rounded-full flex items-center justify-center text-xs font-bold transition-all"
|
||||
style={{
|
||||
backgroundColor: isActive ? "var(--accent)" : isUsed ? "var(--bg-card-hover)" : "var(--bg-primary)",
|
||||
color: isActive ? "var(--bg-primary)" : isUsed ? "var(--text-secondary)" : "var(--border-primary)",
|
||||
border: `2px solid ${isActive ? "var(--accent)" : "var(--border-primary)"}`,
|
||||
transform: isActive ? "scale(1.2)" : "scale(1)",
|
||||
}}
|
||||
>
|
||||
{b}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Play / Stop */}
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -329,7 +390,7 @@ export default function PlaybackModal({ open, melody, builtMelody, files, archet
|
||||
className="px-5 py-2 text-sm rounded-md font-medium transition-colors"
|
||||
style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
|
||||
>
|
||||
▶ Play
|
||||
Play
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
@@ -337,7 +398,7 @@ export default function PlaybackModal({ open, melody, builtMelody, files, archet
|
||||
className="px-5 py-2 text-sm rounded-md font-medium transition-colors"
|
||||
style={{ backgroundColor: "var(--danger-btn)", color: "var(--text-white)" }}
|
||||
>
|
||||
■ Stop
|
||||
Stop
|
||||
</button>
|
||||
)}
|
||||
<span className="text-xs" style={mutedStyle}>Loops continuously</span>
|
||||
|
||||
Reference in New Issue
Block a user