I am attempting to implement <audio>
into a THREE.js scene and I am having some trouble with the audio playback. In my array that I am logging out to the console
, All of the output is "0". I have accounted for the CORS issue by placing this: audio.crossOrigin = "anonymous"
after the audio.play();
initialization, but to no avail has this helped. Any ideas would be appreciated.
Oh, I have also tested this with a server, so that's not the issue.
Here is a link to the repo.
Here is the jsbin.
Below is the code.
HTML
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Threejs Experiment</title>
<link rel="stylesheet" href="style.css" media="screen" title="no title" charset="utf-8">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://dl.dropboxusercontent.com/u/3587259/Code/Threejs/OrbitControls.js"></script>
</head>
<body>
<audio id="song" src="https://soundcloud.com/madeon/madeon-cut-the-kid"></audio>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
</body>
</html>
JavaScript
$(function(){
// Add Audio context and Audio
var ctx = new AudioContext();
var audio = document.getElementById('song');
var audioSrc = ctx.createMediaElementSource(audio);
var analyser = ctx.createAnalyser();
audioSrc.connect(analyser);
audioSrc.connect(ctx.destination);
var frequencyData = new Uint8Array(analyser.frequencyBinCount);
// Set scene and camera
var scene = new THREE.Scene();
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 100);
camera.target = new THREE.Vector3( 10, 10, 10 );
// Set the DOM
var renderer = new THREE.WebGLRenderer({ antialias:true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor("#000000");
document.body.appendChild(renderer.domElement);
// Add controls
// controls = new THREE.OrbitControls(camera, renderer );
// controls.addEventListener( 'change', render );
// Move the camera
camera.position.z = 10;
camera.position.y = 0;
// Point Lights
var pointLightBlue = new THREE.PointLight( "#00ccff", 5, 100, 2 );
pointLightBlue.position.set( -10, -40, -10 );
scene.add(pointLightBlue);
// var pointLightBlue = new THREE.PointLight( "#ffffff", 1, 0, 1 );
// pointLightBlue.position.set( -10, 20, -10 );
// scene.add(pointLightBlue);
// var pointLightPink = new THREE.PointLight( "#EE567C", 5, 100, 10 );
// pointLightPink.position.set( 1, 0, -5 );
// scene.add(pointLightPink);
var pointLight = new THREE.PointLight( "#A805FA", 100, 1000, 40 );
pointLight.position.set( 40, 0, 40 );
scene.add(pointLight);
var sphereSize = 5;
var pointLightHelper = new THREE.PointLightHelper( pointLight, sphereSize );
scene.add( pointLightHelper );
var pointLight2 = new THREE.PointLight( "#07FAA0", 100, 1000, 30 );
pointLight2.position.set( -40, 0, -40 );
scene.add(pointLight2);
var sphereSize = 5;
var pointLightHelper = new THREE.PointLightHelper( pointLight2, sphereSize );
scene.add( pointLightHelper );
var quantity = 50;
var shapes = [];
for (var i = 0; i < quantity; i++) {
if(Math.random() < 0.5){
var geometry = new THREE.RingGeometry( 50, 50, 18);
// var geometries = [
// new THREE.IcosahedronGeometry( 20, 0 ),
// new THREE.OctahedronGeometry( 20, 0 ),
// new THREE.TetrahedronGeometry( 20, 0 ),
// ];
//
// var geometry = geometries[ Math.floor( Math.random() * geometries.length ) ];
// var material = new THREE.MeshLambertMaterial( {
// color: new THREE.Color( Math.random(), Math.random() * 0.5, Math.random() ),
// blending: THREE.AdditiveBlending,
// depthTest: false,
// shading: THREE.FlatShading,
// transparent: true
// } );
// var mesh = new THREE.Mesh( geometry, material );
// var wireframe = mesh.clone();
// wireframe.material = wireframe.material.clone();
// wireframe.material.wireframe = true;
// mesh.add( wireframe );
// scene.add(mesh);
// var geometry = new THREE.RingGeometry( 20, 150, 18);
//var geometry = new THREE.TorusKnotGeometry( 10, 3, 100, 16 );
}
else {
// var geometry = new THREE.RingGeometry( 4, 40, 3);
// var geometry = new THREE.RingGeometry( 1, 5, 6 );
// var material = new THREE.MeshBasicMaterial( { color: 0xffff00,
// side: THREE.DoubleSide } );
// var mesh = new THREE.Mesh( geometry, material );
// scene.add( mesh );
// var points = [];
// for ( var j = 0; j < 10; j++ ) {
// points.push( new THREE.Vector3( Math.sin( j * 0.2 ) * 15 + 50, 0, ( j - 5 ) * 2 ) );
//
// }
// var geometry = new THREE.LatheGeometry( points );
// var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
// var lathe = new THREE.Mesh( geometry, material );
// scene.add( lathe );
}
if(i % 7 === 0) {
var material = new THREE.MeshPhongMaterial( { color: "#ffffff"} );
}
else if(i % 2 === 0){
var material = new THREE.MeshPhongMaterial( { color: "#666666"} );
}
else {
var material = new THREE.MeshPhongMaterial( { color: "#333333"} );
}
var mesh = new THREE.Mesh(geometry, material);
mesh.position.z = -i * 2;
mesh.rotation.z = i;
shapes.push(mesh);
scene.add(mesh);
}
// Variables
var u_time = 0;
// Render function
var render = function() {
requestAnimationFrame(render);
u_time++;
for (var i = 0; i < quantity; i++) {
// Set rotation change of shapes
shapes[i].position.z += 0.2;
shapes[i].rotation.z += 0;
shapes[i].scale.x = 1 + Math.sin(i + u_time * 0.1) * 0.05;
shapes[i].scale.y = 1 + Math.sin(i + u_time * 0.1) * 0.05;
var change = 1.5 + Math.sin(u_time * 0.5) * 0.5;
// Set wireframe & width
if(Math.random() < change){
shapes[i].material.wireframe = true;
shapes[i].material.wireframeLinewidth = Math.random() * 2;
}
else {
shapes[i].material.wireframe = false;
}
if(shapes[i].position.z > 10){
shapes[i].position.z = -70;
shapes[i].rotation.z = i;
}
}
// Set Point light Intensity & Position
pointLight.intensity = Math.abs(Math.sin(u_time * 0.2) * 2);
pointLight2.intensity = Math.abs(Math.cos(u_time * 0.2) * 2);
pointLight.position.z = Math.abs(Math.sin(u_time * 0.02) * 30);
pointLight2.position.z = Math.abs(Math.cos(u_time * 0.02) * 30);
// camera.rotation.y = 90 * Math.PI / 180;
// camera.rotation.z = frequencyData[20] * Math.PI / 180;
// camera.rotation.x = frequencyData[100] * Math.PI / 180;
console.log(frequencyData);
//composer.render();
renderer.render(scene, camera);
}
audio.play();
// audio.crossOrigin = "anonymous";
render();
});
What do you mean "CORS isn't an issue"?
if the audio is coming from a different origin you need to set the crossOrigin
property BEFORE setting the src property
someAudioElement.crossOrigin = "anonymous";
In other words either put the crossOrigin property in the tag itself
<audio crossOrigin="anonymous" src="http://somedomain/someaudio">...
Or in JavaScript
someAudioElement.crossOrigin = "anonymous";
someAudioElement.src = "http://somedomain/someaudio";
The reason you have to do this before is because the moment you set the src
property the browser will try to get the audio. If you haven't already set the crossOrigin
property the browser won't know to ask for permission to use the audio.
Note that all that does is ask the server for permission. The server itself still has to give permission to the browser to use the audio. Most servers do not give this permission. Soundcloud does but not for all songs.
Safari on iOS and Chrome on Android don't support analysing streaming audio data.
You can track the Chrome issue to fix this here. Star it if you want to track the progress. I don't know if there is a webkit bug to track the issue on iOS
AFAIK you can't play soundcloud URLs directly
You have code like this
<audio id="song" src="https://soundcloud.com/madeon/madeon-cut-the-kid"></audio>
That is not a valid URL for soundcloud audio. You have to pass that URL to the soundcloud API along with an API key and it will give you back a correct URL.
Here's an SO question about it
Other things to note:
The soundcloud SDK 3.0 SDK doesn't work on Safari for analysing audio. Use the soundcloud SDK 2.0 or better, use a direct XMLHttpRequest because the soundcloud SDK 2.0 initializes flash even if you don't use it.
Example using the soundcloud sdk 2.0
in HTML
<script src="https://connect.soundcloud.com/sdk-2.0.0.js"></script>
in Javascript
// see https://auth0.com/docs/connections/social/soundcloud
var yourSoundCloudClientId = "???????";
SC.initialize({
client_id: yoursCoundCloudClientId,
});
var soundCloudURL = "https://soundcloud.com/madeon/madeon-cut-the-kid";
SC.get("/resolve", { url: soundCloudUrl }, function(result, err) {
if (err) {
console.error("bad url:", url, err);
return;
}
if (result.streamable && result.stream_url) {
var src = result.stream_url + '?client_id=' + yourSoundCloudClientId;
someAudioElement.crossOrigin = "anonymous";
someAudioElement.src = src;
// other things you might be interested in because
// if you use soundcloud music you're required to give
// and display attribution and links
console.log("link to music:", result.permalink_url);
console.log("link to band:", result.user.permalink_url);
console.log("name of song:", result.title);
console.log("name to band:", result.user.username);
} else {
console.error("not streamable:", url);
}
});
This example does it without the soundcloud SDK.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With