feat: allow multiple simultaneous solo tracks
Replace single-track solo (radio) with multi-solo toggle. No longer clears solo on other tracks when toggling one track.
This commit is contained in:
+15
-12
@@ -9198,8 +9198,7 @@ const App = () => {
|
||||
const [saveProjectModalOpen, setSaveProjectModalOpen] = useState(false);
|
||||
const [saveAsModalOpen, setSaveAsModalOpen] = useState(false);
|
||||
const [openProjectModalOpen, setOpenProjectModalOpen] = useState(false);
|
||||
const soloedTrack = tracks.find(t => t.solo);
|
||||
const soloedTrackId = soloedTrack ? soloedTrack.id : null;
|
||||
const hasAnySolo = tracks.some(t => t.solo);
|
||||
const [toastMessage, setToastMessage] = useState(null);
|
||||
const [audioDevices, setAudioDevices] = useState([]);
|
||||
const [midiDevices, setMidiDevices] = useState([]);
|
||||
@@ -12985,12 +12984,16 @@ const App = () => {
|
||||
// If selection cleared by user, play linearly (don't loop)
|
||||
if (!selectionCleared && isLoopingSelection && selLeft !== null && selRight !== null) {
|
||||
if (selRight > selLeft && updatedTime >= selRight) {
|
||||
if (soloedTrackId !== null || selectionMode === 'local') {
|
||||
if (hasAnySolo || selectionMode === 'local') {
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = selLeft;
|
||||
startAudioTimeRef.current = context.currentTime;
|
||||
const soloTid = soloedTrackId !== null ? soloedTrackId : localSelectionTrackId;
|
||||
startLocalTrackPlayback(soloTid, selLeft);
|
||||
if (hasAnySolo) {
|
||||
const soloed = tracks.filter(t => t.solo);
|
||||
soloed.forEach(t => startLocalTrackPlayback(t.id, selLeft));
|
||||
} else {
|
||||
startLocalTrackPlayback(localSelectionTrackId, selLeft);
|
||||
}
|
||||
setCurrentTime(selLeft);
|
||||
setIsPlaying(true);
|
||||
} else {
|
||||
@@ -13164,9 +13167,9 @@ const App = () => {
|
||||
|
||||
const startTrackPlayback = offsetTime => {
|
||||
const context = getAudioContext();
|
||||
const hasSolo = activeTracks.some(t => t.solo) || soloedTrackId !== null;
|
||||
const hasSolo = activeTracks.some(t => t.solo);
|
||||
activeTracks.forEach(track => {
|
||||
const isPlayable = hasSolo ? track.id === soloedTrackId || track.solo : !track.muted;
|
||||
const isPlayable = hasSolo ? track.solo : !track.muted;
|
||||
if (!isPlayable) return;
|
||||
|
||||
const gainNode = getOrCreateTrackNode(track, context);
|
||||
@@ -20022,8 +20025,8 @@ const App = () => {
|
||||
toggleTrackSoloEvaluate(track.id);
|
||||
},
|
||||
title: "Solo",
|
||||
className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 ${soloedTrackId === track.id || track.solo ? 'bg-amber-950 text-amber-400 border-amber-600' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`
|
||||
}, /*#__PURE__*/React.createElement("i", { "data-lucide": soloedTrackId === track.id || track.solo ? "headphones" : "headphone-off", className: "w-3 h-3" })), /*#__PURE__*/React.createElement("button", {
|
||||
className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 ${track.solo ? 'bg-amber-950 text-amber-400 border-amber-600' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`
|
||||
}, /*#__PURE__*/React.createElement("i", { "data-lucide": track.solo ? "headphones" : "headphone-off", className: "w-3 h-3" })), /*#__PURE__*/React.createElement("button", {
|
||||
onClick: e => {
|
||||
e.stopPropagation();
|
||||
toggleTrackDrum(track.id);
|
||||
@@ -20548,7 +20551,7 @@ const App = () => {
|
||||
e.stopPropagation();
|
||||
toggleTrackSoloEvaluate(vTrack.id);
|
||||
},
|
||||
className: `px-1.5 py-0.5 text-xs rounded font-mono font-bold border ${soloedTrackId === vTrack.id || vTrack.solo ? 'bg-amber-950 text-amber-400 border-amber-600' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`
|
||||
className: `px-1.5 py-0.5 text-xs rounded font-mono font-bold border ${vTrack.solo ? 'bg-amber-950 text-amber-400 border-amber-600' : 'bg-zinc-800 text-zinc-400 border-transparent hover:text-zinc-200'}`
|
||||
}, "S"))), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex flex-col gap-2.5 text-[14px] mb-3"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
@@ -20992,9 +20995,9 @@ const App = () => {
|
||||
className: "text-amber-400 font-semibold uppercase text-xs"
|
||||
}, "Local Sel"), selectionMode === 'global' && /*#__PURE__*/React.createElement("span", {
|
||||
className: "text-purple-400 font-semibold uppercase text-xs"
|
||||
}, "Global Sel"), soloedTrackId && /*#__PURE__*/React.createElement("span", {
|
||||
}, "Global Sel"), hasAnySolo && /*#__PURE__*/React.createElement("span", {
|
||||
className: "text-amber-500 font-semibold"
|
||||
}, "Solo: ID ", soloedTrackId), isLoopingSelection && selectionMode === 'local' && /*#__PURE__*/React.createElement("span", {
|
||||
}, "Solo: ", tracks.filter(t => t.solo).length, " track(s)"), isLoopingSelection && selectionMode === 'local' && /*#__PURE__*/React.createElement("span", {
|
||||
className: "text-emerald-400 font-semibold uppercase text-xs"
|
||||
}, "Solo Loop"), isLoopingSelection && selectionMode !== 'local' && /*#__PURE__*/React.createElement("span", {
|
||||
className: "text-cyan-400 font-semibold uppercase text-xs"
|
||||
|
||||
Reference in New Issue
Block a user