init
This commit is contained in:
commit
6d65616f6b
42 changed files with 12803 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
.ftpconfig
|
||||||
|
.jshintrc
|
||||||
138
archive/compass1.html
Normal file
138
archive/compass1.html
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="js/p5.min.js"></script>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 id="lat">lat: </h1>
|
||||||
|
<h1 id="long">long: </h1>
|
||||||
|
<h1 id="heading">heading: </h1>
|
||||||
|
<button onclick='requestOrientationPermission();'>Request orientation permission</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
//
|
||||||
|
//gps, heading
|
||||||
|
const options = {
|
||||||
|
enableHighAccuracy: true,
|
||||||
|
maximumAge: 30000,
|
||||||
|
timeout: 27000,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/) || navigator.userAgent.match(/AppleWebKit/)) {
|
||||||
|
function requestOrientationPermission() {
|
||||||
|
DeviceOrientationEvent.requestPermission()
|
||||||
|
.then(response => {
|
||||||
|
if (response == 'granted') {
|
||||||
|
// window.addEventListener('deviceorientation', function(eventData) {
|
||||||
|
window.addEventListener("deviceorientation", (event) => {
|
||||||
|
|
||||||
|
var compassdir;
|
||||||
|
if (event.webkitCompassHeading) {
|
||||||
|
// Apple works only with this, alpha doesn't work
|
||||||
|
compassdir = event.webkitCompassHeading;
|
||||||
|
} else compassdir = event.alpha;
|
||||||
|
|
||||||
|
let heading = document.getElementById('heading');
|
||||||
|
heading.innerText = 'heading: ' + compassdir;
|
||||||
|
compass1.heading = -compassdir;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.addEventListener("deviceorientationabsolute", (event) => {
|
||||||
|
let heading = document.getElementById('heading');
|
||||||
|
heading.innerText = 'heading: ' + event.alpha;
|
||||||
|
compass1.heading = event.alpha;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const watchID = navigator.geolocation.watchPosition((position) => {
|
||||||
|
let lat = document.getElementById('lat');
|
||||||
|
lat.innerText = 'lat: ' + position.coords.latitude;
|
||||||
|
let long = document.getElementById('long');
|
||||||
|
long.innerText = 'long: ' + position.coords.longitude;
|
||||||
|
},
|
||||||
|
(error) => {},
|
||||||
|
options);
|
||||||
|
|
||||||
|
//p5
|
||||||
|
class Compass {
|
||||||
|
constructor(cx, cy, size, colors) {
|
||||||
|
this.cx = cx;
|
||||||
|
this.cy = cy;
|
||||||
|
this.size = size;
|
||||||
|
this.colors = colors;
|
||||||
|
this.heading = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
push();
|
||||||
|
//
|
||||||
|
translate(this.cx, this.cy);
|
||||||
|
scale(this.size);
|
||||||
|
//
|
||||||
|
translate(0.5, 0.5);
|
||||||
|
//
|
||||||
|
noStroke();
|
||||||
|
//
|
||||||
|
fill(this.colors[0]);
|
||||||
|
circle(0, 0, 1);
|
||||||
|
//
|
||||||
|
fill(this.colors[1]);
|
||||||
|
rotate(this.heading);
|
||||||
|
quad(0.1, 0.2, 0, -0.4, -0.1, 0.2, 0, 0.1);
|
||||||
|
//
|
||||||
|
fill(this.colors[2]);
|
||||||
|
circle(0, 0, 0.1);
|
||||||
|
//
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let compass1;
|
||||||
|
let compass_reading = 0;
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(windowWidth, windowHeight);
|
||||||
|
compass1 = new Compass(100, 100, 200, ['red', 'limegreen', 'yellow']);
|
||||||
|
compass2 = new Compass(300, 300, 20, ['yellow', 'navy', 'white']);
|
||||||
|
angleMode(DEGREES);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
//
|
||||||
|
background(100);
|
||||||
|
|
||||||
|
//compass
|
||||||
|
fill(200);
|
||||||
|
noStroke();
|
||||||
|
// compass1.heading = compass_reading;
|
||||||
|
// compass1.heading = 5;
|
||||||
|
compass1.draw();
|
||||||
|
// compass2.draw();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
67
archive/get_gps_and_heading.html
Executable file
67
archive/get_gps_and_heading.html
Executable file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!-- <script src="/js/p5.min.js"></script> -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1 id="lat">lat: </h1>
|
||||||
|
<h1 id="long">long: </h1>
|
||||||
|
<h1 id="heading">heading: </h1>
|
||||||
|
<button onclick='requestOrientationPermission();'>Request orientation permission</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
enableHighAccuracy: true,
|
||||||
|
maximumAge: 30000,
|
||||||
|
timeout: 27000,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/) || navigator.userAgent.match(/AppleWebKit/)) {
|
||||||
|
function requestOrientationPermission(){
|
||||||
|
DeviceOrientationEvent.requestPermission()
|
||||||
|
.then(response => {
|
||||||
|
if (response == 'granted') {
|
||||||
|
// window.addEventListener('deviceorientation', function(eventData) {
|
||||||
|
window.addEventListener("deviceorientation", (event) => {
|
||||||
|
|
||||||
|
var compassdir;
|
||||||
|
if(event.webkitCompassHeading) {
|
||||||
|
// Apple works only with this, alpha doesn't work
|
||||||
|
compassdir = event.webkitCompassHeading;
|
||||||
|
}
|
||||||
|
else compassdir = event.alpha;
|
||||||
|
|
||||||
|
let heading = document.getElementById('heading');
|
||||||
|
heading.innerText = 'heading: ' + compassdir;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.addEventListener("deviceorientationabsolute", (event) => {
|
||||||
|
let heading = document.getElementById('heading');
|
||||||
|
heading.innerText = 'heading: ' + event.alpha;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const watchID = navigator.geolocation.watchPosition((position) => {
|
||||||
|
let lat = document.getElementById('lat');
|
||||||
|
lat.innerText = 'lat: ' + position.coords.latitude;
|
||||||
|
let long = document.getElementById('long');
|
||||||
|
long.innerText = 'long: ' + position.coords.longitude;
|
||||||
|
},
|
||||||
|
(error) => {},
|
||||||
|
options);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
215
archive/index_alt.html
Normal file
215
archive/index_alt.html
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
<!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 rel="stylesheet" href="css/default.css">
|
||||||
|
<!-- script -->
|
||||||
|
<script>
|
||||||
|
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||||
|
let audioCtx;
|
||||||
|
</script>
|
||||||
|
<script src="js/util.js"></script>
|
||||||
|
<script src="js/classes.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button class='overlay-userinput' onclick='initialize();'>
|
||||||
|
<div>터치하고-시작하기!</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<audio controls crossorigin="anonymous" style="display:none;">
|
||||||
|
<!-- <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;
|
||||||
|
// let latitude = 37.576774;
|
||||||
|
// let longitude = 126.931232;
|
||||||
|
|
||||||
|
// 맞는 결과 (예) : 작업실 -> 베지스
|
||||||
|
// 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;
|
||||||
|
//all ready flag (not used)
|
||||||
|
let ready = false;
|
||||||
|
|
||||||
|
//clear all permissions
|
||||||
|
async function initialize() {
|
||||||
|
|
||||||
|
// device orientation data permission
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
requestOrientationPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
// start audiocontext
|
||||||
|
audioCtx = new AudioContext;
|
||||||
|
|
||||||
|
// some sounds for check-in
|
||||||
|
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||||
|
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||||
|
|
||||||
|
// start loading for all sounds
|
||||||
|
sounds.forEach(async item => await item.load());
|
||||||
|
|
||||||
|
// unfreeze audio playback for Tone.js
|
||||||
|
silence.start();
|
||||||
|
|
||||||
|
// remove the veil
|
||||||
|
let veil = document.querySelector(".overlay-userinput");
|
||||||
|
veil.style.display = 'none';
|
||||||
|
|
||||||
|
// activate radio by adding source tag
|
||||||
|
const audiotag = document.querySelector('audio'); // N.B. assuming there's only 1 audio tag for streaming...
|
||||||
|
const sourcetag = document.createElement('source');
|
||||||
|
sourcetag.setAttribute('src', 'https://radio.dianaband.in:8000/stream');
|
||||||
|
sourcetag.setAttribute('type', 'audio/aac');
|
||||||
|
audiotag.appendChild(sourcetag);
|
||||||
|
audiotag.play();
|
||||||
|
|
||||||
|
// //link a sounder object to radio streaming..
|
||||||
|
// radio = new Sounder({
|
||||||
|
// // location: [37.576013, 126.927912],
|
||||||
|
// location: [37.574973, 126.935708],
|
||||||
|
// soundfile: "./audio/clap01.mp3",
|
||||||
|
// gain: 6,
|
||||||
|
// spread: 15,
|
||||||
|
// distmap: [0.005, 0.05, 0, -10],
|
||||||
|
// //testing
|
||||||
|
// volmin: -30,
|
||||||
|
// });
|
||||||
|
// // radio.radiolink(audiotag);
|
||||||
|
// radio.load();
|
||||||
|
|
||||||
|
//
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let compass_target;
|
||||||
|
let compass_north;
|
||||||
|
let sounds = [];
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(windowWidth, windowHeight);
|
||||||
|
compass_target = new Compass(200, ['#97FFFF', '#33A1DE', 'white']);
|
||||||
|
compass_north = new Compass(40, ['yellow', 'navy', 'white']);
|
||||||
|
angleMode(DEGREES);
|
||||||
|
|
||||||
|
// register sounders to the list
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.576774, 126.931232],
|
||||||
|
soundfile: "./audio/01.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
//
|
||||||
|
background('#202325');
|
||||||
|
|
||||||
|
//all data is ready to be used.
|
||||||
|
if (ready) {
|
||||||
|
|
||||||
|
//update sounders
|
||||||
|
sounds.forEach(async item => await item.update());
|
||||||
|
// radio.update();
|
||||||
|
|
||||||
|
//
|
||||||
|
let target = sounds[0];
|
||||||
|
|
||||||
|
////main group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//compass (the North)
|
||||||
|
translate(20, 20, 0);
|
||||||
|
compass_target.heading = target.angerr; //target #0
|
||||||
|
compass_target.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
//debug
|
||||||
|
fill('white');
|
||||||
|
translate(30, 200, 0);
|
||||||
|
Textline.restart();
|
||||||
|
Textline.put('ang');
|
||||||
|
Textline.put(target.ang);
|
||||||
|
Textline.put('angerr');
|
||||||
|
Textline.put(target.angerr);
|
||||||
|
Textline.put('distance');
|
||||||
|
Textline.put(target.dist);
|
||||||
|
// Textline.put('pan');
|
||||||
|
// Textline.put(target.pv.pan.value);
|
||||||
|
// Textline.put('distvol');
|
||||||
|
// Textline.put(target.distvol);
|
||||||
|
// Textline.put('vol');
|
||||||
|
// Textline.put(target.pv.volume.value);
|
||||||
|
Textline.put('target_lat');
|
||||||
|
Textline.put(target.latitude);
|
||||||
|
Textline.put('target_lon');
|
||||||
|
Textline.put(target.longitude);
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
////status-left group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(20, windowHeight - 100, 0);
|
||||||
|
fill('white');
|
||||||
|
Textline.restart();
|
||||||
|
Textline.put('current location');
|
||||||
|
Textline.put(latitude);
|
||||||
|
Textline.put(longitude);
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
|
||||||
|
////status-right group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(windowWidth - 50, windowHeight - 50, 0);
|
||||||
|
compass_north.heading = heading;
|
||||||
|
compass_north.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
479
archive/index_old.html
Executable file
479
archive/index_old.html
Executable file
|
|
@ -0,0 +1,479 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="js/p5.min.js"></script>
|
||||||
|
<script src="js/Tone.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-userinput {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
color: white;
|
||||||
|
z-index: 2;
|
||||||
|
/* display: none; */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button class='overlay-userinput' onclick='requestPermissions();'>
|
||||||
|
<div>터치하고-시작하기!</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<audio controls crossorigin="anonymous">
|
||||||
|
<!-- <source src="https://radio.dianaband.in:8000/stream" type="audio/aac" /> -->
|
||||||
|
</audio>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// //
|
||||||
|
// Tone.setContext(new Tone.Context());
|
||||||
|
// // Tone.js is using 'standardized-audio-context' internally. and 'standardized-audio-context' only connects to 'standardized-audio-context' nodes... so, we want to change 'standardized-audio-context' ==> window.AudioContext || window.webkitAudioContext... irony.. standardized doesn't guarantee.. basic audio tag...
|
||||||
|
|
||||||
|
//shared
|
||||||
|
//device location & motion
|
||||||
|
let heading = 0;
|
||||||
|
let latitude = 38.57451;
|
||||||
|
let longitude = 126.92612;
|
||||||
|
// let latitude = 0;
|
||||||
|
// let longitude = 0;
|
||||||
|
//audio playback permission checker
|
||||||
|
let silence;
|
||||||
|
let clap;
|
||||||
|
let radio;
|
||||||
|
//all ready flag (not used)
|
||||||
|
let ready = false;
|
||||||
|
|
||||||
|
//promisify -> new Tone.Player
|
||||||
|
function AudioImport(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var audio = new Tone.Player(url, () => resolve(audio));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear all permissions
|
||||||
|
function requestPermissions() {
|
||||||
|
|
||||||
|
// device orientation data permission
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
requestOrientationPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sound playback permission
|
||||||
|
silence.start();
|
||||||
|
|
||||||
|
//
|
||||||
|
let veil = document.querySelector(".overlay-userinput");
|
||||||
|
veil.style.display = 'none';
|
||||||
|
|
||||||
|
//activate radio by adding source tag
|
||||||
|
const audiotag = document.querySelector('audio'); // N.B. assuming there's only 1 audio tag for streaming...
|
||||||
|
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 Sounder({
|
||||||
|
location: [37.576013, 126.927912],
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
});
|
||||||
|
radio.radiolink(audiotag);
|
||||||
|
|
||||||
|
//
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//gps, heading
|
||||||
|
//get permissions
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
function requestOrientationPermission() {
|
||||||
|
DeviceOrientationEvent
|
||||||
|
.requestPermission()
|
||||||
|
.then(response => {
|
||||||
|
if (response == 'granted') {
|
||||||
|
window.addEventListener("deviceorientation", (event) => {
|
||||||
|
if (event.webkitCompassHeading) {
|
||||||
|
heading = event.webkitCompassHeading * -1;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.addEventListener("deviceorientationabsolute", (event) => {
|
||||||
|
heading = event.alpha;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const watchID = navigator.geolocation.watchPosition(
|
||||||
|
(position) => {
|
||||||
|
latitude = position.coords.latitude;
|
||||||
|
longitude = position.coords.longitude;
|
||||||
|
},
|
||||||
|
(error) => {}, {
|
||||||
|
enableHighAccuracy: true,
|
||||||
|
maximumAge: 30000,
|
||||||
|
timeout: 27000,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//distance between 2 locations (lat/lon)
|
||||||
|
function getDistance(lat1, lon1, lat2, lon2) {
|
||||||
|
var R = 6371; // Radius of the earth in km
|
||||||
|
var dLat = deg2rad(lat2 - lat1); // deg2rad below
|
||||||
|
var dLon = deg2rad(lon2 - lon1);
|
||||||
|
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||||
|
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||||
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
var d = R * c; // Distance in km
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deg2rad(deg) {
|
||||||
|
return deg * (Math.PI / 180)
|
||||||
|
}
|
||||||
|
|
||||||
|
//bearing between 2 locations (lat/lon)
|
||||||
|
|
||||||
|
// Converts from degrees to radians.
|
||||||
|
function toRadians(degrees) {
|
||||||
|
return degrees * Math.PI / 180;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts from radians to degrees.
|
||||||
|
function toDegrees(radians) {
|
||||||
|
return radians * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const y = Math.sin(λ2-λ1) * Math.cos(φ2);
|
||||||
|
// const x = Math.cos(φ1)*Math.sin(φ2) -
|
||||||
|
// Math.sin(φ1)*Math.cos(φ2)*Math.cos(λ2-λ1);
|
||||||
|
// const θ = Math.atan2(y, x);
|
||||||
|
// const brng = (θ*180/Math.PI + 360) % 360; // in degrees
|
||||||
|
|
||||||
|
function getBearing(startLat, startLng, destLat, destLng) {
|
||||||
|
startLat = toRadians(startLat);
|
||||||
|
startLng = toRadians(startLng);
|
||||||
|
destLat = toRadians(destLat);
|
||||||
|
destLng = toRadians(destLng);
|
||||||
|
|
||||||
|
y = Math.sin(destLng - startLng) * Math.cos(destLat);
|
||||||
|
x = Math.cos(startLat) * Math.sin(destLat) -
|
||||||
|
Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
|
||||||
|
brng = Math.atan2(y, x);
|
||||||
|
brng = toDegrees(brng);
|
||||||
|
return (brng + 360) % 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sounds
|
||||||
|
class Sounder {
|
||||||
|
constructor(args) {
|
||||||
|
this.soundfile = args.soundfile;
|
||||||
|
this.gain = args.gain; // (dB)
|
||||||
|
this.latitude = args.location[0];
|
||||||
|
this.longitude = args.location[1];
|
||||||
|
this.spread = args.spread; // (deg)
|
||||||
|
this.distmap = args.distmap;
|
||||||
|
this.volmin = args.volmin; // (dB)
|
||||||
|
this.ramptime = args.ramptime; // (seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
//start sound playback
|
||||||
|
this.pv = new Tone.PanVol(0, -99).toDestination();
|
||||||
|
this.snd = await AudioImport(this.soundfile); // NOTE: url with spaces didn't work here.
|
||||||
|
// this.snd.connect(this.pv).start();
|
||||||
|
this.snd.connect(this.pv);
|
||||||
|
this.snd.loop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
radiolink(audiotag) {
|
||||||
|
//start sound playback
|
||||||
|
this.pv = new Tone.PanVol(0, -99).toDestination();
|
||||||
|
let stream = Tone.context.createMediaElementSource(audiotag);
|
||||||
|
this.snd = Tone.context.createGain();
|
||||||
|
stream.connect(this.snd);
|
||||||
|
console.log(stream);
|
||||||
|
console.log(this.snd);
|
||||||
|
console.log(this.pv);
|
||||||
|
// this.snd.connect(this.pv.input.input); // a hack ...
|
||||||
|
this.snd.gain.value = 1;
|
||||||
|
this.snd.connect(Tone.context.destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
//
|
||||||
|
//update sound pan/volume
|
||||||
|
//
|
||||||
|
this.ang = getBearing(latitude, longitude, this.latitude, this.longitude); // (deg)
|
||||||
|
this.angerr = (((heading - this.ang + 360) % 360) + 180) % 360 - 180;
|
||||||
|
this.dist = getDistance(latitude, longitude, this.latitude, this.longitude); // (km)
|
||||||
|
//
|
||||||
|
this.distvol = map(this.dist, this.distmap[0], this.distmap[1], this.distmap[2], this.distmap[3], true); //(dB)
|
||||||
|
|
||||||
|
// (from Pure Data patch "iamyou", [eqpan2~])
|
||||||
|
// arg #1 (inlet #3): width:
|
||||||
|
// -width*(1.5) ~ -width/2 -> left fade-in
|
||||||
|
// -width/2 ~ width/2 -> cross fading
|
||||||
|
// +width/2 ~ width*(1.5) -> right fade-out
|
||||||
|
|
||||||
|
let panleft_start = this.spread * (-1.5);
|
||||||
|
let panleft_end = this.spread * (-0.5);
|
||||||
|
let panright_start = this.spread * (0.5);
|
||||||
|
let panright_end = this.spread * (1.5);
|
||||||
|
|
||||||
|
//left fade-in
|
||||||
|
if (this.angerr > panleft_start && this.angerr < panleft_end) {
|
||||||
|
this.pv.pan.value = -1; //left-full
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain + map(this.angerr, panleft_start, panleft_end, this.volmin, 0), this.ramptime); //(dB)
|
||||||
|
}
|
||||||
|
//cross fading
|
||||||
|
else if (this.angerr > panleft_end && this.angerr < panright_start) {
|
||||||
|
this.pv.pan.value = map(this.angerr, panleft_end, panright_start, -1, 1); // crossfade
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain, this.ramptime); //(dB)
|
||||||
|
}
|
||||||
|
//right fade-out
|
||||||
|
else if (this.angerr > panright_start && this.angerr < panright_end) {
|
||||||
|
this.pv.pan.value = 1; //right-full
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain + map(this.angerr, panright_start, panright_end, 0, this.volmin), this.ramptime); //(dB)
|
||||||
|
}
|
||||||
|
//slience
|
||||||
|
else {
|
||||||
|
this.pv.volume.rampTo(-99, 10); //(dB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
//draw sound location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//p5
|
||||||
|
class Compass {
|
||||||
|
constructor(size, colors) {
|
||||||
|
this.size = size;
|
||||||
|
this.colors = colors;
|
||||||
|
this.heading = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
push();
|
||||||
|
//
|
||||||
|
scale(this.size);
|
||||||
|
//
|
||||||
|
translate(0.5, 0.5);
|
||||||
|
//
|
||||||
|
noStroke();
|
||||||
|
//
|
||||||
|
fill(this.colors[0]);
|
||||||
|
circle(0, 0, 0.9);
|
||||||
|
//
|
||||||
|
fill(this.colors[1]);
|
||||||
|
rotate(this.heading);
|
||||||
|
quad(0.1, 0.2, 0, -0.4, -0.1, 0.2, 0, 0.1);
|
||||||
|
//
|
||||||
|
fill(this.colors[2]);
|
||||||
|
circle(0, 0, 0.1);
|
||||||
|
//
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let compass_target;
|
||||||
|
let compass_north;
|
||||||
|
let sounds = [];
|
||||||
|
|
||||||
|
async function preload() {
|
||||||
|
|
||||||
|
//some sounds for check-in
|
||||||
|
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||||
|
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||||
|
|
||||||
|
//register sounders to the list
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.576013, 126.927912],
|
||||||
|
soundfile: "./audio/01.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.574409, 126.925210],
|
||||||
|
soundfile: "./audio/02.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.573149, 126.922870],
|
||||||
|
soundfile: "./audio/03.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.577172, 126.922993],
|
||||||
|
soundfile: "./audio/04.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
// sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
// sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
// sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
// sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
|
||||||
|
//preload all sounds
|
||||||
|
sounds.forEach(async item => await item.load());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(windowWidth, windowHeight);
|
||||||
|
compass_target1 = new Compass(100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_target2 = new Compass(100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_target3 = new Compass(100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_target4 = new Compass(100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_north = new Compass(40, ['yellow', 'navy', 'white']);
|
||||||
|
angleMode(DEGREES);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lines = 0;
|
||||||
|
let linestep = 20;
|
||||||
|
|
||||||
|
function textline(t, restart) {
|
||||||
|
if (restart) lines = 0;
|
||||||
|
else lines = lines + 1;
|
||||||
|
text(t, 0, linestep * lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
//
|
||||||
|
background('olive');
|
||||||
|
|
||||||
|
//all data is ready to be used.
|
||||||
|
if (ready) {
|
||||||
|
|
||||||
|
//update sounders
|
||||||
|
sounds.forEach(async item => await item.update());
|
||||||
|
radio.update();
|
||||||
|
|
||||||
|
//
|
||||||
|
let target = sounds[0];
|
||||||
|
|
||||||
|
////main group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//compass (the North)
|
||||||
|
translate(20, 20, 0);
|
||||||
|
compass_target1.heading = radio.angerr; //target #0
|
||||||
|
compass_target1.draw();
|
||||||
|
translate(100, 0, 0);
|
||||||
|
compass_target2.heading = sounds[1].angerr; //target #0
|
||||||
|
compass_target2.draw();
|
||||||
|
translate(-100, 100, 0);
|
||||||
|
compass_target3.heading = sounds[2].angerr; //target #0
|
||||||
|
compass_target3.draw();
|
||||||
|
translate(100, 0, 0);
|
||||||
|
compass_target4.heading = sounds[3].angerr; //target #0
|
||||||
|
compass_target4.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
//debug
|
||||||
|
fill('white');
|
||||||
|
translate(30, 200, 0);
|
||||||
|
textline('angerr', true);
|
||||||
|
textline(target.angerr);
|
||||||
|
textline('distance');
|
||||||
|
textline(target.dist);
|
||||||
|
textline('pan');
|
||||||
|
textline(target.pv.pan.value);
|
||||||
|
textline('distvol');
|
||||||
|
textline(target.distvol);
|
||||||
|
textline('vol');
|
||||||
|
textline(target.pv.volume.value);
|
||||||
|
textline('target_lat');
|
||||||
|
textline(target.latitude);
|
||||||
|
textline('target_lon');
|
||||||
|
textline(target.longitude);
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
////status-left group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(20, windowHeight - 100, 0);
|
||||||
|
fill('white');
|
||||||
|
textline('current location', true);
|
||||||
|
textline(latitude);
|
||||||
|
textline(longitude);
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
|
||||||
|
////status-right group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(windowWidth - 50, windowHeight - 50, 0);
|
||||||
|
compass_north.heading = heading;
|
||||||
|
compass_north.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
44
archive/radio_heeju.html
Normal file
44
archive/radio_heeju.html
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="js//hls.min.js" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<title>Radio</title>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<video id="video" controls></video>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var video = document.getElementById('video');
|
||||||
|
if (Hls.isSupported()) {
|
||||||
|
var hls = new Hls({
|
||||||
|
debug: true,
|
||||||
|
// html5: { nativeAudioTracks: false }
|
||||||
|
});
|
||||||
|
hls.loadSource('https://radio.ururu.cloud/hls/live.m3u8');
|
||||||
|
hls.attachMedia(video);
|
||||||
|
hls.on(Hls.Events.MEDIA_ATTACHED, function () {
|
||||||
|
video.muted = true;
|
||||||
|
video.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// hls.js is not supported on platforms that do not have Media Source Extensions (MSE) enabled.
|
||||||
|
// When the browser has built-in HLS support (check using `canPlayType`), we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video element through the `src` property.
|
||||||
|
// This is using the built-in support of the plain video element, without using hls.js.
|
||||||
|
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||||
|
video.src = 'https://radio.ururu.cloud/hls/live.m3u8';
|
||||||
|
video.addEventListener('canplay', function () {
|
||||||
|
video.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
54
archive/radio_test_heeju.html
Executable file
54
archive/radio_test_heeju.html
Executable file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="js/p5.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.4/howler.min.js"></script>
|
||||||
|
|
||||||
|
<title>Sketch</title>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<audio controls>
|
||||||
|
<source src="https://radio.dianaband.in:8000/stream" type="audio/aac" />
|
||||||
|
</audio>
|
||||||
|
|
||||||
|
<audio controls loop>
|
||||||
|
<source src="audio/102.mp3" type="audio/mpeg" />
|
||||||
|
</audio>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function setup() {
|
||||||
|
createCanvas(600, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
//sky blue background
|
||||||
|
background(135, 206, 235);
|
||||||
|
//sun in top right
|
||||||
|
fill("yellow"); //yellow
|
||||||
|
|
||||||
|
stroke("orange"); //orange outline
|
||||||
|
|
||||||
|
strokeWeight(20); //large outline
|
||||||
|
|
||||||
|
circle(550, 50, 100);
|
||||||
|
//grass on bottom half
|
||||||
|
|
||||||
|
stroke(0); //black outline
|
||||||
|
|
||||||
|
strokeWeight(1); //outline thickness
|
||||||
|
|
||||||
|
fill("green");
|
||||||
|
|
||||||
|
rect(0, 200, 600, 200);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
375
archive/sound_finder_v1.html
Normal file
375
archive/sound_finder_v1.html
Normal file
|
|
@ -0,0 +1,375 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="js/p5.min.js"></script>
|
||||||
|
<script src="js/Tone-14.8.36.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-userinput {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
color: white;
|
||||||
|
z-index: 2;
|
||||||
|
/* display: none; */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button class='overlay-userinput' onclick='requestPermissions();'>
|
||||||
|
<div>터치하고-시작하기!</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
//
|
||||||
|
//shared
|
||||||
|
//device location & motion
|
||||||
|
let heading = 0;
|
||||||
|
let latitude = 37;
|
||||||
|
let longitude = 126;
|
||||||
|
// let latitude = 0;
|
||||||
|
// let longitude = 0;
|
||||||
|
//audio playback permission checker
|
||||||
|
let silence;
|
||||||
|
let clap;
|
||||||
|
//all ready flag (not used)
|
||||||
|
let ready = false;
|
||||||
|
|
||||||
|
//promisify -> new Tone.Player
|
||||||
|
function AudioImport(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var audio = new Tone.Player(url, () => resolve(audio));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear all permissions
|
||||||
|
function requestPermissions() {
|
||||||
|
|
||||||
|
// device orientation data permission
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
requestOrientationPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sound playback permission
|
||||||
|
silence.start();
|
||||||
|
|
||||||
|
//
|
||||||
|
let veil = document.querySelector(".overlay-userinput");
|
||||||
|
veil.style.display = 'none';
|
||||||
|
|
||||||
|
//
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//gps, heading
|
||||||
|
//get permissions
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
function requestOrientationPermission() {
|
||||||
|
DeviceOrientationEvent
|
||||||
|
.requestPermission()
|
||||||
|
.then(response => {
|
||||||
|
if (response == 'granted') {
|
||||||
|
window.addEventListener("deviceorientation", (event) => {
|
||||||
|
if (event.webkitCompassHeading) {
|
||||||
|
heading = event.webkitCompassHeading * -1;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.addEventListener("deviceorientationabsolute", (event) => {
|
||||||
|
heading = event.alpha;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const watchID = navigator.geolocation.watchPosition(
|
||||||
|
(position) => {
|
||||||
|
latitude = position.coords.latitude;
|
||||||
|
longitude = position.coords.longitude;
|
||||||
|
},
|
||||||
|
(error) => {}, {
|
||||||
|
enableHighAccuracy: true,
|
||||||
|
maximumAge: 30000,
|
||||||
|
timeout: 27000,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//distance between 2 locations (lat/lon)
|
||||||
|
function getDistance(lat1, lon1, lat2, lon2) {
|
||||||
|
var R = 6371; // Radius of the earth in km
|
||||||
|
var dLat = deg2rad(lat2 - lat1); // deg2rad below
|
||||||
|
var dLon = deg2rad(lon2 - lon1);
|
||||||
|
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||||
|
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||||
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
var d = R * c; // Distance in km
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deg2rad(deg) {
|
||||||
|
return deg * (Math.PI / 180)
|
||||||
|
}
|
||||||
|
|
||||||
|
//bearing between 2 locations (lat/lon)
|
||||||
|
|
||||||
|
// Converts from degrees to radians.
|
||||||
|
function toRadians(degrees) {
|
||||||
|
return degrees * Math.PI / 180;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts from radians to degrees.
|
||||||
|
function toDegrees(radians) {
|
||||||
|
return radians * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const y = Math.sin(λ2-λ1) * Math.cos(φ2);
|
||||||
|
// const x = Math.cos(φ1)*Math.sin(φ2) -
|
||||||
|
// Math.sin(φ1)*Math.cos(φ2)*Math.cos(λ2-λ1);
|
||||||
|
// const θ = Math.atan2(y, x);
|
||||||
|
// const brng = (θ*180/Math.PI + 360) % 360; // in degrees
|
||||||
|
|
||||||
|
function getBearing(startLat, startLng, destLat, destLng) {
|
||||||
|
startLat = toRadians(startLat);
|
||||||
|
startLng = toRadians(startLng);
|
||||||
|
destLat = toRadians(destLat);
|
||||||
|
destLng = toRadians(destLng);
|
||||||
|
|
||||||
|
y = Math.sin(destLng - startLng) * Math.cos(destLat);
|
||||||
|
x = Math.cos(startLat) * Math.sin(destLat) -
|
||||||
|
Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
|
||||||
|
brng = Math.atan2(y, x);
|
||||||
|
brng = toDegrees(brng);
|
||||||
|
return (brng + 360) % 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sounds
|
||||||
|
class Sounder {
|
||||||
|
constructor(soundfile, latitude, longitude, spread) {
|
||||||
|
this.soundfile = soundfile;
|
||||||
|
this.latitude = latitude;
|
||||||
|
this.longitude = longitude;
|
||||||
|
this.spread = spread; //deg
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
//start sound playback
|
||||||
|
this.pv = new Tone.PanVol(0, -99).toDestination();
|
||||||
|
this.snd = await AudioImport(this.soundfile); // NOTE: url with spaces didn't work here.
|
||||||
|
this.snd.connect(this.pv).start();
|
||||||
|
this.snd.loop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
//
|
||||||
|
//update sound pan/volume
|
||||||
|
//
|
||||||
|
this.ang = getBearing(latitude, longitude, this.latitude, this.longitude); //deg
|
||||||
|
this.angerr = (((heading - this.ang + 360) % 360) + 180) % 360 - 180;
|
||||||
|
this.dist = getDistance(latitude, longitude, this.latitude, this.longitude); //km
|
||||||
|
//
|
||||||
|
this.distvol = map(this.dist, 0.005, 0.05, 0, -10, true); //(dB)
|
||||||
|
|
||||||
|
// (from Pure Data patch "iamyou", [eqpan2~])
|
||||||
|
// arg #1 (inlet #3): width:
|
||||||
|
// -width*(1.5) ~ -width/2 -> left fade-in
|
||||||
|
// -width/2 ~ width/2 -> cross fading
|
||||||
|
// +width/2 ~ width*(1.5) -> right fade-out
|
||||||
|
|
||||||
|
let panleft_start = this.spread * (-1.5);
|
||||||
|
let panleft_end = this.spread * (-0.5);
|
||||||
|
let panright_start = this.spread * (0.5);
|
||||||
|
let panright_end = this.spread * (1.5);
|
||||||
|
|
||||||
|
//left fade-in
|
||||||
|
if (this.angerr > panleft_start && this.angerr < panleft_end) {
|
||||||
|
this.pv.pan.value = -1; //left-full
|
||||||
|
this.pv.volume.exponentialRampTo(this.distvol + map(this.angerr, panleft_start, panleft_end, -50, 0), 3); //(dB)
|
||||||
|
}
|
||||||
|
//cross fading
|
||||||
|
else if (this.angerr > panleft_end && this.angerr < panright_start) {
|
||||||
|
this.pv.pan.value = map(this.angerr, panleft_end, panright_start, -1, 1); // crossfade
|
||||||
|
this.pv.volume.exponentialRampTo(this.distvol, 3); //(dB)
|
||||||
|
}
|
||||||
|
//right fade-out
|
||||||
|
else if (this.angerr > panright_start && this.angerr < panright_end) {
|
||||||
|
this.pv.pan.value = 1; //right-full
|
||||||
|
this.pv.volume.exponentialRampTo(this.distvol + map(this.angerr, panright_start, panright_end, 0, -50), 3); //(dB)
|
||||||
|
}
|
||||||
|
//slience
|
||||||
|
else {
|
||||||
|
this.pv.pan.value = 0; //silent
|
||||||
|
this.pv.volume.exponentialRampTo(-99, 10); //(dB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
//draw sound location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//p5
|
||||||
|
class Compass {
|
||||||
|
constructor(size, colors) {
|
||||||
|
this.size = size;
|
||||||
|
this.colors = colors;
|
||||||
|
this.heading = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
push();
|
||||||
|
//
|
||||||
|
scale(this.size);
|
||||||
|
//
|
||||||
|
translate(0.5, 0.5);
|
||||||
|
//
|
||||||
|
noStroke();
|
||||||
|
//
|
||||||
|
fill(this.colors[0]);
|
||||||
|
circle(0, 0, 0.9);
|
||||||
|
//
|
||||||
|
fill(this.colors[1]);
|
||||||
|
rotate(this.heading);
|
||||||
|
quad(0.1, 0.2, 0, -0.4, -0.1, 0.2, 0, 0.1);
|
||||||
|
//
|
||||||
|
fill(this.colors[2]);
|
||||||
|
circle(0, 0, 0.1);
|
||||||
|
//
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let compass_target;
|
||||||
|
let compass_north;
|
||||||
|
let sounds = [];
|
||||||
|
|
||||||
|
async function preload() {
|
||||||
|
|
||||||
|
//some sounds for check-in
|
||||||
|
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||||
|
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||||
|
|
||||||
|
//register sounders to the list
|
||||||
|
sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
|
||||||
|
//preload all sounds
|
||||||
|
sounds.forEach(async item => await item.load());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(windowWidth, windowHeight);
|
||||||
|
compass_target = new Compass(windowWidth - 100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_north = new Compass(40, ['yellow', 'navy', 'white']);
|
||||||
|
angleMode(DEGREES);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lines = 0;
|
||||||
|
let linestep = 20;
|
||||||
|
|
||||||
|
function textline(t, restart) {
|
||||||
|
if (restart) lines = 0;
|
||||||
|
else lines = lines + 1;
|
||||||
|
text(t, 0, linestep * lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
//
|
||||||
|
background('olive');
|
||||||
|
|
||||||
|
//all data is ready to be used.
|
||||||
|
if (ready) {
|
||||||
|
|
||||||
|
//update sounders
|
||||||
|
sounds.forEach(async item => await item.update());
|
||||||
|
|
||||||
|
//
|
||||||
|
let target = sounds[0];
|
||||||
|
|
||||||
|
////main group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//compass (the North)
|
||||||
|
translate((windowWidth - compass_target.size) / 2, (windowWidth - compass_target.size) / 2, 0);
|
||||||
|
compass_target.heading = target.angerr; //target #0
|
||||||
|
compass_target.draw();
|
||||||
|
|
||||||
|
//debug
|
||||||
|
fill('white');
|
||||||
|
translate(-30, 0, 0);
|
||||||
|
textline('angerr', true);
|
||||||
|
textline(target.angerr);
|
||||||
|
textline('distance');
|
||||||
|
textline(target.dist);
|
||||||
|
textline('pan');
|
||||||
|
textline(target.pv.pan.value);
|
||||||
|
textline('distvol');
|
||||||
|
textline(target.distvol);
|
||||||
|
textline('vol');
|
||||||
|
textline(target.pv.volume.value);
|
||||||
|
textline('target_lat');
|
||||||
|
textline(target.latitude);
|
||||||
|
textline('target_lon');
|
||||||
|
textline(target.longitude);
|
||||||
|
|
||||||
|
//(gap)
|
||||||
|
translate(0, compass_target.size, 0);
|
||||||
|
|
||||||
|
//gps
|
||||||
|
translate(0, 20, 0);
|
||||||
|
fill('white');
|
||||||
|
textline('current location', true);
|
||||||
|
textline(latitude);
|
||||||
|
textline(longitude);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
|
||||||
|
////status group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(windowWidth - 50, windowHeight - 50, 0);
|
||||||
|
compass_north.heading = heading;
|
||||||
|
compass_north.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
99
archive/stream_ios.html
Normal file
99
archive/stream_ios.html
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="js/p5.min.js"></script>
|
||||||
|
<script src="js/Tone.min.js"></script>
|
||||||
|
|
||||||
|
<title>Sketch</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-userinput {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 255, 255, 0.5);
|
||||||
|
color: black;
|
||||||
|
z-index: 2;
|
||||||
|
/* display: none; */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button class='overlay-userinput' onclick='requestPermissions();'>
|
||||||
|
<div>터치하고-시작하기!</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- <audio controls>
|
||||||
|
<source src="https://radio.dianaband.in:8000/stream" type="audio/aac" />
|
||||||
|
<source src="audio/delayecho.mp3" type="audio/mpeg" />
|
||||||
|
</audio> -->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// const audioCtx = new AudioContext();
|
||||||
|
// const myAudio = document.querySelector("audio");
|
||||||
|
// const source = audioCtx.createMediaElementSource(myAudio);
|
||||||
|
// const gainNode = audioCtx.createGain();
|
||||||
|
// gainNode.gain.value = 1;
|
||||||
|
// // source.connect(gainNode);
|
||||||
|
// gainNode.connect(audioCtx.destination);
|
||||||
|
// https://radio.dianaband.in:8000/stream
|
||||||
|
|
||||||
|
let silence;
|
||||||
|
let clap;
|
||||||
|
|
||||||
|
//clear all permissions
|
||||||
|
function requestPermissions() {
|
||||||
|
|
||||||
|
// sound playback permission
|
||||||
|
silence.start();
|
||||||
|
clap.start();
|
||||||
|
|
||||||
|
//
|
||||||
|
let veil = document.querySelector(".overlay-userinput");
|
||||||
|
veil.style.display = 'none';
|
||||||
|
|
||||||
|
//
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//promisify -> new Tone.Player
|
||||||
|
function AudioImport(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var audio = new Tone.Player(url, () => resolve(audio));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function preload() {
|
||||||
|
//some sounds for check-in
|
||||||
|
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||||
|
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(windowWidth, windowHeight);
|
||||||
|
angleMode(DEGREES);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
72
archive/webaudio_ios.html
Normal file
72
archive/webaudio_ios.html
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="js/p5.min.js"></script>
|
||||||
|
<script src="js/Tone.min.js"></script>
|
||||||
|
|
||||||
|
<title>Sketch</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-userinput {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 255, 255, 0.5);
|
||||||
|
color: black;
|
||||||
|
z-index: 2;
|
||||||
|
/* display: none; */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button id='btn' class='overlay-userinput' onclick='requestPermissions();'>
|
||||||
|
<div>터치하고-시작하기!</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- <audio controls>
|
||||||
|
<source src="https://radio.dianaband.in:8000/stream" type="audio/aac" />
|
||||||
|
<source src="audio/delayecho.mp3" type="audio/mpeg" />
|
||||||
|
</audio> -->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function requestPermissions() {
|
||||||
|
const audio = new Audio();
|
||||||
|
|
||||||
|
audio.crossOrigin = "anonymous";
|
||||||
|
const AudioContext = window.AudioContext || window.webkitAudioContext,
|
||||||
|
audioCtx = new AudioContext(),
|
||||||
|
audioSrc = audioCtx.createMediaElementSource(audio);
|
||||||
|
|
||||||
|
audioSrc.connect(audioCtx.destination);
|
||||||
|
|
||||||
|
audio.addEventListener('canplaythrough', () => {
|
||||||
|
audio.play();
|
||||||
|
|
||||||
|
audio.src = 'https://radio.dianaband.in:8000/stream';
|
||||||
|
audio.type = 'audio/aac'
|
||||||
|
audio.load();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
447
archive/wonjung.html
Normal file
447
archive/wonjung.html
Normal file
|
|
@ -0,0 +1,447 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="js/p5.min.js"></script>
|
||||||
|
<script src="js/Tone-14.8.36.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-userinput {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
color: white;
|
||||||
|
z-index: 2;
|
||||||
|
/* display: none; */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button class='overlay-userinput' onclick='requestPermissions();'>
|
||||||
|
<div>터치하고-시작하기!</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
//
|
||||||
|
//shared
|
||||||
|
//device location & motion
|
||||||
|
let heading = 0;
|
||||||
|
let latitude = 37.576762;
|
||||||
|
let longitude = 126.931267;
|
||||||
|
// let latitude = 0;
|
||||||
|
// let longitude = 0;
|
||||||
|
//audio playback permission checker
|
||||||
|
let silence;
|
||||||
|
let clap;
|
||||||
|
//all ready flag (not used)
|
||||||
|
let ready = false;
|
||||||
|
|
||||||
|
//promisify -> new Tone.Player
|
||||||
|
function AudioImport(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var audio = new Tone.Player(url, () => resolve(audio));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear all permissions
|
||||||
|
function requestPermissions() {
|
||||||
|
|
||||||
|
// device orientation data permission
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
requestOrientationPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sound playback permission
|
||||||
|
silence.start();
|
||||||
|
|
||||||
|
//
|
||||||
|
let veil = document.querySelector(".overlay-userinput");
|
||||||
|
veil.style.display = 'none';
|
||||||
|
|
||||||
|
//
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//gps, heading
|
||||||
|
//get permissions
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
function requestOrientationPermission() {
|
||||||
|
DeviceOrientationEvent
|
||||||
|
.requestPermission()
|
||||||
|
.then(response => {
|
||||||
|
if (response == 'granted') {
|
||||||
|
window.addEventListener("deviceorientation", (event) => {
|
||||||
|
if (event.webkitCompassHeading) {
|
||||||
|
heading = event.webkitCompassHeading * -1;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.addEventListener("deviceorientationabsolute", (event) => {
|
||||||
|
heading = event.alpha;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const watchID = navigator.geolocation.watchPosition(
|
||||||
|
(position) => {
|
||||||
|
//latitude = position.coords.latitude;
|
||||||
|
//longitude = position.coords.longitude;
|
||||||
|
},
|
||||||
|
(error) => {}, {
|
||||||
|
enableHighAccuracy: true,
|
||||||
|
maximumAge: 30000,
|
||||||
|
timeout: 27000,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//distance between 2 locations (lat/lon)
|
||||||
|
function getDistance(lat1, lon1, lat2, lon2) {
|
||||||
|
var R = 6371; // Radius of the earth in km
|
||||||
|
var dLat = deg2rad(lat2 - lat1); // deg2rad below
|
||||||
|
var dLon = deg2rad(lon2 - lon1);
|
||||||
|
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||||
|
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||||
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
var d = R * c; // Distance in km
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deg2rad(deg) {
|
||||||
|
return deg * (Math.PI / 180)
|
||||||
|
}
|
||||||
|
|
||||||
|
//bearing between 2 locations (lat/lon)
|
||||||
|
|
||||||
|
// Converts from degrees to radians.
|
||||||
|
function toRadians(degrees) {
|
||||||
|
return degrees * Math.PI / 180;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts from radians to degrees.
|
||||||
|
function toDegrees(radians) {
|
||||||
|
return radians * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const y = Math.sin(λ2-λ1) * Math.cos(φ2);
|
||||||
|
// const x = Math.cos(φ1)*Math.sin(φ2) -
|
||||||
|
// Math.sin(φ1)*Math.cos(φ2)*Math.cos(λ2-λ1);
|
||||||
|
// const θ = Math.atan2(y, x);
|
||||||
|
// const brng = (θ*180/Math.PI + 360) % 360; // in degrees
|
||||||
|
|
||||||
|
function getBearing(startLat, startLng, destLat, destLng) {
|
||||||
|
startLat = toRadians(startLat);
|
||||||
|
startLng = toRadians(startLng);
|
||||||
|
destLat = toRadians(destLat);
|
||||||
|
destLng = toRadians(destLng);
|
||||||
|
|
||||||
|
y = Math.sin(destLng - startLng) * Math.cos(destLat);
|
||||||
|
x = Math.cos(startLat) * Math.sin(destLat) -
|
||||||
|
Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
|
||||||
|
brng = Math.atan2(y, x);
|
||||||
|
brng = toDegrees(brng);
|
||||||
|
return (brng + 360) % 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sounds
|
||||||
|
class Sounder {
|
||||||
|
constructor(args) {
|
||||||
|
this.soundfile = args.soundfile;
|
||||||
|
this.gain = args.gain; // (dB)
|
||||||
|
this.latitude = args.location[0];
|
||||||
|
this.longitude = args.location[1];
|
||||||
|
this.spread = args.spread; // (deg)
|
||||||
|
this.distmap = args.distmap;
|
||||||
|
this.volmin = args.volmin; // (dB)
|
||||||
|
this.ramptime = args.ramptime; // (seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
//start sound playback
|
||||||
|
this.pv = new Tone.PanVol(0, -99).toDestination();
|
||||||
|
this.snd = await AudioImport(this.soundfile); // NOTE: url with spaces didn't work here.
|
||||||
|
this.snd.connect(this.pv).start();
|
||||||
|
this.snd.loop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
//
|
||||||
|
//update sound pan/volume
|
||||||
|
//
|
||||||
|
this.ang = getBearing(latitude, longitude, this.latitude, this.longitude); // (deg)
|
||||||
|
this.angerr = (((heading - this.ang + 360) % 360) + 180) % 360 - 180;
|
||||||
|
this.dist = getDistance(latitude, longitude, this.latitude, this.longitude); // (km)
|
||||||
|
//
|
||||||
|
this.distvol = map(this.dist, this.distmap[0], this.distmap[1], this.distmap[2], this.distmap[3], true); //(dB)
|
||||||
|
|
||||||
|
// (from Pure Data patch "iamyou", [eqpan2~])
|
||||||
|
// arg #1 (inlet #3): width:
|
||||||
|
// -width*(1.5) ~ -width/2 -> left fade-in
|
||||||
|
// -width/2 ~ width/2 -> cross fading
|
||||||
|
// +width/2 ~ width*(1.5) -> right fade-out
|
||||||
|
|
||||||
|
let panleft_start = this.spread * (-1.5);
|
||||||
|
let panleft_end = this.spread * (-0.5);
|
||||||
|
let panright_start = this.spread * (0.5);
|
||||||
|
let panright_end = this.spread * (1.5);
|
||||||
|
|
||||||
|
//left fade-in
|
||||||
|
if (this.angerr > panleft_start && this.angerr < panleft_end) {
|
||||||
|
this.pv.pan.value = -1; //left-full
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain + map(this.angerr, panleft_start, panleft_end, this.volmin, 0), this.ramptime); //(dB)
|
||||||
|
}
|
||||||
|
//cross fading
|
||||||
|
else if (this.angerr > panleft_end && this.angerr < panright_start) {
|
||||||
|
this.pv.pan.value = map(this.angerr, panleft_end, panright_start, -1, 1); // crossfade
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain, this.ramptime); //(dB)
|
||||||
|
}
|
||||||
|
//right fade-out
|
||||||
|
else if (this.angerr > panright_start && this.angerr < panright_end) {
|
||||||
|
this.pv.pan.value = 1; //right-full
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain + map(this.angerr, panright_start, panright_end, 0, this.volmin), this.ramptime); //(dB)
|
||||||
|
}
|
||||||
|
//slience
|
||||||
|
else {
|
||||||
|
this.pv.volume.rampTo(-99, 10); //(dB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
//draw sound location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//p5
|
||||||
|
class Compass {
|
||||||
|
constructor(size, colors) {
|
||||||
|
this.size = size;
|
||||||
|
this.colors = colors;
|
||||||
|
this.heading = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
push();
|
||||||
|
//
|
||||||
|
scale(this.size);
|
||||||
|
//
|
||||||
|
translate(0.5, 0.5);
|
||||||
|
//
|
||||||
|
noStroke();
|
||||||
|
//
|
||||||
|
fill(this.colors[0]);
|
||||||
|
circle(0, 0, 0.9);
|
||||||
|
//
|
||||||
|
fill(this.colors[1]);
|
||||||
|
rotate(this.heading);
|
||||||
|
quad(0.1, 0.2, 0, -0.4, -0.1, 0.2, 0, 0.1);
|
||||||
|
//
|
||||||
|
fill(this.colors[2]);
|
||||||
|
circle(0, 0, 0.1);
|
||||||
|
//
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let compass_target;
|
||||||
|
let compass_north;
|
||||||
|
let sounds = [];
|
||||||
|
|
||||||
|
async function preload() {
|
||||||
|
|
||||||
|
//some sounds for check-in
|
||||||
|
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||||
|
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||||
|
|
||||||
|
//register sounders to the list
|
||||||
|
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [35.574593, 126.925577],
|
||||||
|
soundfile: "./audio/minok01.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [35.574411, 126.925311],
|
||||||
|
soundfile: "./audio/minok02.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.573811, 126.924411],
|
||||||
|
soundfile: "./audio/minok03.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.572811, 126.923511],
|
||||||
|
soundfile: "./audio/minok04.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.571711, 126.922311],
|
||||||
|
soundfile: "./audio/minok05.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
// sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
// sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
// sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
// sounds.push(new Sounder("./audio/delayecho.mp3", 37.57451, 126.92612, 30));
|
||||||
|
|
||||||
|
//preload all sounds
|
||||||
|
sounds.forEach(async item => await item.load());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(windowWidth, windowHeight);
|
||||||
|
compass_target1 = new Compass(100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_target2 = new Compass(100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_target3 = new Compass(100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_target4 = new Compass(100, ['springgreen', 'navy', 'white']);
|
||||||
|
compass_north = new Compass(40, ['yellow', 'navy', 'white']);
|
||||||
|
angleMode(DEGREES);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lines = 0;
|
||||||
|
let linestep = 20;
|
||||||
|
|
||||||
|
function textline(t, restart) {
|
||||||
|
if (restart) lines = 0;
|
||||||
|
else lines = lines + 1;
|
||||||
|
text(t, 0, linestep * lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
//
|
||||||
|
background('olive');
|
||||||
|
|
||||||
|
//all data is ready to be used.
|
||||||
|
if (ready) {
|
||||||
|
|
||||||
|
//update sounders
|
||||||
|
sounds.forEach(async item => await item.update());
|
||||||
|
|
||||||
|
//
|
||||||
|
let target = sounds[0];
|
||||||
|
|
||||||
|
////main group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//compass (the North)
|
||||||
|
translate(20, 20, 0);
|
||||||
|
compass_target1.heading = sounds[0].angerr; //target #0
|
||||||
|
compass_target1.draw();
|
||||||
|
translate(100, 0, 0);
|
||||||
|
compass_target2.heading = sounds[1].angerr; //target #0
|
||||||
|
compass_target2.draw();
|
||||||
|
translate(-100, 100, 0);
|
||||||
|
compass_target3.heading = sounds[2].angerr; //target #0
|
||||||
|
compass_target3.draw();
|
||||||
|
translate(100, 0, 0);
|
||||||
|
compass_target4.heading = sounds[3].angerr; //target #0
|
||||||
|
compass_target4.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
//debug
|
||||||
|
fill('white');
|
||||||
|
translate(30, 200, 0);
|
||||||
|
textline('angerr', true);
|
||||||
|
textline(target.angerr);
|
||||||
|
textline('distance');
|
||||||
|
textline(target.dist);
|
||||||
|
textline('pan');
|
||||||
|
textline(target.pv.pan.value);
|
||||||
|
textline('distvol');
|
||||||
|
textline(target.distvol);
|
||||||
|
textline('vol');
|
||||||
|
textline(target.pv.volume.value);
|
||||||
|
textline('target_lat');
|
||||||
|
textline(target.latitude);
|
||||||
|
textline('target_lon');
|
||||||
|
textline(target.longitude);
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
////status-left group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(20, windowHeight - 100, 0);
|
||||||
|
fill('white');
|
||||||
|
textline('current location', true);
|
||||||
|
textline(latitude);
|
||||||
|
textline(longitude);
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
|
||||||
|
////status-right group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(windowWidth - 50, windowHeight - 50, 0);
|
||||||
|
compass_north.heading = heading;
|
||||||
|
compass_north.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
BIN
audio/01.mp3
Normal file
BIN
audio/01.mp3
Normal file
Binary file not shown.
BIN
audio/02.mp3
Normal file
BIN
audio/02.mp3
Normal file
Binary file not shown.
BIN
audio/03.mp3
Normal file
BIN
audio/03.mp3
Normal file
Binary file not shown.
BIN
audio/04.mp3
Normal file
BIN
audio/04.mp3
Normal file
Binary file not shown.
BIN
audio/102.mp3
Normal file
BIN
audio/102.mp3
Normal file
Binary file not shown.
BIN
audio/_silence.wav
Normal file
BIN
audio/_silence.wav
Normal file
Binary file not shown.
BIN
audio/clap01.mp3
Normal file
BIN
audio/clap01.mp3
Normal file
Binary file not shown.
BIN
audio/delayecho.mp3
Normal file
BIN
audio/delayecho.mp3
Normal file
Binary file not shown.
BIN
audio/minok01.mp3
Normal file
BIN
audio/minok01.mp3
Normal file
Binary file not shown.
BIN
audio/minok02.mp3
Normal file
BIN
audio/minok02.mp3
Normal file
Binary file not shown.
BIN
audio/minok03.mp3
Normal file
BIN
audio/minok03.mp3
Normal file
Binary file not shown.
BIN
audio/minok04.mp3
Normal file
BIN
audio/minok04.mp3
Normal file
Binary file not shown.
BIN
audio/minok05.mp3
Normal file
BIN
audio/minok05.mp3
Normal file
Binary file not shown.
BIN
audio/minok1frequency.mp3
Normal file
BIN
audio/minok1frequency.mp3
Normal file
Binary file not shown.
BIN
audio/minok2transpose.mp3
Normal file
BIN
audio/minok2transpose.mp3
Normal file
Binary file not shown.
BIN
audio/minok3delay.mp3
Normal file
BIN
audio/minok3delay.mp3
Normal file
Binary file not shown.
BIN
audio/minok4eq.mp3
Normal file
BIN
audio/minok4eq.mp3
Normal file
Binary file not shown.
BIN
audio/집중호우와멜로디언.mp3
Normal file
BIN
audio/집중호우와멜로디언.mp3
Normal file
Binary file not shown.
24
css/default.css
Normal file
24
css/default.css
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-userinput {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
color: white;
|
||||||
|
z-index: 2;
|
||||||
|
/* display: none; */
|
||||||
|
}
|
||||||
215
index.html
Normal file
215
index.html
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
<!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 rel="stylesheet" href="css/default.css">
|
||||||
|
<!-- script -->
|
||||||
|
<script>
|
||||||
|
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||||
|
let audioCtx;
|
||||||
|
</script>
|
||||||
|
<script src="js/util.js"></script>
|
||||||
|
<script src="js/classes.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button class='overlay-userinput' onclick='initialize();'>
|
||||||
|
<div>터치하고-시작하기!</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<audio controls crossorigin="anonymous" style="display:none;">
|
||||||
|
<!-- <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;
|
||||||
|
// let latitude = 37.576774;
|
||||||
|
// let longitude = 126.931232;
|
||||||
|
|
||||||
|
// 맞는 결과 (예) : 작업실 -> 베지스
|
||||||
|
// 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;
|
||||||
|
//all ready flag (not used)
|
||||||
|
let ready = false;
|
||||||
|
|
||||||
|
//clear all permissions
|
||||||
|
async function initialize() {
|
||||||
|
|
||||||
|
// device orientation data permission
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
requestOrientationPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
// start audiocontext
|
||||||
|
audioCtx = new AudioContext;
|
||||||
|
|
||||||
|
// some sounds for check-in
|
||||||
|
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||||
|
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||||
|
|
||||||
|
// start loading for all sounds
|
||||||
|
sounds.forEach(async item => await item.load());
|
||||||
|
|
||||||
|
// unfreeze audio playback for Tone.js
|
||||||
|
silence.start();
|
||||||
|
|
||||||
|
// remove the veil
|
||||||
|
let veil = document.querySelector(".overlay-userinput");
|
||||||
|
veil.style.display = 'none';
|
||||||
|
|
||||||
|
// activate radio by adding source tag
|
||||||
|
const audiotag = document.querySelector('audio'); // N.B. assuming there's only 1 audio tag for streaming...
|
||||||
|
const sourcetag = document.createElement('source');
|
||||||
|
sourcetag.setAttribute('src', 'https://radio.dianaband.in:8000/stream');
|
||||||
|
sourcetag.setAttribute('type', 'audio/aac');
|
||||||
|
audiotag.appendChild(sourcetag);
|
||||||
|
audiotag.play();
|
||||||
|
|
||||||
|
// //link a sounder object to radio streaming..
|
||||||
|
// radio = new Sounder({
|
||||||
|
// // location: [37.576013, 126.927912],
|
||||||
|
// location: [37.574973, 126.935708],
|
||||||
|
// soundfile: "./audio/clap01.mp3",
|
||||||
|
// gain: 6,
|
||||||
|
// spread: 15,
|
||||||
|
// distmap: [0.005, 0.05, 0, -10],
|
||||||
|
// //testing
|
||||||
|
// volmin: -30,
|
||||||
|
// });
|
||||||
|
// // radio.radiolink(audiotag);
|
||||||
|
// radio.load();
|
||||||
|
|
||||||
|
//
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let compass_target;
|
||||||
|
let compass_north;
|
||||||
|
let sounds = [];
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(windowWidth, windowHeight);
|
||||||
|
compass_target = new Compass(200, ['#97FFFF', '#33A1DE', 'white']);
|
||||||
|
compass_north = new Compass(40, ['yellow', 'navy', 'white']);
|
||||||
|
angleMode(DEGREES);
|
||||||
|
|
||||||
|
// register sounders to the list
|
||||||
|
sounds.push(new Sounder({
|
||||||
|
location: [37.576774, 126.931232],
|
||||||
|
soundfile: "./audio/01.mp3",
|
||||||
|
gain: 6,
|
||||||
|
spread: 15,
|
||||||
|
distmap: [0.005, 0.05, 0, -10],
|
||||||
|
//testing
|
||||||
|
volmin: -30,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
//
|
||||||
|
background('#202325');
|
||||||
|
|
||||||
|
//all data is ready to be used.
|
||||||
|
if (ready) {
|
||||||
|
|
||||||
|
//update sounders
|
||||||
|
sounds.forEach(async item => await item.update());
|
||||||
|
// radio.update();
|
||||||
|
|
||||||
|
//
|
||||||
|
let target = sounds[0];
|
||||||
|
|
||||||
|
////main group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//compass (the North)
|
||||||
|
translate(20, 20, 0);
|
||||||
|
compass_target.heading = target.angerr; //target #0
|
||||||
|
compass_target.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
//debug
|
||||||
|
fill('white');
|
||||||
|
translate(30, 200, 0);
|
||||||
|
Textline.restart();
|
||||||
|
Textline.put('ang');
|
||||||
|
Textline.put(target.ang);
|
||||||
|
Textline.put('angerr');
|
||||||
|
Textline.put(target.angerr);
|
||||||
|
Textline.put('distance');
|
||||||
|
Textline.put(target.dist);
|
||||||
|
// Textline.put('pan');
|
||||||
|
// Textline.put(target.pv.pan.value);
|
||||||
|
// Textline.put('distvol');
|
||||||
|
// Textline.put(target.distvol);
|
||||||
|
// Textline.put('vol');
|
||||||
|
// Textline.put(target.pv.volume.value);
|
||||||
|
Textline.put('target_lat');
|
||||||
|
Textline.put(target.latitude);
|
||||||
|
Textline.put('target_lon');
|
||||||
|
Textline.put(target.longitude);
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
////status-left group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(20, windowHeight - 100, 0);
|
||||||
|
fill('white');
|
||||||
|
Textline.restart();
|
||||||
|
Textline.put('current location');
|
||||||
|
Textline.put(latitude);
|
||||||
|
Textline.put(longitude);
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
|
||||||
|
|
||||||
|
////status-right group
|
||||||
|
|
||||||
|
//(push)
|
||||||
|
push();
|
||||||
|
|
||||||
|
//
|
||||||
|
translate(windowWidth - 50, windowHeight - 50, 0);
|
||||||
|
compass_north.heading = heading;
|
||||||
|
compass_north.draw();
|
||||||
|
|
||||||
|
//(pop)
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
1
js/Tone-14.8.36.min.js
vendored
Normal file
1
js/Tone-14.8.36.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
17
js/Tone.min.js
vendored
Normal file
17
js/Tone.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
117
js/classes.js
Normal file
117
js/classes.js
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
// sounds
|
||||||
|
class Sounder {
|
||||||
|
constructor(args) {
|
||||||
|
this.soundfile = args.soundfile;
|
||||||
|
this.gain = args.gain; // (dB)
|
||||||
|
this.latitude = args.location[0];
|
||||||
|
this.longitude = args.location[1];
|
||||||
|
this.spread = args.spread; // (deg)
|
||||||
|
this.distmap = args.distmap;
|
||||||
|
this.volmin = args.volmin; // (dB)
|
||||||
|
this.ramptime = args.ramptime; // (seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
//start sound playback
|
||||||
|
this.pv = new Tone.PanVol(0, -99).toDestination();
|
||||||
|
this.snd = await AudioImport(this.soundfile); // NOTE: url with spaces didn't work here.
|
||||||
|
// this.snd.connect(this.pv).start();
|
||||||
|
this.snd.connect(this.pv);
|
||||||
|
this.snd.loop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
//
|
||||||
|
//update sound pan/volume
|
||||||
|
//
|
||||||
|
// this.ang = getBearing(latitude, longitude, this.latitude, this.longitude); (deg)
|
||||||
|
this.ang = getBearing(latitude, longitude, this.latitude, this.longitude); // (deg)
|
||||||
|
this.angerr = (((this.ang - heading + 360) % 360) + 180) % 360 - 180;
|
||||||
|
this.dist = getDistance(latitude, longitude, this.latitude, this.longitude); // (km)
|
||||||
|
//
|
||||||
|
this.distvol = map(this.dist, this.distmap[0], this.distmap[1], this.distmap[2], this.distmap[3], true); //(dB)
|
||||||
|
|
||||||
|
// (from Pure Data patch "iamyou", [eqpan2~])
|
||||||
|
// arg #1 (inlet #3): width:
|
||||||
|
// -width*(1.5) ~ -width/2 -> left fade-in
|
||||||
|
// -width/2 ~ width/2 -> cross fading
|
||||||
|
// +width/2 ~ width*(1.5) -> right fade-out
|
||||||
|
|
||||||
|
let panleft_start = this.spread * (-1.5);
|
||||||
|
let panleft_end = this.spread * (-0.5);
|
||||||
|
let panright_start = this.spread * (0.5);
|
||||||
|
let panright_end = this.spread * (1.5);
|
||||||
|
|
||||||
|
//left fade-in
|
||||||
|
if (this.angerr > panleft_start && this.angerr < panleft_end) {
|
||||||
|
this.pv.pan.value = -1; //left-full
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain + map(this.angerr, panleft_start, panleft_end, this.volmin, 0), this.ramptime)//cross fading; //(dB)
|
||||||
|
} else if (this.angerr > panleft_end && this.angerr < panright_start) {
|
||||||
|
this.pv.pan.value = map(this.angerr, panleft_end, panright_start, -1, 1); // crossfade
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain, this.ramptime)//right fade-out; //(dB)
|
||||||
|
} else if (this.angerr > panright_start && this.angerr < panright_end) {
|
||||||
|
this.pv.pan.value = 1; //right-full
|
||||||
|
this.pv.volume.rampTo(this.distvol + this.gain + map(this.angerr, panright_start, panright_end, 0, this.volmin), this.ramptime)//slience; //(dB)
|
||||||
|
} else {
|
||||||
|
this.pv.volume.rampTo(-99, 10); //(dB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
//draw sound location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
class RadioSounder extends Sounder {
|
||||||
|
|
||||||
|
//do nothing
|
||||||
|
async load() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
link(audiotag) {
|
||||||
|
//start sound playback
|
||||||
|
this.pv = new Tone.PanVol(0, -99).toDestination();
|
||||||
|
let stream = Tone.context.createMediaElementSource(audiotag);
|
||||||
|
this.snd = Tone.context.createGain();
|
||||||
|
stream.connect(this.snd);
|
||||||
|
console.log(stream);
|
||||||
|
console.log(this.snd);
|
||||||
|
console.log(this.pv);
|
||||||
|
// this.snd.connect(this.pv.input.input); a hack ...
|
||||||
|
this.snd.gain.value = 1;
|
||||||
|
this.snd.connect(Tone.context.destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//p5
|
||||||
|
class Compass {
|
||||||
|
constructor(size, colors) {
|
||||||
|
this.size = size;
|
||||||
|
this.colors = colors;
|
||||||
|
this.heading = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
push();
|
||||||
|
//
|
||||||
|
scale(this.size);
|
||||||
|
//
|
||||||
|
translate(0.5, 0.5);
|
||||||
|
//
|
||||||
|
noStroke();
|
||||||
|
//
|
||||||
|
fill(this.colors[0]);
|
||||||
|
circle(0, 0, 0.9);
|
||||||
|
//
|
||||||
|
fill(this.colors[1]);
|
||||||
|
rotate(this.heading);
|
||||||
|
quad(0.1, 0.2, 0, -0.4, -0.1, 0.2, 0, 0.1);
|
||||||
|
//
|
||||||
|
fill(this.colors[2]);
|
||||||
|
circle(0, 0, 0.05);
|
||||||
|
//
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
4
js/hls.min.js
vendored
Normal file
4
js/hls.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
28
js/p5-v0.3.11.sound.min.js
vendored
Normal file
28
js/p5-v0.3.11.sound.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
js/p5-v1.1.9.min.js
vendored
Normal file
3
js/p5-v1.1.9.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/p5.min.js
vendored
Normal file
2
js/p5.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
js/socket-v2.3.0.io.slim.js
Normal file
9
js/socket-v2.3.0.io.slim.js
Normal file
File diff suppressed because one or more lines are too long
10204
js/standardized-audio-context.js
Normal file
10204
js/standardized-audio-context.js
Normal file
File diff suppressed because it is too large
Load diff
87
js/two-v0.8.1.min.js
vendored
Normal file
87
js/two-v0.8.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
100
js/util.js
Normal file
100
js/util.js
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
//gps, heading
|
||||||
|
//get permissions
|
||||||
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
||||||
|
function requestOrientationPermission() {
|
||||||
|
DeviceOrientationEvent
|
||||||
|
.requestPermission()
|
||||||
|
.then(response => {
|
||||||
|
if (response == 'granted') {
|
||||||
|
window.addEventListener("deviceorientation", (event) => {
|
||||||
|
if (event.webkitCompassHeading) {
|
||||||
|
heading = event.webkitCompassHeading;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.addEventListener("deviceorientationabsolute", (event) => {
|
||||||
|
heading = event.alpha * -1;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const watchID = navigator.geolocation.watchPosition(
|
||||||
|
(position) => {
|
||||||
|
latitude = position.coords.latitude;
|
||||||
|
longitude = position.coords.longitude;
|
||||||
|
},
|
||||||
|
(error) => {}, {
|
||||||
|
enableHighAccuracy: true,
|
||||||
|
maximumAge: 30000,
|
||||||
|
timeout: 27000,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//distance between 2 locations (lat/lon)
|
||||||
|
function getDistance(lat1, lon1, lat2, lon2) {
|
||||||
|
var R = 6371; // Radius of the earth in km
|
||||||
|
var dLat = deg2rad(lat2 - lat1); // deg2rad below
|
||||||
|
var dLon = deg2rad(lon2 - lon1);
|
||||||
|
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||||
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
var d = R * c; // Distance in km
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deg2rad(deg) {
|
||||||
|
return deg * (Math.PI / 180)
|
||||||
|
}
|
||||||
|
|
||||||
|
//bearing between 2 locations (lat/lon)
|
||||||
|
|
||||||
|
// Converts from degrees to radians.
|
||||||
|
function toRadians(degrees) {
|
||||||
|
return degrees * Math.PI / 180;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts from radians to degrees.
|
||||||
|
function toDegrees(radians) {
|
||||||
|
return radians * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const y = Math.sin(λ2-λ1) * Math.cos(φ2);
|
||||||
|
// const x = Math.cos(φ1)*Math.sin(φ2) -
|
||||||
|
// Math.sin(φ1)*Math.cos(φ2)*Math.cos(λ2-λ1);
|
||||||
|
// const θ = Math.atan2(y, x);
|
||||||
|
// const brng = (θ*180/Math.PI + 360) % 360; in degrees
|
||||||
|
|
||||||
|
function getBearing(startLat, startLng, destLat, destLng) {
|
||||||
|
startLat = toRadians(startLat);
|
||||||
|
startLng = toRadians(startLng);
|
||||||
|
destLat = toRadians(destLat);
|
||||||
|
destLng = toRadians(destLng);
|
||||||
|
|
||||||
|
y = Math.sin(destLng - startLng) * Math.cos(destLat);
|
||||||
|
x = Math.cos(startLat) * Math.sin(destLat) - Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
|
||||||
|
brng = Math.atan2(y, x);
|
||||||
|
brng = toDegrees(brng);
|
||||||
|
return (brng + 360) % 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
//promisify -> new Tone.Player
|
||||||
|
function AudioImport(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var audio = new Tone.Player(url, () => resolve(audio));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// some p5 tools
|
||||||
|
class Textline {
|
||||||
|
static #lines = 0;
|
||||||
|
static #linestep = 20;
|
||||||
|
static restart() {
|
||||||
|
Textline.#lines = 0;
|
||||||
|
}
|
||||||
|
static put(t) {
|
||||||
|
Textline.#lines = Textline.#lines + 1;
|
||||||
|
text(t, 0, Textline.#linestep * Textline.#lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue