editando patterns na playlist
Deploy / Deploy (push) Successful in 1m55s
Details
Deploy / Deploy (push) Successful in 1m55s
Details
This commit is contained in:
parent
6bb8cb8dad
commit
f7c81dd1de
|
|
@ -188,10 +188,6 @@ export function renderAudioEditor() {
|
||||||
|
|
||||||
if (!audioEditor || !existingTrackContainer) return;
|
if (!audioEditor || !existingTrackContainer) return;
|
||||||
|
|
||||||
// ✅ Salva o scroll atual (senão toda edição “pula” pro início)
|
|
||||||
const prevScrollLeft = existingTrackContainer.scrollLeft || 0;
|
|
||||||
const prevScrollTop = existingTrackContainer.scrollTop || 0;
|
|
||||||
|
|
||||||
_ensureGlobalPlaylistSelectionFields();
|
_ensureGlobalPlaylistSelectionFields();
|
||||||
_installPlaylistKeybindOnce();
|
_installPlaylistKeybindOnce();
|
||||||
|
|
||||||
|
|
@ -808,47 +804,22 @@ export function renderAudioEditor() {
|
||||||
newTrackContainer.addEventListener("mousedown", (e) => {
|
newTrackContainer.addEventListener("mousedown", (e) => {
|
||||||
document.getElementById("timeline-context-menu").style.display = "none";
|
document.getElementById("timeline-context-menu").style.display = "none";
|
||||||
document.getElementById("ruler-context-menu").style.display = "none";
|
document.getElementById("ruler-context-menu").style.display = "none";
|
||||||
|
|
||||||
const clipElement = e.target.closest(".timeline-clip");
|
const clipElement = e.target.closest(".timeline-clip");
|
||||||
const isBasslineClip =
|
|
||||||
!!(clipElement && clipElement.classList.contains("bassline-clip"));
|
|
||||||
|
|
||||||
// ✅ limpa seleções ao clicar no vazio (sem mexer no RMB)
|
|
||||||
if (!clipElement && e.button !== 2) {
|
if (!clipElement && e.button !== 2) {
|
||||||
if (appState.global.selectedClipId) {
|
if (appState.global.selectedClipId) {
|
||||||
appState.global.selectedClipId = null;
|
appState.global.selectedClipId = null;
|
||||||
}
|
|
||||||
if (appState.global.selectedPlaylistClipId) {
|
|
||||||
appState.global.selectedPlaylistClipId = null;
|
|
||||||
appState.global.selectedPlaylistPatternIndex = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
newTrackContainer
|
newTrackContainer
|
||||||
.querySelectorAll(".timeline-clip.selected")
|
.querySelectorAll(".timeline-clip.selected")
|
||||||
.forEach((c) => c.classList.remove("selected"));
|
.forEach((c) => c.classList.remove("selected"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const currentPixelsPerSecond = getPixelsPerSecond();
|
const currentPixelsPerSecond = getPixelsPerSecond();
|
||||||
const handle = e.target.closest(".clip-resize-handle");
|
const handle = e.target.closest(".clip-resize-handle");
|
||||||
const patternHandle = e.target.closest(".pattern-resize-handle");
|
|
||||||
|
|
||||||
// ✅ se clicou num clip de áudio, deseleciona a pattern da playlist
|
// Slice Tool
|
||||||
if (clipElement && !isBasslineClip && e.button === 0) {
|
if (appState.global.sliceToolActive && clipElement && !clipElement.classList.contains("bassline-clip")) {
|
||||||
if (appState.global.selectedPlaylistClipId) {
|
|
||||||
appState.global.selectedPlaylistClipId = null;
|
|
||||||
appState.global.selectedPlaylistPatternIndex = null;
|
|
||||||
newTrackContainer
|
|
||||||
.querySelectorAll(".bassline-clip.selected")
|
|
||||||
.forEach((c) => c.classList.remove("selected"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice Tool (áudio apenas)
|
|
||||||
if (
|
|
||||||
appState.global.sliceToolActive &&
|
|
||||||
clipElement &&
|
|
||||||
!clipElement.classList.contains("bassline-clip")
|
|
||||||
) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const clipId = clipElement.dataset.clipId;
|
const clipId = clipElement.dataset.clipId;
|
||||||
|
|
@ -872,186 +843,7 @@ export function renderAudioEditor() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================
|
// Resize Handle
|
||||||
// ✅ BASSLINE / PATTERN CLIPS (drag horizontal + resize L/R)
|
|
||||||
// =========================================================
|
|
||||||
if (isBasslineClip && e.button === 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
_ensureGlobalPlaylistSelectionFields();
|
|
||||||
|
|
||||||
const patternIndex = Number(clipElement.dataset.patternIndex ?? 0);
|
|
||||||
const plClipId = String(clipElement.dataset.plClipId || "");
|
|
||||||
if (!plClipId) return;
|
|
||||||
|
|
||||||
// seleção visual/estado
|
|
||||||
appState.global.selectedPlaylistClipId = plClipId;
|
|
||||||
appState.global.selectedPlaylistPatternIndex = patternIndex;
|
|
||||||
|
|
||||||
// desmarca seleção de áudio se tiver
|
|
||||||
if (appState.global.selectedClipId) {
|
|
||||||
appState.global.selectedClipId = null;
|
|
||||||
newTrackContainer
|
|
||||||
.querySelectorAll(".timeline-clip.selected")
|
|
||||||
.forEach((c) => c.classList.remove("selected"));
|
|
||||||
}
|
|
||||||
|
|
||||||
newTrackContainer
|
|
||||||
.querySelectorAll(".bassline-clip.selected")
|
|
||||||
.forEach((c) => c.classList.remove("selected"));
|
|
||||||
clipElement.classList.add("selected");
|
|
||||||
|
|
||||||
// (opcional) sync de seleção
|
|
||||||
sendActionSafe({
|
|
||||||
type: "SELECT_PLAYLIST_PATTERN_CLIP",
|
|
||||||
patternIndex,
|
|
||||||
clipId: plClipId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const model = _getPlaylistClipModel(patternIndex, plClipId);
|
|
||||||
if (!model) return;
|
|
||||||
|
|
||||||
const initialMouseX = e.clientX;
|
|
||||||
const initialScrollLeft = scrollEl.scrollLeft;
|
|
||||||
|
|
||||||
const initialPosTicks = Number(model.pos || 0);
|
|
||||||
const initialLenTicks = Math.max(
|
|
||||||
PL_MIN_LEN_TICKS,
|
|
||||||
Number(model.len || PL_MIN_LEN_TICKS)
|
|
||||||
);
|
|
||||||
const initialEndTicks = initialPosTicks + initialLenTicks;
|
|
||||||
|
|
||||||
const previewUpdate = (posTicks, lenTicks) => {
|
|
||||||
const leftPx = _ticksToPx(posTicks, stepWidthPx);
|
|
||||||
const widthPx = Math.max(1, _ticksToPx(lenTicks, stepWidthPx));
|
|
||||||
clipElement.style.left = `${leftPx}px`;
|
|
||||||
clipElement.style.width = `${widthPx}px`;
|
|
||||||
// mantém grid alinhado com a timeline
|
|
||||||
clipElement.style.backgroundPosition = `-${leftPx}px 0px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---------- RESIZE ----------
|
|
||||||
if (patternHandle) {
|
|
||||||
const handleType = patternHandle.classList.contains("left")
|
|
||||||
? "left"
|
|
||||||
: "right";
|
|
||||||
|
|
||||||
clipElement.classList.add("dragging");
|
|
||||||
|
|
||||||
const onMouseMove = (moveEvent) => {
|
|
||||||
const deltaPx =
|
|
||||||
(moveEvent.clientX - initialMouseX) +
|
|
||||||
(scrollEl.scrollLeft - initialScrollLeft);
|
|
||||||
|
|
||||||
const deltaTicks = _pxToTicks(deltaPx, stepWidthPx);
|
|
||||||
|
|
||||||
let newPos = initialPosTicks;
|
|
||||||
let newLen = initialLenTicks;
|
|
||||||
|
|
||||||
if (handleType === "right") {
|
|
||||||
let newEnd = _snapTicks(initialEndTicks + deltaTicks, PL_SNAP_TICKS);
|
|
||||||
newEnd = Math.max(initialPosTicks + PL_MIN_LEN_TICKS, newEnd);
|
|
||||||
newLen = newEnd - initialPosTicks;
|
|
||||||
} else {
|
|
||||||
newPos = _snapTicks(initialPosTicks + deltaTicks, PL_SNAP_TICKS);
|
|
||||||
newPos = Math.max(0, newPos);
|
|
||||||
newPos = Math.min(newPos, initialEndTicks - PL_MIN_LEN_TICKS);
|
|
||||||
newLen = initialEndTicks - newPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
previewUpdate(newPos, newLen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMouseUp = () => {
|
|
||||||
clipElement.classList.remove("dragging");
|
|
||||||
document.removeEventListener("mousemove", onMouseMove);
|
|
||||||
document.removeEventListener("mouseup", onMouseUp);
|
|
||||||
|
|
||||||
// converte estado final (px -> ticks) com snap
|
|
||||||
const finalLeftPx = clipElement.offsetLeft;
|
|
||||||
const finalWidthPx = clipElement.offsetWidth;
|
|
||||||
|
|
||||||
let finalPos = _pxToTicks(finalLeftPx, stepWidthPx);
|
|
||||||
let finalLen = _pxToTicks(finalWidthPx, stepWidthPx);
|
|
||||||
|
|
||||||
finalPos = _snapTicks(finalPos, PL_SNAP_TICKS);
|
|
||||||
finalLen = _snapTicks(finalLen, PL_SNAP_TICKS);
|
|
||||||
|
|
||||||
finalPos = Math.max(0, finalPos);
|
|
||||||
finalLen = Math.max(PL_MIN_LEN_TICKS, finalLen);
|
|
||||||
|
|
||||||
_updatePlaylistClipLocal(patternIndex, plClipId, {
|
|
||||||
pos: finalPos,
|
|
||||||
len: finalLen,
|
|
||||||
});
|
|
||||||
|
|
||||||
sendActionSafe({
|
|
||||||
type: "UPDATE_PLAYLIST_PATTERN_CLIP",
|
|
||||||
patternIndex,
|
|
||||||
clipId: plClipId,
|
|
||||||
pos: finalPos,
|
|
||||||
len: finalLen,
|
|
||||||
});
|
|
||||||
|
|
||||||
renderAudioEditor();
|
|
||||||
restartAudioEditorIfPlaying();
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("mousemove", onMouseMove);
|
|
||||||
document.addEventListener("mouseup", onMouseUp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- DRAG (horizontal apenas) ----------
|
|
||||||
clipElement.classList.add("dragging");
|
|
||||||
|
|
||||||
const onMouseMove = (moveEvent) => {
|
|
||||||
const deltaPx =
|
|
||||||
(moveEvent.clientX - initialMouseX) +
|
|
||||||
(scrollEl.scrollLeft - initialScrollLeft);
|
|
||||||
|
|
||||||
let newPos = initialPosTicks + _pxToTicks(deltaPx, stepWidthPx);
|
|
||||||
newPos = _snapTicks(newPos, PL_SNAP_TICKS);
|
|
||||||
newPos = Math.max(0, newPos);
|
|
||||||
|
|
||||||
previewUpdate(newPos, initialLenTicks);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMouseUp = () => {
|
|
||||||
clipElement.classList.remove("dragging");
|
|
||||||
document.removeEventListener("mousemove", onMouseMove);
|
|
||||||
document.removeEventListener("mouseup", onMouseUp);
|
|
||||||
|
|
||||||
const finalLeftPx = clipElement.offsetLeft;
|
|
||||||
let finalPos = _pxToTicks(finalLeftPx, stepWidthPx);
|
|
||||||
finalPos = _snapTicks(finalPos, PL_SNAP_TICKS);
|
|
||||||
finalPos = Math.max(0, finalPos);
|
|
||||||
|
|
||||||
_updatePlaylistClipLocal(patternIndex, plClipId, {
|
|
||||||
pos: finalPos,
|
|
||||||
len: initialLenTicks,
|
|
||||||
});
|
|
||||||
|
|
||||||
sendActionSafe({
|
|
||||||
type: "UPDATE_PLAYLIST_PATTERN_CLIP",
|
|
||||||
patternIndex,
|
|
||||||
clipId: plClipId,
|
|
||||||
pos: finalPos,
|
|
||||||
len: initialLenTicks,
|
|
||||||
});
|
|
||||||
|
|
||||||
renderAudioEditor();
|
|
||||||
restartAudioEditorIfPlaying();
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("mousemove", onMouseMove);
|
|
||||||
document.addEventListener("mouseup", onMouseUp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================
|
|
||||||
// Resize Handle (ÁUDIO)
|
|
||||||
// =========================================================
|
|
||||||
if (handle) {
|
if (handle) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -1067,8 +859,7 @@ export function renderAudioEditor() {
|
||||||
const initialStartTime = clip.startTimeInSeconds;
|
const initialStartTime = clip.startTimeInSeconds;
|
||||||
const initialDuration = clip.durationInSeconds;
|
const initialDuration = clip.durationInSeconds;
|
||||||
const initialOffset = clip.offset || 0;
|
const initialOffset = clip.offset || 0;
|
||||||
const initialOriginalDuration =
|
const initialOriginalDuration = clip.originalDuration || clip.buffer.duration;
|
||||||
clip.originalDuration || clip.buffer.duration;
|
|
||||||
const bufferStartTime = initialStartTime - initialOffset;
|
const bufferStartTime = initialStartTime - initialOffset;
|
||||||
|
|
||||||
const onMouseMove = (moveEvent) => {
|
const onMouseMove = (moveEvent) => {
|
||||||
|
|
@ -1081,21 +872,16 @@ export function renderAudioEditor() {
|
||||||
newEndTime = Math.max(initialStartTime + secondsPerStep, newEndTime);
|
newEndTime = Math.max(initialStartTime + secondsPerStep, newEndTime);
|
||||||
const maxEndTime = bufferStartTime + initialOriginalDuration;
|
const maxEndTime = bufferStartTime + initialOriginalDuration;
|
||||||
newEndTime = Math.min(newEndTime, maxEndTime);
|
newEndTime = Math.min(newEndTime, maxEndTime);
|
||||||
clipElement.style.width = `${
|
clipElement.style.width = `${(newEndTime - initialStartTime) * currentPixelsPerSecond}px`;
|
||||||
(newEndTime - initialStartTime) * currentPixelsPerSecond
|
|
||||||
}px`;
|
|
||||||
} else if (handleType === "left") {
|
} else if (handleType === "left") {
|
||||||
let newLeftPx = initialLeftPx + deltaX;
|
let newLeftPx = initialLeftPx + deltaX;
|
||||||
let newStartTime = newLeftPx / currentPixelsPerSecond;
|
let newStartTime = newLeftPx / currentPixelsPerSecond;
|
||||||
newStartTime = quantizeTime(newStartTime);
|
newStartTime = quantizeTime(newStartTime);
|
||||||
const minStartTime =
|
const minStartTime = initialStartTime + initialDuration - secondsPerStep;
|
||||||
initialStartTime + initialDuration - secondsPerStep;
|
|
||||||
newStartTime = Math.min(newStartTime, minStartTime);
|
newStartTime = Math.min(newStartTime, minStartTime);
|
||||||
newStartTime = Math.max(bufferStartTime, newStartTime);
|
newStartTime = Math.max(bufferStartTime, newStartTime);
|
||||||
const newLeftFinalPx = newStartTime * currentPixelsPerSecond;
|
const newLeftFinalPx = newStartTime * currentPixelsPerSecond;
|
||||||
const newWidthFinalPx =
|
const newWidthFinalPx = (initialStartTime + initialDuration - newStartTime) * currentPixelsPerSecond;
|
||||||
(initialStartTime + initialDuration - newStartTime) *
|
|
||||||
currentPixelsPerSecond;
|
|
||||||
clipElement.style.left = `${newLeftFinalPx}px`;
|
clipElement.style.left = `${newLeftFinalPx}px`;
|
||||||
clipElement.style.width = `${newWidthFinalPx}px`;
|
clipElement.style.width = `${newWidthFinalPx}px`;
|
||||||
}
|
}
|
||||||
|
|
@ -1105,27 +891,22 @@ export function renderAudioEditor() {
|
||||||
let newDuration = newWidthPx / currentPixelsPerSecond;
|
let newDuration = newWidthPx / currentPixelsPerSecond;
|
||||||
let newEndTime = quantizeTime(initialStartTime + newDuration);
|
let newEndTime = quantizeTime(initialStartTime + newDuration);
|
||||||
newEndTime = Math.max(initialStartTime + secondsPerStep, newEndTime);
|
newEndTime = Math.max(initialStartTime + secondsPerStep, newEndTime);
|
||||||
clipElement.style.width = `${
|
clipElement.style.width = `${(newEndTime - initialStartTime) * currentPixelsPerSecond}px`;
|
||||||
(newEndTime - initialStartTime) * currentPixelsPerSecond
|
|
||||||
}px`;
|
|
||||||
} else if (handleType === "left") {
|
} else if (handleType === "left") {
|
||||||
let newLeftPx = initialLeftPx + deltaX;
|
let newLeftPx = initialLeftPx + deltaX;
|
||||||
let newStartTime = newLeftPx / currentPixelsPerSecond;
|
let newStartTime = newLeftPx / currentPixelsPerSecond;
|
||||||
newStartTime = quantizeTime(newStartTime);
|
newStartTime = quantizeTime(newStartTime);
|
||||||
const minStartTime =
|
const minStartTime = initialStartTime + initialDuration - secondsPerStep;
|
||||||
initialStartTime + initialDuration - secondsPerStep;
|
|
||||||
newStartTime = Math.min(newStartTime, minStartTime);
|
newStartTime = Math.min(newStartTime, minStartTime);
|
||||||
const newLeftFinalPx = newStartTime * currentPixelsPerSecond;
|
const newLeftFinalPx = newStartTime * currentPixelsPerSecond;
|
||||||
const newWidthFinalPx =
|
const newWidthFinalPx = (initialStartTime + initialDuration - newStartTime) * currentPixelsPerSecond;
|
||||||
(initialStartTime + initialDuration - newStartTime) *
|
|
||||||
currentPixelsPerSecond;
|
|
||||||
clipElement.style.left = `${newLeftFinalPx}px`;
|
clipElement.style.left = `${newLeftFinalPx}px`;
|
||||||
clipElement.style.width = `${newWidthFinalPx}px`;
|
clipElement.style.width = `${newWidthFinalPx}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseUp = () => {
|
const onMouseUp = (upEvent) => {
|
||||||
document.removeEventListener("mousemove", onMouseMove);
|
document.removeEventListener("mousemove", onMouseMove);
|
||||||
document.removeEventListener("mouseup", onMouseUp);
|
document.removeEventListener("mouseup", onMouseUp);
|
||||||
const finalLeftPx = clipElement.offsetLeft;
|
const finalLeftPx = clipElement.offsetLeft;
|
||||||
|
|
@ -1136,70 +917,26 @@ export function renderAudioEditor() {
|
||||||
if (appState.global.resizeMode === "trim") {
|
if (appState.global.resizeMode === "trim") {
|
||||||
const newOffset = newStartTime - bufferStartTime;
|
const newOffset = newStartTime - bufferStartTime;
|
||||||
if(handleType === "right") {
|
if(handleType === "right") {
|
||||||
updateAudioClipProperties(clipId, {
|
updateAudioClipProperties(clipId, { durationInSeconds: newDuration, pitch: 0 });
|
||||||
durationInSeconds: newDuration,
|
sendActionSafe({ type: "UPDATE_AUDIO_CLIP", clipId, props: { durationInSeconds: newDuration, pitch: 0 } });
|
||||||
pitch: 0,
|
|
||||||
});
|
|
||||||
sendActionSafe({
|
|
||||||
type: "UPDATE_AUDIO_CLIP",
|
|
||||||
clipId,
|
|
||||||
props: { durationInSeconds: newDuration, pitch: 0 },
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
updateAudioClipProperties(clipId, {
|
updateAudioClipProperties(clipId, { startTimeInSeconds: newStartTime, durationInSeconds: newDuration, offset: newOffset, pitch: 0 });
|
||||||
startTimeInSeconds: newStartTime,
|
sendActionSafe({ type: "UPDATE_AUDIO_CLIP", clipId, props: { startTimeInSeconds: newStartTime, durationInSeconds: newDuration, offset: newOffset, pitch: 0 } });
|
||||||
durationInSeconds: newDuration,
|
|
||||||
offset: newOffset,
|
|
||||||
pitch: 0,
|
|
||||||
});
|
|
||||||
sendActionSafe({
|
|
||||||
type: "UPDATE_AUDIO_CLIP",
|
|
||||||
clipId,
|
|
||||||
props: {
|
|
||||||
startTimeInSeconds: newStartTime,
|
|
||||||
durationInSeconds: newDuration,
|
|
||||||
offset: newOffset,
|
|
||||||
pitch: 0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const newPlaybackRate = initialOriginalDuration / newDuration;
|
const newPlaybackRate = initialOriginalDuration / newDuration;
|
||||||
const newPitch = 12 * Math.log2(newPlaybackRate);
|
const newPitch = 12 * Math.log2(newPlaybackRate);
|
||||||
if(handleType === "right") {
|
if(handleType === "right") {
|
||||||
updateAudioClipProperties(clipId, {
|
updateAudioClipProperties(clipId, { durationInSeconds: newDuration, pitch: newPitch, offset: 0 });
|
||||||
durationInSeconds: newDuration,
|
sendActionSafe({ type: "UPDATE_AUDIO_CLIP", clipId, props: { durationInSeconds: newDuration, pitch: newPitch, offset: 0 } });
|
||||||
pitch: newPitch,
|
|
||||||
offset: 0,
|
|
||||||
});
|
|
||||||
sendActionSafe({
|
|
||||||
type: "UPDATE_AUDIO_CLIP",
|
|
||||||
clipId,
|
|
||||||
props: { durationInSeconds: newDuration, pitch: newPitch, offset: 0 },
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
updateAudioClipProperties(clipId, {
|
updateAudioClipProperties(clipId, { startTimeInSeconds: newStartTime, durationInSeconds: newDuration, pitch: newPitch, offset: 0 });
|
||||||
startTimeInSeconds: newStartTime,
|
sendActionSafe({ type: "UPDATE_AUDIO_CLIP", clipId, props: { startTimeInSeconds: newStartTime, durationInSeconds: newDuration, pitch: newPitch, offset: 0 } });
|
||||||
durationInSeconds: newDuration,
|
|
||||||
pitch: newPitch,
|
|
||||||
offset: 0,
|
|
||||||
});
|
|
||||||
sendActionSafe({
|
|
||||||
type: "UPDATE_AUDIO_CLIP",
|
|
||||||
clipId,
|
|
||||||
props: {
|
|
||||||
startTimeInSeconds: newStartTime,
|
|
||||||
durationInSeconds: newDuration,
|
|
||||||
pitch: newPitch,
|
|
||||||
offset: 0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
restartAudioEditorIfPlaying();
|
restartAudioEditorIfPlaying();
|
||||||
renderAudioEditor();
|
renderAudioEditor();
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("mousemove", onMouseMove);
|
document.addEventListener("mousemove", onMouseMove);
|
||||||
document.addEventListener("mouseup", onMouseUp);
|
document.addEventListener("mouseup", onMouseUp);
|
||||||
return;
|
return;
|
||||||
|
|
@ -1209,9 +946,8 @@ export function renderAudioEditor() {
|
||||||
if (clipElement && !clipElement.classList.contains("bassline-clip")) {
|
if (clipElement && !clipElement.classList.contains("bassline-clip")) {
|
||||||
const clipId = clipElement.dataset.clipId;
|
const clipId = clipElement.dataset.clipId;
|
||||||
|
|
||||||
const clipModel = appState.audio.clips.find(
|
// 🔑 pega o clip no estado pra ter o tempo inicial real
|
||||||
(c) => String(c.id) === String(clipId)
|
const clipModel = appState.audio.clips.find(c => String(c.id) === String(clipId));
|
||||||
);
|
|
||||||
const initialStartTime = Number(clipModel?.startTimeInSeconds || 0);
|
const initialStartTime = Number(clipModel?.startTimeInSeconds || 0);
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -1225,13 +961,8 @@ export function renderAudioEditor() {
|
||||||
const deltaX = moveEvent.clientX - initialMouseX;
|
const deltaX = moveEvent.clientX - initialMouseX;
|
||||||
clipElement.style.transform = `translateX(${deltaX}px)`;
|
clipElement.style.transform = `translateX(${deltaX}px)`;
|
||||||
|
|
||||||
const overElement = document.elementFromPoint(
|
const overElement = document.elementFromPoint(moveEvent.clientX, moveEvent.clientY);
|
||||||
moveEvent.clientX,
|
const overLane = overElement ? overElement.closest(".audio-track-lane") : null;
|
||||||
moveEvent.clientY
|
|
||||||
);
|
|
||||||
const overLane = overElement
|
|
||||||
? overElement.closest(".audio-track-lane")
|
|
||||||
: null;
|
|
||||||
if (overLane && overLane !== lastOverLane) {
|
if (overLane && overLane !== lastOverLane) {
|
||||||
if (lastOverLane) lastOverLane.classList.remove("drag-over");
|
if (lastOverLane) lastOverLane.classList.remove("drag-over");
|
||||||
overLane.classList.add("drag-over");
|
overLane.classList.add("drag-over");
|
||||||
|
|
@ -1251,24 +982,15 @@ export function renderAudioEditor() {
|
||||||
|
|
||||||
const newTrackId = finalLane.dataset.trackId;
|
const newTrackId = finalLane.dataset.trackId;
|
||||||
|
|
||||||
const deltaX =
|
// ✅ delta do mouse + delta de scroll durante o drag (se houver)
|
||||||
(upEvent.clientX - initialMouseX) +
|
const deltaX = (upEvent.clientX - initialMouseX) + (scrollEl.scrollLeft - initialScrollLeft);
|
||||||
(scrollEl.scrollLeft - initialScrollLeft);
|
|
||||||
|
|
||||||
let newStartTime =
|
let newStartTime = initialStartTime + (deltaX / currentPixelsPerSecond);
|
||||||
initialStartTime + deltaX / currentPixelsPerSecond;
|
|
||||||
newStartTime = Math.max(0, newStartTime);
|
newStartTime = Math.max(0, newStartTime);
|
||||||
newStartTime = quantizeTime(newStartTime);
|
newStartTime = quantizeTime(newStartTime);
|
||||||
|
|
||||||
updateAudioClipProperties(clipId, {
|
updateAudioClipProperties(clipId, { trackId: newTrackId, startTimeInSeconds: newStartTime });
|
||||||
trackId: newTrackId,
|
sendActionSafe({ type: "UPDATE_AUDIO_CLIP", clipId, props: { trackId: newTrackId, startTimeInSeconds: newStartTime } });
|
||||||
startTimeInSeconds: newStartTime,
|
|
||||||
});
|
|
||||||
sendActionSafe({
|
|
||||||
type: "UPDATE_AUDIO_CLIP",
|
|
||||||
clipId,
|
|
||||||
props: { trackId: newTrackId, startTimeInSeconds: newStartTime },
|
|
||||||
});
|
|
||||||
renderAudioEditor();
|
renderAudioEditor();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1277,6 +999,7 @@ export function renderAudioEditor() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Seek na Pista
|
// Seek na Pista
|
||||||
const timelineContainer = e.target.closest(".timeline-container");
|
const timelineContainer = e.target.closest(".timeline-container");
|
||||||
if (timelineContainer) {
|
if (timelineContainer) {
|
||||||
|
|
@ -1374,21 +1097,7 @@ export function renderAudioEditor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ Restaura o scroll anterior após reconstruir o container
|
|
||||||
// (evita “voltar pro início” depois de mover/redimensionar/deletar)
|
|
||||||
try {
|
|
||||||
newTrackContainer.scrollLeft = prevScrollLeft;
|
|
||||||
newTrackContainer.scrollTop = prevScrollTop;
|
|
||||||
|
|
||||||
// mantém régua alinhada (caso ela suporte scrollLeft)
|
|
||||||
const mainRuler = tracksParent.querySelector(".timeline-ruler");
|
|
||||||
if (mainRuler) mainRuler.scrollLeft = prevScrollLeft;
|
|
||||||
} catch (err) {
|
|
||||||
// silencioso: não pode quebrar a DAW
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function updateAudioEditorUI() {
|
export function updateAudioEditorUI() {
|
||||||
const playBtn = document.getElementById("audio-editor-play-btn");
|
const playBtn = document.getElementById("audio-editor-play-btn");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue