GENERADOR DE MELODIAS GIRALISMO
- samuel gaitan
- 30 ene
- 3 Min. de lectura
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
background: #f0f0f0;
margin: 0;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.controls {
margin-bottom: 20px;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.button {
background: #4CAF50;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s;
}
.button:hover {
background: #45a049;
}
.button.stop {
background: #f44336;
}
.button.stop:hover {
background: #da190b;
}
.piano {
display: flex;
justify-content: center;
position: relative;
height: 200px;
margin: 20px 0;
}
.key {
position: relative;
width: 40px;
height: 180px;
border: 1px solid #000;
background: white;
border-radius: 0 0 5px 5px;
cursor: pointer;
transition: background 0.1s;
}
.key.black {
position: absolute;
width: 24px;
height: 100px;
background: #000;
z-index: 1;
}
.key.active {
background: #e3f2fd;
}
.key.black.active {
background: #444;
}
.settings {
margin: 20px 0;
padding: 15px;
background: #f5f5f5;
border-radius: 5px;
}
.settings label {
display: block;
margin-bottom: 10px;
}
.settings input[type="range"] {
width: 200px;
}
.visualization {
height: 100px;
background: #f5f5f5;
margin: 20px 0;
border-radius: 5px;
overflow: hidden;
position: relative;
}
.note-particle {
position: absolute;
width: 10px;
height: 10px;
background: #4CAF50;
border-radius: 50%;
opacity: 0.8;
transition: transform 1s, opacity 1s;
}
</style>
</head>
<body>
<div class="container">
<h1>Generador de Melodías de Piano</h1>
<div class="controls">
<button class="button" id="playButton">Generar Melodía</button>
<button class="button stop" id="stopButton">Detener</button>
<button class="button" id="tempoUpButton">Tempo +</button>
<button class="button" id="tempoDownButton">Tempo -</button>
</div>
<div class="settings">
<label>
Velocidad:
<input type="range" id="tempoSlider" min="60" max="180" value="100">
<span id="tempoValue">100 BPM</span>
</label>
<label>
Notas por secuencia:
<input type="range" id="notesSlider" min="2" max="7" value="3">
<span id="notesValue">3 notas</span>
</label>
</div>
<div class="visualization" id="visualization"></div>
<div class="piano" id="piano"></div>
</div>
<script>
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
let isPlaying = false;
let currentTempo = 100;
let noteCount = 3;
let melodyInterval;
const notes = [
{ note: 'C4', freq: 261.63 },
{ note: 'D4', freq: 293.66 },
{ note: 'E4', freq: 329.63 },
{ note: 'F4', freq: 349.23 },
{ note: 'G4', freq: 392.00 },
{ note: 'A4', freq: 440.00 },
{ note: 'B4', freq: 493.88 },
{ note: 'C5', freq: 523.25 }
];
// Crear piano visual
const piano = document.getElementById('piano');
notes.forEach((note, index) => {
const key = document.createElement('div');
key.className = 'key';
key.dataset.note = note.note;
key.dataset.freq = note.freq;
piano.appendChild(key);
});
function playNote(frequency, duration = 0.5) {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.5, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration);
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + duration);
createNoteVisualization(frequency);
}
function createNoteVisualization(frequency) {
const viz = document.getElementById('visualization');
const particle = document.createElement('div');
particle.className = 'note-particle';
const x = Math.random() * (viz.offsetWidth - 10);
const y = viz.offsetHeight - 10;
particle.style.left = `${x}px`;
particle.style.bottom = '0';
viz.appendChild(particle);
requestAnimationFrame(() => {
particle.style.transform = `translateY(-${y}px)`;
particle.style.opacity = '0';
});
setTimeout(() => particle.remove(), 1000);
}
function generateMelody() {
const selectedNotes = [];
for (let i = 0; i < noteCount; i++) {
const randomNote = notes[Math.floor(Math.random() * notes.length)];
selectedNotes.push(randomNote);
}
return selectedNotes;
}
function highlightKey(note) {
const keys = document.querySelectorAll('.key');
keys.forEach(key => key.classList.remove('active'));
const activeKey = document.querySelector(`[data-note="${note}"]`);
if (activeKey) activeKey.classList.add('active');
}
function playMelody() {
const melody = generateMelody();
let noteIndex = 0;
melodyInterval = setInterval(() => {
if (noteIndex >= melody.length) {
noteIndex = 0;
}
const note = melody[noteIndex];
playNote(note.freq, 0.8);
highlightKey(note.note);
noteIndex++;
}, (60 / currentTempo) * 1000);
}
// Event Listeners
document.getElementById('playButton').addEventListener('click', () => {
if (!isPlaying) {
isPlaying = true;
playMelody();
}
});
document.getElementById('stopButton').addEventListener('click', () => {
isPlaying = false;
clearInterval(melodyInterval);
document.querySelectorAll('.key').forEach(key => key.classList.remove('active'));
});
document.getElementById('tempoSlider').addEventListener('input', (e) => {
currentTempo = parseInt(e.target.value);
document.getElementById('tempoValue').textContent = `${currentTempo} BPM`;
if (isPlaying) {
clearInterval(melodyInterval);
playMelody();
}
});
document.getElementById('notesSlider').addEventListener('input', (e) => {
noteCount = parseInt(e.target.value);
document.getElementById('notesValue').textContent = `${noteCount} notas`;
});
document.getElementById('tempoUpButton').addEventListener('click', () => {
const slider = document.getElementById('tempoSlider');
slider.value = Math.min(parseInt(slider.value) + 10, slider.max);
slider.dispatchEvent(new Event('input'));
});
document.getElementById('tempoDownButton').addEventListener('click', () => {
const slider = document.getElementById('tempoSlider');
slider.value = Math.max(parseInt(slider.value) - 10, slider.min);
slider.dispatchEvent(new Event('input'));
});
</script>
</body>
</html>




Comentarios