Amostras de áudio funcionais até o momento. Agulha de tempo, mute/solo e ajustado com a velocidade de reprodução
This commit is contained in:
1 parent
8c391412e1
commit
602df1db6d
10 files changed
+942
-689
No files matched your search
@@ -5,18 +5,25 @@ import {
|
||||
getAudioContext,
|
||||
getMainGainNode,
|
||||
} from "./audio.js";
|
||||
import { renderApp } from "./ui.js";
|
||||
import { renderApp, renderAudioEditor } from "./ui.js";
|
||||
import { getTotalSteps } from "./utils.js";
|
||||
|
||||
export let appState = {
|
||||
tracks: [],
|
||||
audioTracks: [],
|
||||
activeTrackId: null,
|
||||
activePatternIndex: 0, // <-- VOLTOU A SER GLOBAL
|
||||
activePatternIndex: 0,
|
||||
isPlaying: false,
|
||||
isAudioEditorPlaying: false,
|
||||
activeAudioSources: [],
|
||||
audioEditorStartTime: 0,
|
||||
audioEditorAnimationId: null,
|
||||
audioEditorPlaybackTime: 0,
|
||||
playbackIntervalId: null,
|
||||
currentStep: 0,
|
||||
metronomeEnabled: false,
|
||||
originalXmlDoc: null,
|
||||
currentBeatBasslineName: 'Novo Projeto',
|
||||
masterVolume: DEFAULT_VOLUME,
|
||||
masterPan: DEFAULT_PAN,
|
||||
};
|
||||
@@ -37,6 +44,54 @@ export async function loadAudioForTrack(track) {
|
||||
return track;
|
||||
}
|
||||
|
||||
export function addAudioTrack(samplePath) {
|
||||
initializeAudioContext();
|
||||
const audioContext = getAudioContext();
|
||||
const mainGainNode = getMainGainNode();
|
||||
|
||||
const newAudioTrack = {
|
||||
id: Date.now() + Math.random(),
|
||||
name: samplePath.split('/').pop(),
|
||||
samplePath: samplePath,
|
||||
audioBuffer: null,
|
||||
volume: DEFAULT_VOLUME,
|
||||
pan: DEFAULT_PAN,
|
||||
isMuted: false,
|
||||
isSoloed: false, // <-- ADICIONADO: Começa como não-solada
|
||||
gainNode: audioContext.createGain(),
|
||||
pannerNode: audioContext.createStereoPanner(),
|
||||
};
|
||||
|
||||
newAudioTrack.gainNode.connect(newAudioTrack.pannerNode);
|
||||
newAudioTrack.pannerNode.connect(mainGainNode);
|
||||
newAudioTrack.gainNode.gain.value = newAudioTrack.volume;
|
||||
newAudioTrack.pannerNode.pan.value = newAudioTrack.pan;
|
||||
|
||||
appState.audioTracks.push(newAudioTrack);
|
||||
|
||||
loadAudioForTrack(newAudioTrack).then(() => {
|
||||
renderAudioEditor();
|
||||
});
|
||||
}
|
||||
|
||||
// A função de mute agora será a de solo.
|
||||
export function toggleAudioTrackSolo(trackId) {
|
||||
const track = appState.audioTracks.find(t => t.id == trackId);
|
||||
if (track) {
|
||||
track.isSoloed = !track.isSoloed;
|
||||
renderAudioEditor(); // Re-renderiza para mostrar a nova cor
|
||||
}
|
||||
}
|
||||
|
||||
// Mantemos a função de mute caso precise no futuro, mas ela não está conectada ao botão.
|
||||
export function toggleAudioTrackMute(trackId) {
|
||||
const track = appState.audioTracks.find(t => t.id == trackId);
|
||||
if (track) {
|
||||
track.isMuted = !track.isMuted;
|
||||
renderAudioEditor();
|
||||
}
|
||||
}
|
||||
|
||||
export function addTrackToState() {
|
||||
initializeAudioContext();
|
||||
const audioContext = getAudioContext();
|
||||
@@ -52,7 +107,7 @@ export function addTrackToState() {
|
||||
patterns: referenceTrack
|
||||
? referenceTrack.patterns.map(p => ({ name: p.name, steps: new Array(p.steps.length).fill(false), pos: p.pos }))
|
||||
: [{ name: "Pattern 1", steps: new Array(totalSteps).fill(false), pos: 0 }],
|
||||
// activePatternIndex foi removido daqui
|
||||
activePatternIndex: 0,
|
||||
volume: DEFAULT_VOLUME,
|
||||
pan: DEFAULT_PAN,
|
||||
gainNode: audioContext.createGain(),
|
||||
@@ -64,18 +119,12 @@ export function addTrackToState() {
|
||||
newTrack.pannerNode.pan.value = newTrack.pan;
|
||||
|
||||
appState.tracks.push(newTrack);
|
||||
if (!appState.activeTrackId) {
|
||||
appState.activeTrackId = newTrack.id;
|
||||
}
|
||||
renderApp();
|
||||
}
|
||||
|
||||
export function removeLastTrackFromState() {
|
||||
if (appState.tracks.length > 0) {
|
||||
const removedTrack = appState.tracks.pop();
|
||||
if (appState.activeTrackId === removedTrack.id) {
|
||||
appState.activeTrackId = appState.tracks[0]?.id || null;
|
||||
}
|
||||
appState.tracks.pop();
|
||||
renderApp();
|
||||
}
|
||||
}
|
||||
@@ -87,18 +136,14 @@ export async function updateTrackSample(trackId, samplePath) {
|
||||
track.name = samplePath.split("/").pop();
|
||||
track.audioBuffer = null;
|
||||
await loadAudioForTrack(track);
|
||||
const trackLane = document.querySelector(`.track-lane[data-track-id="${trackId}"] .track-name`);
|
||||
if (trackLane) {
|
||||
trackLane.textContent = track.name;
|
||||
}
|
||||
renderApp();
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleStepState(trackId, stepIndex) {
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
if (track && track.patterns && track.patterns.length > 0) {
|
||||
// Usa o índice GLOBAL para saber qual pattern modificar
|
||||
const activePattern = track.patterns[appState.activePatternIndex];
|
||||
const activePattern = track.patterns[track.activePatternIndex];
|
||||
if (activePattern && activePattern.steps.length > stepIndex) {
|
||||
activePattern.steps[stepIndex] = !activePattern.steps[stepIndex];
|
||||
}
|
||||
@@ -106,7 +151,7 @@ export function toggleStepState(trackId, stepIndex) {
|
||||
}
|
||||
|
||||
export function updateTrackVolume(trackId, volume) {
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
const track = appState.tracks.find((t) => t.id == trackId) || appState.audioTracks.find((t) => t.id == trackId);
|
||||
if (track) {
|
||||
const clampedVolume = Math.max(0, Math.min(1.5, volume));
|
||||
track.volume = clampedVolume;
|
||||
@@ -117,7 +162,7 @@ export function updateTrackVolume(trackId, volume) {
|
||||
}
|
||||
|
||||
export function updateTrackPan(trackId, pan) {
|
||||
const track = appState.tracks.find((t) => t.id == trackId);
|
||||
const track = appState.tracks.find((t) => t.id == trackId) || appState.audioTracks.find((t) => t.id == trackId);
|
||||
if (track) {
|
||||
const clampedPan = Math.max(-1, Math.min(1, pan));
|
||||
track.pan = clampedPan;
|
||||
|
||||
Reference in new issue
Block a user