136 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
// js/audio_state.js
 | 
						|
import { DEFAULT_VOLUME, DEFAULT_PAN } from "../config.js";
 | 
						|
import { renderAudioEditor } from "./audio_ui.js";
 | 
						|
import { getMainGainNode } from "../audio.js";
 | 
						|
 | 
						|
const initialState = {
 | 
						|
    tracks: [],
 | 
						|
    clips: [],
 | 
						|
    audioEditorStartTime: 0,
 | 
						|
    audioEditorAnimationId: null,
 | 
						|
    audioEditorPlaybackTime: 0,
 | 
						|
    isAudioEditorLoopEnabled: false,
 | 
						|
};
 | 
						|
 | 
						|
export let audioState = { ...initialState };
 | 
						|
 | 
						|
export function initializeAudioState() {
 | 
						|
    audioState.clips.forEach(clip => {
 | 
						|
        if (clip.player) clip.player.dispose();
 | 
						|
        if (clip.pannerNode) clip.pannerNode.dispose();
 | 
						|
        if (clip.gainNode) clip.gainNode.dispose();
 | 
						|
    });
 | 
						|
    Object.assign(audioState, initialState, { tracks: [], clips: [] });
 | 
						|
}
 | 
						|
 | 
						|
export async function loadAudioForClip(clip) {
 | 
						|
  if (!clip.sourcePath) return clip;
 | 
						|
  try {
 | 
						|
    // Cria o player e o conecta à cadeia de áudio do clipe
 | 
						|
    clip.player = new Tone.Player();
 | 
						|
    clip.player.chain(clip.gainNode, clip.pannerNode, getMainGainNode());
 | 
						|
 | 
						|
    // Carrega o áudio e espera a conclusão
 | 
						|
    await clip.player.load(clip.sourcePath);
 | 
						|
    
 | 
						|
    if (clip.duration === 0) {
 | 
						|
      clip.duration = clip.player.buffer.duration;
 | 
						|
    }
 | 
						|
  } catch (error) {
 | 
						|
    console.error(`Falha ao carregar áudio para o clipe ${clip.name}:`, error);
 | 
						|
    clip.player = null;
 | 
						|
  }
 | 
						|
  return clip;
 | 
						|
}
 | 
						|
 | 
						|
export function addAudioClipToTimeline(samplePath, trackId = 1, startTime = 0) {
 | 
						|
    const newClip = {
 | 
						|
        id: Date.now() + Math.random(),
 | 
						|
        trackId: trackId,
 | 
						|
        sourcePath: samplePath,
 | 
						|
        name: samplePath.split('/').pop(),
 | 
						|
        player: null,
 | 
						|
        startTime: startTime,
 | 
						|
        offset: 0,
 | 
						|
        duration: 0,
 | 
						|
        pitch: 0,
 | 
						|
        volume: DEFAULT_VOLUME,
 | 
						|
        pan: DEFAULT_PAN,
 | 
						|
        isSoloed: true,
 | 
						|
        // --- ADICIONADO: Nós de áudio para cada clipe ---
 | 
						|
        gainNode: new Tone.Gain(Tone.gainToDb(DEFAULT_VOLUME)),
 | 
						|
        pannerNode: new Tone.Panner(DEFAULT_PAN),
 | 
						|
    };
 | 
						|
 | 
						|
    audioState.clips.push(newClip);
 | 
						|
    
 | 
						|
    loadAudioForClip(newClip).then(() => {
 | 
						|
        renderAudioEditor();
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
export function updateAudioClipProperties(clipId, properties) {
 | 
						|
    const clip = audioState.clips.find(c => c.id == clipId);
 | 
						|
    if (clip) {
 | 
						|
        Object.assign(clip, properties);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export function sliceAudioClip(clipId, sliceTimeInTimeline) {
 | 
						|
    const originalClip = audioState.clips.find(c => c.id == clipId);
 | 
						|
    if (!originalClip || sliceTimeInTimeline <= originalClip.startTime || sliceTimeInTimeline >= originalClip.startTime + originalClip.duration) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    const cutPointInClip = sliceTimeInTimeline - originalClip.startTime;
 | 
						|
 | 
						|
    const newClip = {
 | 
						|
        id: Date.now() + Math.random(),
 | 
						|
        trackId: originalClip.trackId,
 | 
						|
        sourcePath: originalClip.sourcePath,
 | 
						|
        name: originalClip.name,
 | 
						|
        player: originalClip.player,
 | 
						|
        startTime: sliceTimeInTimeline,
 | 
						|
        offset: originalClip.offset + cutPointInClip,
 | 
						|
        duration: originalClip.duration - cutPointInClip,
 | 
						|
        pitch: originalClip.pitch,
 | 
						|
        volume: originalClip.volume,
 | 
						|
        pan: originalClip.pan,
 | 
						|
        isSoloed: false,
 | 
						|
        gainNode: new Tone.Gain(Tone.gainToDb(originalClip.volume)),
 | 
						|
        pannerNode: new Tone.Panner(originalClip.pan),
 | 
						|
    };
 | 
						|
    newClip.player.chain(newClip.gainNode, newClip.pannerNode, getMainGainNode());
 | 
						|
 | 
						|
 | 
						|
    originalClip.duration = cutPointInClip;
 | 
						|
    audioState.clips.push(newClip);
 | 
						|
}
 | 
						|
 | 
						|
export function updateClipVolume(clipId, volume) {
 | 
						|
  const clip = audioState.clips.find((c) => c.id == clipId);
 | 
						|
  if (clip) {
 | 
						|
    const clampedVolume = Math.max(0, Math.min(1.5, volume));
 | 
						|
    clip.volume = clampedVolume;
 | 
						|
    if (clip.gainNode) {
 | 
						|
        clip.gainNode.gain.value = Tone.gainToDb(clampedVolume);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
export function updateClipPan(clipId, pan) {
 | 
						|
  const clip = audioState.clips.find((c) => c.id == clipId);
 | 
						|
  if (clip) {
 | 
						|
    const clampedPan = Math.max(-1, Math.min(1, pan));
 | 
						|
    clip.pan = clampedPan;
 | 
						|
    if (clip.pannerNode) {
 | 
						|
        clip.pannerNode.pan.value = clampedPan;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
export function addAudioTrackLane() {
 | 
						|
    const newTrackName = `Pista de Áudio ${audioState.tracks.length + 1}`;
 | 
						|
    audioState.tracks.push({ id: Date.now(), name: newTrackName });
 | 
						|
    // A UI será re-renderizada a partir do main.js
 | 
						|
} |