Skip to content

Commit 1ead2d9

Browse files
committed
Web audio spatialization
1 parent 9597ffa commit 1ead2d9

6 files changed

Lines changed: 151 additions & 2 deletions

File tree

demos/goat.mp3

36.5 KB
Binary file not shown.

demos/missing-goat.html

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<script src="https://cesium.com/downloads/cesiumjs/releases/1.107.1/Build/Cesium/Cesium.js"></script>
6+
<link
7+
href="https://cesium.com/downloads/cesiumjs/releases/1.107.1/Build/Cesium/Widgets/widgets.css"
8+
rel="stylesheet"
9+
/>
10+
<style>
11+
html,
12+
body,
13+
#cesiumContainer {
14+
height: 100%;
15+
margin: 0;
16+
}
17+
#play {
18+
position: absolute;
19+
right: 30px;
20+
top: 30px;
21+
font-family: monospace;
22+
font-weight: bold;
23+
font-size: 1.6em;
24+
}
25+
</style>
26+
</head>
27+
28+
<body>
29+
<audio src="goat.mp3"></audio>
30+
31+
<div id="cesiumContainer"></div>
32+
<button id="play" type="button">play</button>
33+
<script type="module">
34+
import { createViewer } from "./setup.js";
35+
import CesiumSphereCamera from "../packages/cesium-sphere-camera/cesium-sphere-camera.js";
36+
import CesiumBinoculars from "../packages/cesium-binoculars/cesium-binoculars.js";
37+
import CesiumAudio from "../packages/cesium-audio/cesium-audio.js";
38+
39+
Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjNmIwZTMxMy0zYzQ2LTQzMDMtOWIyMC0wZTkzNWQwNzAyOTgiLCJpZCI6MjE3NTQsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODA0ODE5NTF9.Cggz223KUrmnC7VoflmC3u2CMcSUKZobn-8NXOnQEvc";
40+
41+
createViewer("cesiumContainer").then(async (viewer) => {
42+
43+
const sphereMode = new CesiumSphereCamera(viewer);
44+
const binocularsMode = new CesiumBinoculars(viewer);
45+
sphereMode.active = true;
46+
binocularsMode.active = true;
47+
48+
// goat position
49+
const position = new Cesium.Cartesian3.fromDegrees(7.897325, 46.690576, 1000);
50+
51+
viewer.entities.add({
52+
position: position,
53+
model: {
54+
uri: await Cesium.IonResource.fromAssetId(2048297),
55+
},
56+
});
57+
58+
let init = false;
59+
let audioElement;
60+
let listener;
61+
document.querySelector("#play").addEventListener("click", () => {
62+
if (!init) {
63+
const audioCtx = new AudioContext();
64+
audioElement = document.querySelector("audio");
65+
const track = audioCtx.createMediaElementSource(audioElement);
66+
const panner = audioCtx.createStereoPanner();
67+
68+
track.connect(panner).connect(audioCtx.destination);
69+
70+
const cesiumAudio = new CesiumAudio(viewer, position, panner);
71+
init = true;
72+
}
73+
audioElement.play();
74+
});
75+
});
76+
</script>
77+
</body>
78+
</html>

demos/setup.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export async function createViewer(container) {
1717
homeButton: false,
1818
fullscreenButton: false,
1919
scene3DOnly: true,
20+
requestRenderMode: true,
2021
baseLayer: new Cesium.ImageryLayer(
2122
new Cesium.UrlTemplateImageryProvider({
2223
url: "https://wmts.geo.admin.ch/1.0.0/ch.swisstopo.swissimage/default/current/3857/{z}/{x}/{y}.jpeg",
@@ -37,9 +38,9 @@ export async function createViewer(container) {
3738
viewer.scene.fog.minimumBrightness = 0.03 * 10;
3839

3940
viewer.camera.flyTo({
40-
destination: Cesium.Cartesian3.fromDegrees(7.863775, 46.686447, 1200),
41+
destination: Cesium.Cartesian3.fromDegrees(7.863775, 46.686447, 1000),
4142
orientation: {
42-
heading: Cesium.Math.toRadians(25.0),
43+
heading: Math.PI,
4344
pitch: 0.0,
4445
},
4546
duration: 0,

packages/cesium-audio/LICENSE

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2020-present, camptocamp SA
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
1. Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
2. Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
3. Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Cartesian2, Cartesian3} from "cesium";
2+
3+
export default class CesiumSound {
4+
constructor(viewer, position, panner) {
5+
this.scene = viewer.scene;
6+
this.position = position;
7+
this.panner = panner;
8+
9+
this.setPan();
10+
11+
viewer.scene.postRender.addEventListener(() => this.setPan());
12+
}
13+
14+
setPan() {
15+
const camera = this.scene.camera;
16+
const cameraDirection = Cartesian2.fromCartesian3(camera.direction);
17+
// vector from camera to sound
18+
const toPosition = Cartesian2.fromCartesian3(Cartesian3.subtract(this.position, camera.position, new Cartesian3()));
19+
20+
// angle between camera direction and sound
21+
const angle = Cartesian2.angleBetween(cameraDirection, toPosition);
22+
const rightSide = Cartesian2.cross(cameraDirection, toPosition) > 0;
23+
24+
this.panner.pan.value = rightSide ? -Math.sin(angle) : Math.sin(angle);
25+
console.log(angle, this.panner.pan.value);
26+
}
27+
}

packages/cesium-audio/package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "@geoblocks/cesium-audio",
3+
"version": "0.0.0",
4+
"license": "BSD-3-Clause",
5+
"repository": "github:geoblocks/cesium-helpers",
6+
"main": "cesium-audio.js",
7+
"module": "cesium-audio.js",
8+
"publishConfig": {
9+
"access": "public"
10+
},
11+
"peerDependencies": {
12+
"cesium": "1.x"
13+
}
14+
}

0 commit comments

Comments
 (0)