heading-ears/js/classes.js
2024-10-15 00:26:12 +09:00

163 lines
4.2 KiB
JavaScript

// sounds
class Sounder {
constructor(args) {
this.name = args.name;
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)
this.zone = 0;
}
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);
this.snd.start();
this.snd.loop = true;
}
update_location() {
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)
}
update_panvol() {
// console.log('pv');
//
//update sound pan/volume
//
// (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
//if close enough. don't do pan/vol, but just playback with full volume.
this.zone = 0;
if (this.dist < 0.07) {
this.zone = 1;
this.pv.pan.rampTo(0, this.ramptime);
this.pv.volume.rampTo(this.gain, this.ramptime); //(dB)
return;
}
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);
//pan & volume
if (this.angerr > panleft_start && this.angerr < panleft_end) {
//left fade-in
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)
} else if (this.angerr > panleft_end && this.angerr < panright_start) {
//cross fading; //(dB)
this.pv.pan.value = map(this.angerr, panleft_end, panright_start, -1, 1);
this.pv.volume.rampTo(this.distvol + this.gain, this.ramptime); //(dB)
} else if (this.angerr > panright_start && this.angerr < panright_end) {
//right fade-out
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)
} else {
//slience
this.pv.volume.rampTo(-99, 10); //(dB)
}
}
draw() {
//draw sound location
}
static allStart(array) {
array.forEach(item => {
if (item.snd.state != 'started') item.snd.start();
});
}
static allStop(array) {
array.forEach(item => {
if (item.snd.state != 'stopped') item.snd.stop();
});
}
static allSilent(array) {
array.forEach(item => {
item.pv.volume.rampTo(-99, item.ramptime);
});
}
}
//
class RadioSounder extends Sounder {
constructor(args) {
super(args);
}
//do nothing
async load() {
;
}
link(audiotag) {
//start sound playback
this.pv = new Tone.PanVol(0, -99).toDestination();
//native webaudio
this.source = Tone.context.createMediaElementSource(audiotag);
this.snd = undefined;
this.gainnode = Tone.context.createGain();
this.gainnode.gain.value = 1; // 1 == max. volume
//a hacky native-to-Tonejs connection(?!)
this.source.connect(this.gainnode).connect(this.pv.input.input);
}
update() {
super.update();
// console.log('hi!');
}
}
//p5
class Compass {
constructor(size, colors) {
this.size = size;
this.colors = colors;
this.pointer = 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.pointer);
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();
}
}