// 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(); } }