|
<!DOCTYPE html> |
|
<html lang="ru"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Изменение времени по голосу</title> |
|
<style> |
|
body { |
|
margin: 0; |
|
overflow: hidden; |
|
font-family: Arial, sans-serif; |
|
} |
|
#game-container { |
|
position: relative; |
|
width: 100vw; |
|
height: 100vh; |
|
transition: background-color 0.5s ease; |
|
} |
|
#sky { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 70%; |
|
transition: background-color 0.5s ease; |
|
} |
|
#ground { |
|
position: absolute; |
|
bottom: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 30%; |
|
background-color: #4a7c59; |
|
transition: background-color 0.5s ease; |
|
} |
|
#sun-moon { |
|
position: absolute; |
|
width: 80px; |
|
height: 80px; |
|
border-radius: 50%; |
|
transition: all 0.5s ease; |
|
} |
|
#buildings { |
|
position: absolute; |
|
bottom: 30%; |
|
left: 0; |
|
width: 100%; |
|
height: 20%; |
|
} |
|
.building { |
|
position: absolute; |
|
bottom: 0; |
|
background-color: #555; |
|
transition: background-color 0.5s ease; |
|
} |
|
.window { |
|
position: absolute; |
|
background-color: #333; |
|
transition: background-color 0.5s ease; |
|
} |
|
#controls { |
|
position: absolute; |
|
top: 20px; |
|
left: 20px; |
|
background-color: rgba(255, 255, 255, 0.8); |
|
padding: 15px; |
|
border-radius: 10px; |
|
z-index: 100; |
|
} |
|
#time-display { |
|
font-size: 24px; |
|
font-weight: bold; |
|
margin-bottom: 10px; |
|
} |
|
#pitch-display, #volume-display { |
|
font-size: 16px; |
|
margin-bottom: 5px; |
|
} |
|
button { |
|
padding: 10px 15px; |
|
margin-right: 5px; |
|
background-color: #4CAF50; |
|
color: white; |
|
border: none; |
|
border-radius: 5px; |
|
cursor: pointer; |
|
} |
|
button:hover { |
|
background-color: #45a049; |
|
} |
|
#stars { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 70%; |
|
transition: opacity 0.5s ease; |
|
opacity: 0; |
|
} |
|
.star { |
|
position: absolute; |
|
background-color: white; |
|
border-radius: 50%; |
|
} |
|
#instructions { |
|
position: absolute; |
|
bottom: 20px; |
|
left: 20px; |
|
background-color: rgba(255, 255, 255, 0.8); |
|
padding: 15px; |
|
border-radius: 10px; |
|
max-width: 300px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="game-container"> |
|
<div id="sky"></div> |
|
<div id="stars"></div> |
|
<div id="sun-moon"></div> |
|
<div id="buildings"></div> |
|
<div id="ground"></div> |
|
|
|
<div id="controls"> |
|
<div id="time-display">12:00</div> |
|
<div id="pitch-display">Тембр: 0</div> |
|
<div id="volume-display">Громкость: 0</div> |
|
<button id="start-btn">Начать запись голоса</button> |
|
<button id="stop-btn" disabled>Остановить</button> |
|
</div> |
|
|
|
<div id="instructions"> |
|
<h3>Инструкция:</h3> |
|
<p>1. Нажмите "Начать запись голоса" и разрешите доступ к микрофону</p> |
|
<p>2. Говорите с разным тембром голоса:</p> |
|
<p>- Низкий тембр = ночь/раннее утро</p> |
|
<p>- Средний тембр = день</p> |
|
<p>- Высокий тембр = вечер/закат</p> |
|
<p>3. Громкость голоса влияет на яркость сцены</p> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
function setupScene() { |
|
|
|
const buildingsContainer = document.getElementById('buildings'); |
|
const buildingCount = 8; |
|
for (let i = 0; i < buildingCount; i++) { |
|
const building = document.createElement('div'); |
|
building.classList.add('building'); |
|
const height = 50 + Math.random() * 100; |
|
const width = 40 + Math.random() * 60; |
|
building.style.height = height + 'px'; |
|
building.style.width = width + 'px'; |
|
building.style.left = (i * (100 / buildingCount)) + '%'; |
|
|
|
|
|
const windowRows = Math.floor(height / 20); |
|
const windowCols = Math.floor(width / 15); |
|
for (let r = 0; r < windowRows; r++) { |
|
for (let c = 0; c < windowCols; c++) { |
|
const windowEl = document.createElement('div'); |
|
windowEl.classList.add('window'); |
|
windowEl.style.width = '10px'; |
|
windowEl.style.height = '15px'; |
|
windowEl.style.left = (c * 15 + 5) + 'px'; |
|
windowEl.style.top = (r * 20 + 5) + 'px'; |
|
building.appendChild(windowEl); |
|
} |
|
} |
|
|
|
buildingsContainer.appendChild(building); |
|
} |
|
|
|
|
|
const starsContainer = document.getElementById('stars'); |
|
for (let i = 0; i < 100; i++) { |
|
const star = document.createElement('div'); |
|
star.classList.add('star'); |
|
const size = 1 + Math.random() * 2; |
|
star.style.width = size + 'px'; |
|
star.style.height = size + 'px'; |
|
star.style.left = Math.random() * 100 + '%'; |
|
star.style.top = Math.random() * 100 + '%'; |
|
starsContainer.appendChild(star); |
|
} |
|
} |
|
|
|
|
|
function updateTimeOfDay(hour, minute, pitch, volume) { |
|
const gameContainer = document.getElementById('game-container'); |
|
const sky = document.getElementById('sky'); |
|
const ground = document.getElementById('ground'); |
|
const sunMoon = document.getElementById('sun-moon'); |
|
const buildings = document.querySelectorAll('.building'); |
|
const windows = document.querySelectorAll('.window'); |
|
const stars = document.getElementById('stars'); |
|
|
|
|
|
const time = hour + minute / 60; |
|
|
|
|
|
const angle = (time / 24) * 2 * Math.PI - Math.PI / 2; |
|
const radius = Math.min(window.innerWidth, window.innerHeight) * 0.4; |
|
const centerX = window.innerWidth / 2; |
|
const centerY = window.innerHeight * 0.7; |
|
const x = centerX + radius * Math.cos(angle); |
|
const y = centerY + radius * Math.sin(angle); |
|
|
|
sunMoon.style.left = (x - 40) + 'px'; |
|
sunMoon.style.top = (y - 40) + 'px'; |
|
|
|
|
|
let skyColor, groundColor, sunMoonColor, buildingColor, windowColor; |
|
let starsOpacity = 0; |
|
|
|
|
|
if (time >= 5 && time < 7) { |
|
skyColor = `rgb(${Math.floor(59 + (158-59) * (time-5)/2)}, ${Math.floor(88 + (206-88) * (time-5)/2)}, ${Math.floor(128 + (235-128) * (time-5)/2)})`; |
|
groundColor = '#385c46'; |
|
sunMoonColor = '#ff9e4f'; |
|
buildingColor = '#555'; |
|
windowColor = '#333'; |
|
} |
|
|
|
else if (time >= 7 && time < 17) { |
|
skyColor = '#9eceff'; |
|
groundColor = '#4a7c59'; |
|
sunMoonColor = '#ffee00'; |
|
buildingColor = '#666'; |
|
windowColor = '#444'; |
|
} |
|
|
|
else if (time >= 17 && time < 19) { |
|
const factor = (time - 17) / 2; |
|
skyColor = `rgb(${Math.floor(158 + (255-158) * factor)}, ${Math.floor(206 + (107-206) * factor)}, ${Math.floor(235 + (62-235) * factor)})`; |
|
groundColor = '#3d5d49'; |
|
sunMoonColor = '#ff6700'; |
|
buildingColor = '#555'; |
|
windowColor = '#b3721f'; |
|
} |
|
|
|
else if (time >= 19 && time < 21) { |
|
const factor = (time - 19) / 2; |
|
skyColor = `rgb(${Math.floor(255 - (255-20) * factor)}, ${Math.floor(107 - (107-42) * factor)}, ${Math.floor(62 - (62-87) * factor)})`; |
|
groundColor = '#2e463a'; |
|
sunMoonColor = `rgb(${Math.floor(255 - 200 * factor)}, ${Math.floor(103 - 43 * factor)}, ${Math.floor(0 + 230 * factor)})`; |
|
buildingColor = '#444'; |
|
windowColor = '#ffdb70'; |
|
starsOpacity = factor; |
|
} |
|
|
|
else { |
|
skyColor = '#14283b'; |
|
groundColor = '#1e2e26'; |
|
sunMoonColor = '#e0e0e0'; |
|
buildingColor = '#333'; |
|
windowColor = '#ffdb70'; |
|
starsOpacity = 1; |
|
} |
|
|
|
|
|
const brightnessMultiplier = 0.7 + volume * 0.3; |
|
|
|
|
|
function adjustBrightness(hexOrRgb, multiplier) { |
|
|
|
if (hexOrRgb.startsWith('rgb')) { |
|
const rgbValues = hexOrRgb.match(/\d+/g).map(Number); |
|
return `rgb(${Math.min(255, Math.floor(rgbValues[0] * multiplier))}, ${Math.min(255, Math.floor(rgbValues[1] * multiplier))}, ${Math.min(255, Math.floor(rgbValues[2] * multiplier))})`; |
|
} |
|
|
|
|
|
const hex = hexOrRgb.replace('#', ''); |
|
let r = parseInt(hex.substring(0, 2), 16); |
|
let g = parseInt(hex.substring(2, 4), 16); |
|
let b = parseInt(hex.substring(4, 6), 16); |
|
|
|
r = Math.min(255, Math.floor(r * multiplier)); |
|
g = Math.min(255, Math.floor(g * multiplier)); |
|
b = Math.min(255, Math.floor(b * multiplier)); |
|
|
|
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; |
|
} |
|
|
|
|
|
sky.style.backgroundColor = skyColor; |
|
ground.style.backgroundColor = adjustBrightness(groundColor, brightnessMultiplier); |
|
sunMoon.style.backgroundColor = adjustBrightness(sunMoonColor, brightnessMultiplier); |
|
|
|
buildings.forEach(building => { |
|
building.style.backgroundColor = adjustBrightness(buildingColor, brightnessMultiplier); |
|
}); |
|
|
|
windows.forEach(window => { |
|
|
|
if (time >= 19 || time < 7) { |
|
|
|
if (window.dataset.lit === undefined) { |
|
window.dataset.lit = Math.random() > 0.3 ? 'true' : 'false'; |
|
} |
|
if (window.dataset.lit === 'true') { |
|
window.style.backgroundColor = adjustBrightness(windowColor, brightnessMultiplier); |
|
} else { |
|
window.style.backgroundColor = '#333'; |
|
} |
|
} else { |
|
|
|
window.style.backgroundColor = '#444'; |
|
delete window.dataset.lit; |
|
} |
|
}); |
|
|
|
stars.style.opacity = starsOpacity; |
|
} |
|
|
|
|
|
let audioContext; |
|
let analyser; |
|
let microphone; |
|
let javascriptNode; |
|
let hours = 12; |
|
let minutes = 0; |
|
let lastPitch = 0; |
|
let lastVolume = 0; |
|
|
|
function startAudioProcessing() { |
|
const startBtn = document.getElementById('start-btn'); |
|
const stopBtn = document.getElementById('stop-btn'); |
|
|
|
startBtn.disabled = true; |
|
stopBtn.disabled = false; |
|
|
|
audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
|
analyser = audioContext.createAnalyser(); |
|
analyser.fftSize = 2048; |
|
|
|
|
|
navigator.mediaDevices.getUserMedia({ audio: true }) |
|
.then(function(stream) { |
|
microphone = audioContext.createMediaStreamSource(stream); |
|
microphone.connect(analyser); |
|
|
|
|
|
javascriptNode = audioContext.createScriptProcessor(2048, 1, 1); |
|
analyser.connect(javascriptNode); |
|
javascriptNode.connect(audioContext.destination); |
|
|
|
|
|
javascriptNode.onaudioprocess = processAudio; |
|
}) |
|
.catch(function(err) { |
|
console.error('Ошибка доступа к микрофону:', err); |
|
alert('Не удалось получить доступ к микрофону. Проверьте настройки браузера.'); |
|
startBtn.disabled = false; |
|
stopBtn.disabled = true; |
|
}); |
|
} |
|
|
|
function stopAudioProcessing() { |
|
const startBtn = document.getElementById('start-btn'); |
|
const stopBtn = document.getElementById('stop-btn'); |
|
|
|
startBtn.disabled = false; |
|
stopBtn.disabled = true; |
|
|
|
if (javascriptNode) { |
|
javascriptNode.onaudioprocess = null; |
|
javascriptNode.disconnect(); |
|
} |
|
|
|
if (microphone) { |
|
microphone.disconnect(); |
|
} |
|
|
|
if (analyser) { |
|
analyser.disconnect(); |
|
} |
|
|
|
if (audioContext) { |
|
audioContext.close(); |
|
} |
|
} |
|
|
|
function processAudio(event) { |
|
const bufferLength = analyser.frequencyBinCount; |
|
const dataArray = new Uint8Array(bufferLength); |
|
analyser.getByteFrequencyData(dataArray); |
|
|
|
|
|
let sum = 0; |
|
let count = 0; |
|
|
|
|
|
for (let i = 5; i < bufferLength / 4; i++) { |
|
if (dataArray[i] > 10) { |
|
sum += i * dataArray[i]; |
|
count += dataArray[i]; |
|
} |
|
} |
|
|
|
|
|
let volumeSum = 0; |
|
for (let i = 0; i < bufferLength; i++) { |
|
volumeSum += dataArray[i]; |
|
} |
|
let volumeAvg = volumeSum / bufferLength / 255; |
|
|
|
if (count > 0) { |
|
const normalizedPitch = sum / count / (bufferLength / 8); |
|
|
|
|
|
lastPitch = lastPitch * 0.7 + normalizedPitch * 0.3; |
|
lastVolume = lastVolume * 0.7 + volumeAvg * 0.3; |
|
|
|
|
|
document.getElementById('pitch-display').textContent = `Тембр: ${lastPitch.toFixed(2)}`; |
|
document.getElementById('volume-display').textContent = `Громкость: ${lastVolume.toFixed(2)}`; |
|
|
|
|
|
hours = Math.floor(lastPitch * 24); |
|
minutes = Math.floor((lastPitch * 24 - hours) * 60); |
|
|
|
|
|
document.getElementById('time-display').textContent = |
|
`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`; |
|
|
|
|
|
updateTimeOfDay(hours, minutes, lastPitch, lastVolume); |
|
} |
|
} |
|
|
|
|
|
window.onload = function() { |
|
setupScene(); |
|
updateTimeOfDay(hours, minutes, 0.5, 0.5); |
|
|
|
|
|
document.getElementById('start-btn').addEventListener('click', startAudioProcessing); |
|
document.getElementById('stop-btn').addEventListener('click', stopAudioProcessing); |
|
|
|
|
|
setInterval(function() { |
|
if (!audioContext) { |
|
minutes++; |
|
if (minutes >= 60) { |
|
minutes = 0; |
|
hours++; |
|
if (hours >= 24) { |
|
hours = 0; |
|
} |
|
} |
|
|
|
document.getElementById('time-display').textContent = |
|
`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`; |
|
|
|
updateTimeOfDay(hours, minutes, hours / 24, 0.5); |
|
} |
|
}, 1000); |
|
}; |
|
</script> |
|
</body> |
|
</html> |