heading-ears/index.html
2024-10-15 00:26:12 +09:00

617 lines
19 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<!-- lib -->
<script src="js/p5.min.js"></script>
<script src="js/Tone.min.js"></script>
<!-- css -->
<link type="text/css" rel="stylesheet" href="css/default.css">
<link type="text/css" rel="stylesheet" href="css/loading-animation.css" />
<!-- <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"> -->
<script>
// const AudioContext = window.AudioContext || window.webkitAudioContext;
// let audioCtx;
</script>
<script src="js/util.js"></script>
<script src="js/classes.js"></script>
</head>
<body>
<div id='overlay-userinput' class='fullscreen' onclick='initialize();'>
<div style='width:100%;'>
<div style='text-align: left;'>터치하고-</div>
<div style='text-align: right;'>시작하기!</div>
</div>
</div>
<div id='loading-veil' class='fullscreen animationStripes'>
<div>로~~~~~딩⏳</div>
</div>
<div id='announcements' class='fullscreen announcements'>
<div></div>
</div>
<button id='radiostart-userinput' onclick='document.querySelector("#radio").play();'>
<span style='font-size:3em;'>📻</span>
</button>
<button id='scene-minok' onclick='scene=10;sceneChanger();'>
<span style='font-size:3em;'>🍇</span>
</button>
<button id='scene-wonjung' onclick='scene=20;sceneChanger();'>
<span style='font-size:3em;'>🥑</span>
</button>
<button id='scene-all' onclick='scene=scene+1;sceneChanger();'>
<span style='font-size:3em;'>🥥</span>
</button>
<audio id="radio" controls crossorigin="anonymous">
<!-- iOS bug... you must add <source> later! -->
<!-- <source src="https://radio.dianaband.in:8000/stream" type="audio/aac" /> -->
</audio>
<script>
//shared
//device location & motion
let heading = 0;
let latitude = 37.574973;
let longitude = 126.925708;
// 맞는 결과 (예) : 작업실 -> 베지스
// p1 = [37.574973, 126.925708]
// p2 = [37.576774, 126.931232]
// getBearing(p1[0], p1[1], p2[0], p2[1])
// 67.6373956496954 (deg)
// getDistance(p1[0], p1[1], p2[0], p2[1])
// 0.5263963610554471 (km)
//audio playback permission checker
let silence;
let clap;
let radio;
//scene
let scene = -1;
//sounders' list
let scenes = {};
let minok_sounds = [];
let wonjung_sounds = [];
scenes.minok_sounds = minok_sounds;
scenes.wonjung_sounds = wonjung_sounds;
//all ready flag
let ready = false;
//clear all permissions
async function initialize() {
// device orientation data permission
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
requestOrientationPermission();
}
// remove the 'userinput' veil
document.querySelector("#overlay-userinput").style.display = 'none';
// show loading screen.
document.querySelector("#loading-veil").style.display = 'grid';
// unfreeze Tone.js with userinput
await Tone.start();
// start audiocontext
// audioCtx = new AudioContext;
// audioCtx = Tone.getContext();
// some sounds for check-in
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
silence.start();
// clap.start();
// register sounders to the list
minok_sounds.push(new Sounder({
name: '주파수 구간',
location: [37.575451, 126.926939],
soundfile: "./audio/minok_01.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
minok_sounds.push(new Sounder({
name: 'Transpose',
location: [37.575009, 126.926373],
soundfile: "./audio/minok_02.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
minok_sounds.push(new Sounder({
name: 'Delay',
location: [37.574383, 126.925500],
soundfile: "./audio/minok_03.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
minok_sounds.push(new Sounder({
name: 'EQ',
location: [37.573562, 126.924426],
soundfile: "./audio/minok_04.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
minok_sounds.push(new Sounder({
name: '모든 효과가 다 섞인 뇌절 of 뇌절 트랙',
location: [37.572931, 126.923612],
soundfile: "./audio/minok_05.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
minok_sounds.push(new Sounder({
name: '뇌절 트랙 (끝!)',
location: [37.571918, 126.922360],
soundfile: "./audio/minok_05.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
wonjung_sounds.push(new Sounder({
name: '푸르지오 앞 데크길 밑 운동기구',
location: [37.576013, 126.927912],
soundfile: "./audio/01.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
wonjung_sounds.push(new Sounder({
name: '연리지',
location: [37.574409, 126.925210],
soundfile: "./audio/02.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
wonjung_sounds.push(new Sounder({
name: '폭우와 멜로디혼',
location: [37.573430, 126.923445],
soundfile: "./audio/03.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
wonjung_sounds.push(new Sounder({
name: '농구대',
location: [37.573149, 126.922870],
soundfile: "./audio/04.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
wonjung_sounds.push(new Sounder({
name: '물소리 조각',
location: [37.574623, 126.924903],
soundfile: "./audio/05.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
wonjung_sounds.push(new Sounder({
name: '좌우패닝 벤치-지나가는 무게들',
location: [37.577172, 126.922993],
soundfile: "./audio/06.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
wonjung_sounds.push(new Sounder({
name: '귀뚜라미',
location: [37.576753, 126.929252],
soundfile: "./audio/07.mp3",
gain: 0,
spread: 20,
distmap: [0.01, 0.08, 0, -20], // (km) -> (dB)
//testing
volmin: -30,
}));
// start loading for all soundfile-based sounders.
//N.B. for 'await', watch out 'forEach' ... https://stackoverflow.com/a/37576787
for (const item of scenes.wonjung_sounds) await item.load();
for (const item of scenes.minok_sounds) await item.load();
// activate radio by injecting <source> tag
const audiotag = document.querySelector('#radio');
const sourcetag = document.createElement('source');
sourcetag.setAttribute('src', 'https://radio.dianaband.in:8000/stream');
sourcetag.setAttribute('type', 'audio/aac');
audiotag.appendChild(sourcetag);
//link a sounder object to radio streaming..
radio = new RadioSounder({
name: "베짜-📻",
location: [37.574973, 126.925708],
gain: 0,
spread: 90,
distmap: [0.01, 2, 0, -10], // (km) -> (dB)
//testing
volmin: -30,
});
radio.link(audiotag);
// audiotag.play(); // this doesn't work.. for some buggy iOS versions. another userinput needed..
// remove loading screen.
document.querySelector("#loading-veil").style.display = 'none';
// remove loading screen.
document.querySelector("#announcements").style.display = 'grid';
//
ready = true;
//
scene = 0;
sceneChanger();
}
async function sceneChanger() {
switch (scene) {
case 0:
await fullscreenSplasher('<div>스테이지</div>', 1000); // some *splashes*
await fullscreenSplasher('<div>0</div>', 1000);
await fullscreenSplasher('<div>시작~~~</div>', 3000);
await fullscreenSplasher('', 0);
break;
case 10:
await fullscreenSplasher('<div>스테이지</div>', 1000); // some *splashes*
await fullscreenSplasher('<div>1</div>', 1000);
await fullscreenSplasher('<div>시작~~~</div>', 3000);
await fullscreenSplasher('', 0);
break;
case 20:
await fullscreenSplasher('<div>스테이지</div>', 1000); // some *splashes*
await fullscreenSplasher('<div>2</div>', 1000);
await fullscreenSplasher('<div>시작~~~</div>', 3000);
await fullscreenSplasher('', 0);
break;
case 30:
await fullscreenSplasher('<div>스테이지</div>', 1000); // some *splashes*
await fullscreenSplasher('<div>3</div>', 1000);
await fullscreenSplasher('<div>시작~~~</div>', 3000);
await fullscreenSplasher('', 0);
break;
default:
;
}
}
async function fullscreenSplasher(html, timeout) {
let element = document.querySelector('#announcements');
// element.style.display = 'grid';
element.innerHTML = html;
await asyncTimeout(timeout);
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES);
}
function draw() {
//
background('#202325');
//all data is ready to be used.
if (ready) {
//update sounders
scenes.wonjung_sounds.forEach(item => item.update_location());
scenes.minok_sounds.forEach(item => item.update_location());
//update radio
radio.update_location();
radio.pv.pan.value = 0;
radio.pv.volume.rampTo(radio.gain, radio.ramptime);
////main group
//a tabular compass closet.
let compass_table;
//scene logic
if (scene == 0) {
// tutorial stage
scenes.wonjung_sounds.forEach(item => item.update_panvol());
//
compass_table = [];
Sounder.allStart(scenes.wonjung_sounds);
Sounder.allStop(scenes.minok_sounds);
// draw sth.
push();
let sounder = scenes.wonjung_sounds[0];
let compass_target = new Compass(250, ['#003456', '#77AAAA', 'navy']);
compass_target.pointer = sounder.angerr;
translate((windowWidth - compass_target.size) / 2, (windowHeight - compass_target.size) / 2, 0);
compass_target.draw();
fill('white');
Textline.restart();
Textline.put(sounder.name);
Textline.put(sounder.latitude.toFixed(4) + ', ' + sounder.longitude.toFixed(4));
Textline.put(sounder.angerr.toFixed(2) + ', ' + (sounder.dist * 1000).toFixed(2));
Textline.put(sounder.pv.pan.value.toFixed(2) + ', ' + sounder.distvol.toFixed(2) + ', ' + sounder.pv.volume.value.toFixed(2));
Textline.put(sounder.zone);
pop();
} else if (scene == 10) {
//no sound
//N.B. assumption: change all locations to 'trigger' locations!
//minok scene
// approaching to the spot. **before** reaching the first trigger location.
compass_table = scenes.minok_sounds;
Sounder.allSilent(scenes.minok_sounds);
Sounder.allStart(scenes.minok_sounds);
Sounder.allStop(scenes.wonjung_sounds);
//
let next = scenes.minok_sounds[0];
if (next.dist < 0.05) {
//fade-in
// console.log(next.snd.state);
next.pv.pan.value = 0;
next.pv.volume.rampTo(next.gain, next.ramptime);
scene = 11;
sceneChanger();
}
} else if (scene == 11) {
//minok scene
//listening to track #1
compass_table = scenes.minok_sounds;
//
let current = scenes.minok_sounds[0];
current.pv.volume.rampTo(current.gain, current.ramptime);
let next = scenes.minok_sounds[1];
if (next.dist < 0.05) {
//N.B. assumption: change location to 'playstart' location!
//cross-fade
current.pv.volume.rampTo(-99, current.ramptime);
next.pv.volume.rampTo(next.gain, next.ramptime);
scene = 12;
sceneChanger();
}
} else if (scene == 12) {
//minok scene
//listening to track #2
compass_table = scenes.minok_sounds;
//
let current = scenes.minok_sounds[1];
current.pv.volume.rampTo(current.gain, current.ramptime);
let next = scenes.minok_sounds[2];
if (next.dist < 0.05) {
//N.B. assumption: change location to 'playstart' location!
//cross-fade
current.pv.volume.rampTo(-99, current.ramptime);
next.pv.volume.rampTo(next.gain, next.ramptime);
scene = 13;
sceneChanger();
}
} else if (scene == 13) {
//minok scene
//listening to track #3
compass_table = scenes.minok_sounds;
//
let current = scenes.minok_sounds[2];
current.pv.volume.rampTo(current.gain, current.ramptime);
let next = scenes.minok_sounds[3];
if (next.dist < 0.05) {
//N.B. assumption: change location to 'playstart' location!
//cross-fade
current.pv.volume.rampTo(-99, current.ramptime);
next.pv.volume.rampTo(next.gain, next.ramptime);
scene = 14;
sceneChanger();
}
} else if (scene == 14) {
//minok scene
//listening to track #4
compass_table = scenes.minok_sounds;
//
let current = scenes.minok_sounds[3];
current.pv.volume.rampTo(current.gain, current.ramptime);
let next = scenes.minok_sounds[4];
if (next.dist < 0.05) {
//N.B. assumption: change location to 'playstart' location!
//cross-fade
current.pv.volume.rampTo(-99, current.ramptime);
next.pv.volume.rampTo(next.gain, next.ramptime);
scene = 15;
sceneChanger();
}
} else if (scene == 15) {
//minok scene
//listening to track #5
compass_table = scenes.minok_sounds;
//
let current = scenes.minok_sounds[4];
current.pv.volume.rampTo(current.gain, current.ramptime);
let next = scenes.minok_sounds[5]; // <-- dummy location, no sound. ending waypoint.
if (next.dist < 0.05) {
//N.B. assumption: change location to 'playstart' location!
//fade-out
current.pv.volume.rampTo(-99, current.ramptime);
next.pv.volume.rampTo(-99, next.ramptime);
scene = 16;
sceneChanger();
}
} else if (scene == 16) {
//minok scene
//No-no zone.
compass_table = [];
Sounder.allStop(scenes.minok_sounds);
Sounder.allStop(scenes.minok_sounds);
} else if (scene == 20) {
//wonjung scene
compass_table = scenes.wonjung_sounds;
Sounder.allStart(scenes.wonjung_sounds);
Sounder.allStop(scenes.minok_sounds);
scenes.wonjung_sounds.forEach(item => item.update_panvol());
} else {
//everything else
// nothing to show/playback.
compass_table = [];
Sounder.allStop(scenes.wonjung_sounds);
Sounder.allStop(scenes.minok_sounds);
}
//
push(); // +1
translate(20, 90, 0);
//
let compass_offsets = [
createVector(0, 0),
createVector(0, 110),
createVector(0, 220),
createVector(0, 330),
createVector(150, 0),
createVector(150, 110),
createVector(150, 220),
createVector(150, 330),
];
for (const [i, sounder] of compass_table.entries()) {
push(); // +2
translate(compass_offsets[i].x, compass_offsets[i].y, 0);
//compass targeting the location
// let compass_target = new Compass(120, ['#33A1DE', '#97FFFF', 'navy']);
let compass_target = new Compass(120, ['#003456', '#77AAAA', 'navy']);
compass_target.pointer = sounder.angerr;
compass_target.draw();
//debug
// translate(0, 120, 0);
translate(0, 0, 0);
fill('white');
Textline.restart();
Textline.put(sounder.name);
Textline.put(sounder.latitude.toFixed(4) + ', ' + sounder.longitude.toFixed(4));
Textline.put(sounder.angerr.toFixed(2) + ', ' + (sounder.dist * 1000).toFixed(2));
Textline.put(sounder.pv.pan.value.toFixed(2) + ', ' + sounder.distvol.toFixed(2) + ', ' + sounder.pv.volume.value.toFixed(2));
pop(); // -2
}
pop(); // -1
// ////radio
// push(); // +2
// translate(windowWidth - 120, windowHeight - 200, 0);
//
// //compass targeting the location
// // let compass_target = new Compass(120, ['#33A1DE', '#97FFFF', 'navy']);
// let compass_radio = new Compass(120, ['#003456', '#77AAAA', 'navy']);
// compass_radio.pointer = radio.angerr;
// compass_radio.draw();
//
// //debug
// // translate(0, 120, 0);
// translate(0, 0, 0);
// fill('white');
// Textline.restart();
// Textline.put(radio.name);
// Textline.put(radio.latitude.toFixed(4) + ', ' + radio.longitude.toFixed(4));
// Textline.put(radio.angerr.toFixed(2) + ', ' + (radio.dist * 1000).toFixed(2));
// Textline.put(radio.pv.pan.value.toFixed(2) + ', ' + radio.distvol.toFixed(2) + ', ' + radio.pv.volume.value.toFixed(2));
//
// pop(); // -2
////status-left group
push(); // +1
translate(20, windowHeight - 100, 0);
fill('white');
Textline.restart();
Textline.put('current location');
Textline.put(latitude.toFixed(6) + ', ' + longitude.toFixed(6));
Textline.put('scene');
Textline.put(scene);
pop(); // -1
////status-right group
push(); // +1
translate(windowWidth - 50, windowHeight - 100, 0);
let compass_north = new Compass(40, ['yellow', 'navy', 'white']);
compass_north.pointer = heading * -1; // the North pole azimuth is opposite of my heading.
compass_north.draw();
translate(0, 40, 0);
fill('white');
Textline.restart();
Textline.put('N');
Textline.put(heading.toFixed(2));
pop(); // -1
}
}
</script>
</body>
</html>