如何将高音频与 CSS 变化同步?

How to synchronize high audio frequencies with CSS changes?

我正在创建一个与音乐互动的程序,我试图将鼓点或低音与 CSS 同步,因此在每个高频声音中,背景都会及时改变.

我的第一个想法是创建一个音频频谱,以了解它如何与播放的声音相互作用。在这种情况下,我使用 Web Audio API analyserNode.fftSize 获取音频文件中的所有频率点并使用 <canvas>

创建频谱
const audioCtx = new AudioContext();

    //Create audio source
    //Here, we use an audio file, but this could also be e.g. microphone input
    const audioEle = new Audio();
    audioEle.src = "good-day.mp3"; //insert file name here
    audioEle.autoplay = true;
    audioEle.preload = "auto";
    const audioSourceNode = audioCtx.createMediaElementSource(audioEle);

    //Create analyser node
    const analyserNode = audioCtx.createAnalyser();
    analyserNode.fftSize = 256;
    const bufferLength = analyserNode.frequencyBinCount;
    const dataArray = new Float32Array(bufferLength); // this is where all the frequencies are stored

然后,使用这个 dataArray 和那个时刻的所有频率,我 运行 一个条件说: 如果频率高于 X,将背景更改为蓝色:

      if (Math.max(...dataArray) + 100 > 70) { // +- 70 is a good number for that specific song
        await changeBgColor();
      }

最终结果似乎有效,但并不完美,因为 if 条件每秒 运行s 60 次,有时由于声音或音频文件中的另一个声音干扰了低音的频率。

我不知道是否有什么东西真的可以对此有用。我尝试使用 ToneJS and wadJS 但我无法使用这些库。

我想知道用声音进行这些 CSS 迭代的最佳方法是什么。

用于测试此代码的歌曲是:Ice Cube - It Was A Good Day (good-day.mp3)

完整代码:

index.html

<!DOCTYPE html>
<body id="bd">
  <script>
    const audioCtx = new AudioContext();

    //Create audio source
    //Here, we use an audio file, but this could also be e.g. microphone input
    const audioEle = new Audio();
    audioEle.src = "good-day.mp3"; //insert file name here
    audioEle.autoplay = true;
    audioEle.preload = "auto";
    const audioSourceNode = audioCtx.createMediaElementSource(audioEle);

    //Create analyser node
    const analyserNode = audioCtx.createAnalyser();
    analyserNode.fftSize = 256;
    const bufferLength = analyserNode.frequencyBinCount;
    const dataArray = new Float32Array(bufferLength);

    //Set up audio node network
    audioSourceNode.connect(analyserNode);
    analyserNode.connect(audioCtx.destination);

    //Create 2D canvas
    const canvas = document.createElement("canvas");
    canvas.style.position = "absolute";
    canvas.style.top = 0;
    canvas.style.left = 0;
    canvas.width = window.innerWidth;
    canvas.height = 300;
    document.body.appendChild(canvas);
    const canvasCtx = canvas.getContext("2d");
    canvasCtx.clearRect(0, 0, canvas.width, canvas.height);

    async function draw() {
      const changeBgColor = () => {
        document.getElementById("bd").style["background-color"] = "blue";
      };

      const sleep = () => {
        // setTimeout(() => null, 1000)
        console.log("bass sound");
      };

      //Schedule next redraw
      requestAnimationFrame(draw);

      //Get spectrum data
      analyserNode.getFloatFrequencyData(dataArray);

      //Draw black background
      canvasCtx.fillStyle = "rgb(0, 0, 0)";
      canvasCtx.fillRect(0, 0, canvas.width, canvas.height);

      //Draw spectrum
      const barWidth = (canvas.width / bufferLength) * 2.5;
      let posX = 0;
      document.getElementById("bd").style["background-color"] = "red";

      if (Math.max(...dataArray) + 100 > 70) { // +- 70 is a good number for that specific song
        await changeBgColor();
      }

      for (let i = 0; i < bufferLength; i++) {
        const barHeight = (dataArray[i] + 100) * 2;
        //   console.log(barHeight)

        canvasCtx.fillStyle = `rgb(${Math.floor(
          barHeight + 100
        )}, 255, ${Math.floor(barHeight + 200)})`;

        canvasCtx.fillRect(
          posX,
          canvas.height - barHeight / 2,
          barWidth,
          barHeight / 2
        );
        posX += barWidth + 1;
      }
    }

    draw();
  </script>
</body>

我对这段代码的引用是 MDN 中关于 AnalyserNode

的文档

这里是关于如何将音乐频率检索到数组并将其传递给 CSS 的快速演示。

这是非常基础的,有很多问题,但也许是进一步制作适当动画的良好开端。

const audio = document.getElementById('music');
audio.load();
audio.play();

const ctx = new AudioContext();
const audioSrc = ctx.createMediaElementSource(audio);
const analyser = ctx.createAnalyser();

audioSrc.connect(analyser);
analyser.connect(ctx.destination);

analyser.fftSize = 256;
const bufferLength = analyser.frequencyBinCount;
const frequencyData = new Uint8Array(bufferLength);

setInterval(async () => {
  analyser.getByteFrequencyData(frequencyData);
  let dataArray = Object.values(frequencyData);
  //if (Math.max(...dataArray) + 100 > 70) { // +- 70 is a good number for that specific song
    window.requestAnimationFrame(
      await changeBgColor(dataArray)
    )
  //}
}, 100);

const changeBgColor = (dataArray) => {
  console.log(dataArray)
  let color1 = dataArray.slice(0,3).map((a) => a / 100) // Smaller numbers are darker
  let color2 = dataArray.slice(3,6)
  let color3 = dataArray.slice(6,9)
  document.body.style.background = `linear-gradient(90deg, rgba(${color1}, 1) 0%, rgba(${color2},0.2) 35%, rgba(${color3},1) 100%)`
}
<audio id="music" src="https://cdn.glitch.com/02dcea11-9bd2-4462-ac38-eeb6a5ad9530%2F331_full_beautiful-minds_0171_preview.mp3?1522829295082" crossorigin="use-URL-credentials" controls="true"></audio>
<hr>
<span id="console"></span>