Аудиореактивный визуальный эффект с использованием API веб-аудио

Я только начал изучать API веб-аудио и пытаюсь сделать визуальную синхронизацию со звуком. Когда есть увеличение громкости (бит?) в аудио, я хочу, чтобы на экране мигал белый цвет. Что я сделал до сих пор:

var mp3     = "08 - No More Sorrow.mp3";
var context = new AudioContext();
var request = new XMLHttpRequest();
request.open('GET', mp3, true);
request.responseType = "arraybuffer";
request.onload = function () {
    context.decodeAudioData(request.response, function (buffer) {
        var sourceBuffer = context.createBufferSource();
        sourceBuffer.buffer = buffer;
        sourceBuffer.connect(context.destination);
        sourceBuffer.start(context.currentTime);
    });
};
request.send();

... который просто воспроизводит звук с помощью API веб-аудио. Не знаю, что делать дальше. Я проверил обнаружение ритма с помощью JavaScript и веб-аудио. API и страницы Создание визуальных эффектов, реагирующих на аудио, но толком ничего не понял.

Если бы я хотел показать вам, что я хочу делать без использования Web Audio API, это было бы примерно так:

Array.prototype.pushIfExists = function(item) {
  if (item) {
    this.push(item);
  }
}

function random(min, max) {
  var min = min || 0;
  var max = max || 100;
  var num = Math.floor(Math.random() * max);
  while (num < min) {
    num = Math.floor(Math.random() * max);
  }
  return num;
}

function avarage(array) {
  var sum = 0;
  var avarage = 0;
  for (var i = 0; i < array.length; i++) {
    sum += array[i];
  }
  avarage = sum / array.length;
  return avarage;
}

var beats = [];
var delay = 500;
var delayIncrement = 200;
var threshold = 50;
var thresholdLimit = 100;
var beatAvarageRange = 5;
var flashDuration = 100;

for (var i = 0; i < 100; i++) {
  beats.push(random(0, thresholdLimit));
}

for (var i = 0; i < beats.length; i++) {
  (function(i) {
    setTimeout(function() {
      var recentBeats = [];
      for (var j = 1; j < beatAvarageRange + 1; j++) {
        recentBeats.pushIfExists(beats[i - j]);
      }
      threshold = avarage(recentBeats);
      if (beats[i] > threshold) {
        document.body.style.backgroundColor = "white";
        setTimeout(function() {
          document.body.style.backgroundColor = "black";
        }, flashDuration);
      }
    }, delay);
    delay += delayIncrement;
  })(i);
}
body {
  background-color: black;
}


person akinuri    schedule 06.10.2015    source источник
comment
Мне нравится видеть хорошие вопросы об API веб-аудио здесь, в SO, и я сделал несколько копий, чтобы дать вам некоторую (незначительную) помощь в получении ответа здесь. (И проголосовал.)   -  person sideshowbarker    schedule 07.10.2015
comment
@sideshowbarker Кажется, это одна из проблем, которые я решаю сам. Рад, что все обошлось, раз никто еще не ответил.   -  person akinuri    schedule 08.10.2015


Ответы (1)


Я еще немного покопался и нашел решение. Используя объяснение в Изучение веб-аудио HTML5: визуализация звука | Smartjava.org, я придумал следующее:

var volumeBars = {
    mono : document.getElementById("monoFill")
};

document.getElementById("open-file").onchange = function (evt) {
    var file = evt.target.files[0];
    var reader = new FileReader();
    reader.onload = function(e) {
        playSound(e.target.result);
    }
    reader.readAsArrayBuffer(file);
}

var context = new AudioContext();

function playSound(arraybuffer) {
    context.close();
    context = new AudioContext();

    var source = context.createBufferSource();
    context.decodeAudioData(arraybuffer, function (buffer) {
        source.buffer = buffer;
    });

    var analyser = context.createAnalyser();
    analyser.smoothingTimeConstant = 0.3;
    analyser.fftSize = 1024;

    jsNode = context.createScriptProcessor(2048, 1, 1);
    jsNode.onaudioprocess = function() {
        var array = new Uint8Array(analyser.frequencyBinCount);
        analyser.getByteFrequencyData(array);
        volumeBars.mono.style.height = Math.average(array) * 2 + "px";
        volumeBars.mono.innerHTML = Math.floor(Math.average(array));
    }

    source.connect(analyser);
    source.connect(context.destination);
    jsNode.connect(context.destination);
    analyser.connect(jsNode);

    source.start();
}

Math.average = function(arguments) {
    var numbers;
    if (arguments[0] instanceof Array) {
        numbers = arguments[0];
    }
    else if (typeof arguments[0] == "number") {
        numbers = arguments;
    }
    var sum = 0;
    var average = 0;
    for (var i = 0; i < numbers.length; i++) {
        sum += numbers[i];
    }
    average = sum / numbers.length;
    return average;
}
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body {
    background-color: gainsboro;
}
#container {
    height: 340px;
}
.bar {
    width: 50px;
    height: 100%;
    position: relative;
    float: left;
    margin: 20px;
    height: calc(100% - 40px);
}
.fill {
    background: LawnGreen;
    height: 20px;
    width: 100%;
    box-shadow: 0 0 3px rgba(0,0,0,.25),
        inset 1px 1px 1px rgba(255,255,255,.75),
        inset -1px -1px 1px rgba(0,0,0,.4);
    position: absolute;
    bottom: 0;
    padding: 5px;
    color: rgba(0,0,0,.75);
}
input {
    margin: 20px;
}
<div id="container">
	<div class="bar" id="mono">
		<div class="fill" id="monoFill"></div>
	</div>
</div>

<input type="file" id="open-file" accept="audio/*" />

API веб-аудио — Измеритель громкости — JSFiddle

Это не окончательная визуализация, которую я пытаюсь сделать, но сначала создание измерителя громкости показалось лучшей идеей для понимания того, как работает API веб-аудио.

person akinuri    schedule 08.10.2015