ativar/desativar tracks de áudio, knobs funcionais

This commit is contained in:
JotaChina committed 2025-12-27 22:10:08 -03:00
1 parent d3e209d8e8
commit 73a5257aec
5 files changed
+260 -28

No files matched your search

+81 -22
View File
@@ -16,6 +16,82 @@ const DEFAULT_KICKER_XML = `<kicker>
<env amt="0" attack="0.01" hold="0.1" decay="0.1" release="0.1" sustain="0.5" sync_mode="0"/>
</kicker>`;
// ------------------------------------------------------------
// MIX / MUTE HELPERS (Volume, Pan)
// ------------------------------------------------------------
const MAX_TRACK_VOLUME = 1.5;
function _clamp(n, min, max) {
const x = Number(n);
if (!Number.isFinite(x)) return min;
return Math.min(max, Math.max(min, x));
}
function _ensureMuteFields(track) {
if (!track) return;
if (track.isMuted == null) track.isMuted = !!track.muted;
if (track.muted == null) track.muted = !!track.isMuted;
}
function applyTrackMix(track) {
if (!track) return;
_ensureMuteFields(track);
const vol = _clamp(track.volume ?? DEFAULT_VOLUME, 0, MAX_TRACK_VOLUME);
const pan = _clamp(track.pan ?? DEFAULT_PAN, -1, 1);
const isMuted = !!(track.isMuted || track.muted);
// Volume (dB): mute => -Infinity
if (!track.volumeNode) {
track.volumeNode = new Tone.Volume(
isMuted || vol === 0 ? -Infinity : Tone.gainToDb(vol)
);
} else {
track.volumeNode.volume.value =
isMuted || vol === 0 ? -Infinity : Tone.gainToDb(vol);
}
// Pan
if (!track.pannerNode) {
track.pannerNode = new Tone.Panner(pan);
} else {
track.pannerNode.pan.value = pan;
}
// reconecta cadeia base (idempotente)
try { track.volumeNode.disconnect(); } catch {}
try { track.pannerNode.disconnect(); } catch {}
track.volumeNode.connect(track.pannerNode);
track.pannerNode.connect(getMainGainNode());
}
// API pública (usada pelo UI/socket)
export function setPatternTrackMute(trackId, isMuted) {
const t = (appState.pattern.tracks || []).find((x) => String(x.id) === String(trackId));
if (!t) return false;
t.isMuted = !!isMuted;
t.muted = !!isMuted;
applyTrackMix(t);
return true;
}
export function setPatternTrackVolume(trackId, volume) {
const t = (appState.pattern.tracks || []).find((x) => String(x.id) === String(trackId));
if (!t) return false;
t.volume = _clamp(volume, 0, MAX_TRACK_VOLUME);
applyTrackMix(t);
return true;
}
export function setPatternTrackPan(trackId, pan) {
const t = (appState.pattern.tracks || []).find((x) => String(x.id) === String(trackId));
if (!t) return false;
t.pan = _clamp(pan, -1, 1);
applyTrackMix(t);
return true;
}
export function initializePatternState() {
appState.pattern.tracks.forEach(track => {
try { track.player?.dispose(); } catch {}
@@ -30,33 +106,14 @@ export function initializePatternState() {
}
export async function loadAudioForTrack(track) {
// 1) Garante Volume/Pan
// 1) Garante Volume/Pan/Mute e reconecta a cadeia base
try {
if (!track.volumeNode) {
track.volumeNode = new Tone.Volume(
track.volume === 0 ? -Infinity : Tone.gainToDb(track.volume)
);
} else {
track.volumeNode.volume.value =
track.volume === 0 ? -Infinity : Tone.gainToDb(track.volume);
}
applyTrackMix(track);
if (!track.pannerNode) {
track.pannerNode = new Tone.Panner(track.pan ?? 0);
} else {
track.pannerNode.pan.value = track.pan ?? 0;
}
// Desconecta o que existir
// desconecta o que existir (para reconectar sem duplicar rotas)
try { track.instrument?.disconnect(); } catch {}
try { track.player?.disconnect(); } catch {}
try { track.previewPlayer?.disconnect(); } catch {}
// Reconecta cadeia base
try { track.volumeNode.disconnect(); } catch {}
try { track.pannerNode.disconnect(); } catch {}
track.volumeNode.connect(track.pannerNode);
track.pannerNode.connect(getMainGainNode());
} catch (e) {
console.error("Erro ao criar nós de áudio base:", e);
}
@@ -257,6 +314,8 @@ export function addTrackToState() {
patterns,
activePatternIndex: appState.pattern.activePatternIndex ?? 0,
isMuted: false,
muted: false,
volume: DEFAULT_VOLUME,
pan: DEFAULT_PAN,
volumeNode: new Tone.Volume(Tone.gainToDb(DEFAULT_VOLUME)),