htmlaudiothree.js

Load different audio in my audio visualiser project


I have an audio visualiser coded using three.js and shaders. My problem is that I can only use it on my device as the audio source is on my computer. Can someone help me change the code so that the audio loaded is chose by the user, on their own device?

Here is the project on Github: https://github.com/AlfieHarris10/audiovisualiserwithshaders

(I'm relatively new to coding so if I haven't explained this very well, please feel free to ask questions).

Thanks.

const listener = new AudioListener();
camera.add( listener );

// create an Audio source
const sound = new Audio( listener );

let button = document.querySelector('.button');
button.innerHTML = "Loading Audio..."

// load a sound and set it as the Audio object's buffer
const audioLoader = new AudioLoader();
audioLoader.load( 'audio file.mp3', function( buffer ) {
    sound.setBuffer( buffer );
    sound.setLoop(true);
    sound.setVolume(0.5);
  button.innerHTML = "Play Audio"
  button.addEventListener('pointerdown', () => {
    sound.play();
    button.style.display = 'none';
  }, false);
});

Solution

  • This has nothing (almost) to do with JS. You must give the potential user the opportunity to upload the file via html input. I made some adjustments to the code. Generally, consider removing OrbitControls as you don't need it in your scenario.

    body { 
      margin: 0; 
      overflow: hidden; 
    }
    .button { 
      position: absolute; 
      top: 20px; 
      left: 20px; 
      padding: 10px 20px; 
      background: #444; 
      color: #fff; 
      border: none; 
      border-radius: 5px; 
      cursor: pointer; 
    }
    <input type="file" id="song" accept="audio/*" style="position: absolute; top: 20px; left: 150px; padding: 10px;">
    <button class="button">PLAY/STOP</button>
    
    <script type="importmap">
        {
            "imports": {
                "three": "https://unpkg.com/three@0.167.0/build/three.module.js",
                "three/examples/jsm/": "https://unpkg.com/three@0.167.0/examples/jsm/",
                "shader-park-core": "https://unpkg.com/shader-park-core/dist/shader-park-core.esm.js"
            }
        }
        </script>
    
    <script type="module">
    import {
        Scene,
        PerspectiveCamera,
        WebGLRenderer,
        Color,
        Clock,
        AudioListener,
        Audio,
        AudioLoader,
        AudioAnalyser,
        Vector3,
        SphereGeometry
    } from 'three';
    import { createSculptureWithGeometry } from 'shader-park-core';
    
    const scene = new Scene();
    const camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 1.5;
    
    const renderer = new WebGLRenderer({ antialias: true, transparent: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setClearColor(new Color(1, 1, 1), 0);
    document.body.appendChild(renderer.domElement);
    
    const clock = new Clock();
    const listener = new AudioListener();
    camera.add(listener);
    
    const sound = new Audio(listener);
    let analyser = new AudioAnalyser(sound, 32);
    
    const state = {
        mouse: new Vector3(),
        currMouse: new Vector3(),
        pointerDown: 0.0,
        currPointerDown: 0.0,
        audio: 0.0,
        currAudio: 0.0,
        time: 0.0
    };
    
    window.addEventListener('pointermove', (event) => {
        state.currMouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        state.currMouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
    }, false);
    
    window.addEventListener('pointerdown', () => state.currPointerDown = 1.0, false);
    window.addEventListener('pointerup', () => state.currPointerDown = 0.0, false);
    
    const geometry = new SphereGeometry(2, 45, 45);
    
    const mesh = createSculptureWithGeometry(geometry, spCode(), () => ({
        time: state.time,
        pointerDown: state.pointerDown,
        audio: state.audio,
        mouse: state.mouse,
        _scale: .5
    }));
    
    scene.add(mesh);
    
    const onWindowResize = () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    };
    
    window.addEventListener('resize', onWindowResize);
    
    const fileInput = document.getElementById('song');
    const button = document.querySelector('.button');
    
    const audioLoader = new AudioLoader();
    
    fileInput.addEventListener('change', (event) => {
        const file = event.target.files[0];
        if (file) {
            const url = URL.createObjectURL(file);
            audioLoader.load(url, (buffer) => {
                sound.setBuffer(buffer);
                sound.setLoop(true);
                sound.setVolume(0.5);
                sound.play();
                analyser = new AudioAnalyser(sound, 32);
            });
        }
    });
    
    let isPlaying = false;
    
    button.addEventListener('click', () => {
        if (isPlaying) {
            sound.stop();
        } else {
            sound.play();
        }
        isPlaying = !isPlaying;
    });
    
    const render = () => {
        requestAnimationFrame(render);
        state.time += clock.getDelta();
        if (analyser) {
            state.currAudio += Math.pow((analyser.getFrequencyData()[2] / 255) * .81, 8) + clock.getDelta() * .5;
            state.audio = .2 * state.currAudio + .8 * state.audio;
        }
        state.pointerDown = .1 * state.currPointerDown + .9 * state.pointerDown;
        state.mouse.lerp(state.currMouse, .05);
        renderer.render(scene, camera);
    };
    
    render();
    
    function spCode() {
        return `
    let audio = input();
    let pointerDown = input();
    
    setMaxIterations(5);
    let s = getSpace();
    let r = getRayDirection();
    
    let n1 = noise(r * 4 + vec3(0, audio, vec3(0, audio, audio)) * .5);
    let n = noise(s + vec3(0, 0, audio + time * .1) + n1);
    
    metal(n * .5 + .5);
    shine(n * .5 + .5);
    
    color(normal * .1 + vec3(0.3, 0.8, 1));
    displace(mouse.x * 2, mouse.y * 2, 0);
    boxFrame(vec3(2), abs(n) * .1 + .04);
    mixGeo(pointerDown);
    sphere(n * .5 + .8);
    `;
    }
    
    </script>

    UPDATE

    You cannot set maxZoom on this camera, because it is a property reserved for OrthographicCamera. What you probably mean is Dolly, and the property defining it is maxDistance.

    const controls = new OrbitControls(camera, renderer.domElement);
          controls.enableDamping = true;
          controls.dampingFactor = 0.1;
          controls.enableZoom = true;
          controls.enableRotate = true;
          controls.enablePan = true;
    
          // Set dolly
          controls.minDistance = 5;
          controls.maxDistance = 50;
    

    Here is a very good illustration of the difference between Dolly and Zoom. This is from the CameraControls documentation.

    Dolly/Zoom