617 lines
19 KiB
HTML
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> |