171 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
// js/pattern_ui.js
 | 
						|
import { appState } from "../state.js";
 | 
						|
import { 
 | 
						|
    toggleStepState, 
 | 
						|
    updateTrackSample 
 | 
						|
} from "./pattern_state.js";
 | 
						|
import { playSample, stopPlayback } from "./pattern_audio.js"; // Será criado no próximo passo
 | 
						|
import { getTotalSteps } from "../utils.js";
 | 
						|
 | 
						|
// Função principal de renderização para o editor de patterns
 | 
						|
export function renderPatternEditor() {
 | 
						|
  const trackContainer = document.getElementById("track-container");
 | 
						|
  trackContainer.innerHTML = "";
 | 
						|
 | 
						|
  appState.pattern.tracks.forEach((trackData) => {
 | 
						|
    const trackLane = document.createElement("div");
 | 
						|
    trackLane.className = "track-lane";
 | 
						|
    trackLane.dataset.trackId = trackData.id;
 | 
						|
 | 
						|
    if (trackData.id === appState.pattern.activeTrackId) {
 | 
						|
        trackLane.classList.add('active-track');
 | 
						|
    }
 | 
						|
 | 
						|
    trackLane.innerHTML = `
 | 
						|
      <div class="track-info">
 | 
						|
        <i class="fa-solid fa-gear"></i>
 | 
						|
        <div class="track-mute"></div>
 | 
						|
        <span class="track-name">${trackData.name}</span>
 | 
						|
      </div>
 | 
						|
      <div class="track-controls">
 | 
						|
        <div class="knob-container">
 | 
						|
          <div class="knob" data-control="volume" data-track-id="${trackData.id}"><div class="knob-indicator"></div></div>
 | 
						|
          <span>VOL</span>
 | 
						|
        </div>
 | 
						|
        <div class="knob-container">
 | 
						|
          <div class="knob" data-control="pan" data-track-id="${trackData.id}"><div class="knob-indicator"></div></div>
 | 
						|
          <span>PAN</span>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
      <div class="step-sequencer-wrapper"></div>
 | 
						|
    `;
 | 
						|
 | 
						|
    trackLane.addEventListener('click', () => {
 | 
						|
        if (appState.pattern.activeTrackId === trackData.id) return;
 | 
						|
        stopPlayback();
 | 
						|
        appState.pattern.activeTrackId = trackData.id;
 | 
						|
        document.querySelectorAll('.track-lane').forEach(lane => lane.classList.remove('active-track'));
 | 
						|
        trackLane.classList.add('active-track');
 | 
						|
        updateGlobalPatternSelector();
 | 
						|
        redrawSequencer();
 | 
						|
    });
 | 
						|
 | 
						|
    trackLane.addEventListener("dragover", (e) => { e.preventDefault(); trackLane.classList.add("drag-over"); });
 | 
						|
    trackLane.addEventListener("dragleave", () => trackLane.classList.remove("drag-over"));
 | 
						|
    trackLane.addEventListener("drop", (e) => {
 | 
						|
      e.preventDefault();
 | 
						|
      trackLane.classList.remove("drag-over");
 | 
						|
      const filePath = e.dataTransfer.getData("text/plain");
 | 
						|
      if (filePath) {
 | 
						|
        updateTrackSample(trackData.id, filePath);
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    trackContainer.appendChild(trackLane);
 | 
						|
    // A lógica dos knobs precisará ser reimplementada ou movida para um arquivo de componentes
 | 
						|
  });
 | 
						|
  
 | 
						|
  updateGlobalPatternSelector();
 | 
						|
  redrawSequencer();
 | 
						|
}
 | 
						|
 | 
						|
export function redrawSequencer() {
 | 
						|
  const totalGridSteps = getTotalSteps();
 | 
						|
  document.querySelectorAll(".step-sequencer-wrapper").forEach((wrapper) => {
 | 
						|
    let sequencerContainer = wrapper.querySelector(".step-sequencer");
 | 
						|
    if (!sequencerContainer) {
 | 
						|
      sequencerContainer = document.createElement("div");
 | 
						|
      sequencerContainer.className = "step-sequencer";
 | 
						|
      wrapper.appendChild(sequencerContainer);
 | 
						|
    }
 | 
						|
    
 | 
						|
    const parentTrackElement = wrapper.closest(".track-lane");
 | 
						|
    const trackId = parentTrackElement.dataset.trackId;
 | 
						|
    const trackData = appState.pattern.tracks.find((t) => t.id == trackId);
 | 
						|
 | 
						|
    if (!trackData || !trackData.patterns || trackData.patterns.length === 0) {
 | 
						|
      sequencerContainer.innerHTML = ""; return;
 | 
						|
    }
 | 
						|
 | 
						|
    const activePattern = trackData.patterns[appState.pattern.activePatternIndex];
 | 
						|
    if (!activePattern) {
 | 
						|
        sequencerContainer.innerHTML = ""; return;
 | 
						|
    }
 | 
						|
    const patternSteps = activePattern.steps;
 | 
						|
 | 
						|
    sequencerContainer.innerHTML = "";
 | 
						|
    for (let i = 0; i < totalGridSteps; i++) {
 | 
						|
      const stepWrapper = document.createElement("div");
 | 
						|
      stepWrapper.className = "step-wrapper";
 | 
						|
      const stepElement = document.createElement("div");
 | 
						|
      stepElement.className = "step";
 | 
						|
      
 | 
						|
      if (patternSteps[i] === true) {
 | 
						|
        stepElement.classList.add("active");
 | 
						|
      }
 | 
						|
 | 
						|
      stepElement.addEventListener("click", () => {
 | 
						|
        toggleStepState(trackData.id, i); 
 | 
						|
        stepElement.classList.toggle("active");
 | 
						|
        if (trackData && trackData.samplePath) {
 | 
						|
          playSample(trackData.samplePath, trackData.id);
 | 
						|
        }
 | 
						|
      });
 | 
						|
 | 
						|
      const beatsPerBar = parseInt(document.getElementById("compasso-a-input").value, 10) || 4;
 | 
						|
      const groupIndex = Math.floor(i / beatsPerBar);
 | 
						|
      if (groupIndex % 2 === 0) {
 | 
						|
        stepElement.classList.add("step-dark");
 | 
						|
      }
 | 
						|
 | 
						|
      const stepsPerBar = 16;
 | 
						|
      if (i > 0 && i % stepsPerBar === 0) {
 | 
						|
        const marker = document.createElement("div");
 | 
						|
        marker.className = "step-marker";
 | 
						|
        marker.textContent = Math.floor(i / stepsPerBar) + 1;
 | 
						|
        stepWrapper.appendChild(marker);
 | 
						|
      }
 | 
						|
      
 | 
						|
      stepWrapper.appendChild(stepElement);
 | 
						|
      sequencerContainer.appendChild(stepWrapper);
 | 
						|
    }
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
export function updateGlobalPatternSelector() {
 | 
						|
    const globalPatternSelector = document.getElementById('global-pattern-selector');
 | 
						|
    if (!globalPatternSelector) return;
 | 
						|
 | 
						|
    const referenceTrack = appState.pattern.tracks[0];
 | 
						|
    globalPatternSelector.innerHTML = '';
 | 
						|
    if (referenceTrack && referenceTrack.patterns.length > 0) {
 | 
						|
        referenceTrack.patterns.forEach((pattern, index) => {
 | 
						|
            const option = document.createElement('option');
 | 
						|
            option.value = index;
 | 
						|
            option.textContent = pattern.name;
 | 
						|
            globalPatternSelector.appendChild(option);
 | 
						|
        });
 | 
						|
        globalPatternSelector.selectedIndex = appState.pattern.activePatternIndex;
 | 
						|
        globalPatternSelector.disabled = false;
 | 
						|
    } else {
 | 
						|
        const option = document.createElement('option');
 | 
						|
        option.textContent = 'Sem patterns';
 | 
						|
        globalPatternSelector.appendChild(option);
 | 
						|
        globalPatternSelector.disabled = true;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export function highlightStep(stepIndex, isActive) {
 | 
						|
  if (stepIndex < 0) return;
 | 
						|
  document.querySelectorAll(".track-lane").forEach((track) => {
 | 
						|
    const stepWrapper = track.querySelector(
 | 
						|
      `.step-sequencer .step-wrapper:nth-child(${stepIndex + 1})`
 | 
						|
    );
 | 
						|
    if (stepWrapper) {
 | 
						|
      const stepElement = stepWrapper.querySelector(".step");
 | 
						|
      if (stepElement) {
 | 
						|
        stepElement.classList.toggle("playing", isActive);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  });
 | 
						|
} |