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