批处理之家's Archiver

aa77dd@163.com 发表于 2016-10-22 22:06

音频可视化效果

[img]https://ooo.0o0.ooo/2016/10/22/580b5f50212f7.gif[/img]

转自: [url=http://codepen.io/raurir/pen/ZQVrzg]http://codepen.io/raurir/pen/ZQVrzg[/url]

演示地址:

[url=https://a7d.000webhostapp.com/audio-visualiser/index_s_dread_rock.html]https://a7d.000webhostapp.com/audio-visualiser/index_s_dread_rock.html[/url][code]<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
    <title>Audio Visualiser</title>

    <style>
    body {
        background-color: #000;
    }
    canvas {
        margin: 50px auto;
        display: block;
    }
    </style>

  </head>

  <body>
  </body>
</html>

<script>
(function() {
  var Alien, Aliencon, Analyse, Music, con,
    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  Alien = (function() {
    Alien.prototype.grid = null;

    Alien.prototype.cellSize = 30;

    Alien.prototype.numberOfRows = 10;

    Alien.prototype.numberOfColumns = 10;

    Alien.prototype.halfColumns = 0;

    Alien.prototype.colReflected = 0;

    Alien.prototype.ticks = 0;

    Alien.prototype.canvas = null;

    Alien.prototype.drawingContext = null;

    Alien.prototype.canvasWidth = 0;

    Alien.prototype.canvasHeight = 0;

    function Alien() {
      this.step = bind(this.step, this);
      this.getAlien = bind(this.getAlien, this);
      this.halfColumns = this.numberOfColumns / 2;
      this.canvasWidth = this.cellSize * (this.numberOfColumns + 2);
      this.canvasHeight = this.cellSize * (this.numberOfRows + 2);
      this.canvas = document.createElement('canvas');
      this.drawingContext = this.canvas.getContext('2d');
      this.canvas.height = this.canvasWidth;
      this.canvas.width = this.canvasHeight;
      this.canvas.aliens = {
        x: 0,
        y: 0
      };
    }

    Alien.prototype.clearCanvas = function() {
      return this.drawingContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
    };

    Alien.prototype.seed = function() {
      var column, k, ref, results, row, seedCell;
      this.grid = [];
      results = [];
      for (row = k = 0, ref = this.numberOfRows; 0 <= ref ? k < ref : k > ref; row = 0 <= ref ? ++k : --k) {
        this.grid[row] = [];
        results.push((function() {
          var n, ref1, results1;
          results1 = [];
          for (column = n = 0, ref1 = this.halfColumns; 0 <= ref1 ? n < ref1 : n > ref1; column = 0 <= ref1 ? ++n : --n) {
            seedCell = this.createSeedCell(column);
            results1.push(this.grid[row][column] = seedCell);
          }
          return results1;
        }).call(this));
      }
      return results;
    };

    Alien.prototype.createSeedCell = function(probability) {
      var chance, cutoff, tobe;
      chance = Math.random();
      cutoff = (probability + 1) / (this.halfColumns + 1);
      tobe = chance < cutoff;
      return tobe;
    };

    Alien.prototype.generateAlien = function() {
      var q, r;
      r = (function(_this) {
        return function(m) {
          return ~~(Math.random() * m + 1);
        };
      })(this);
      q = (function(_this) {
        return function() {
          var a, g, i, j, l;
          l = r(11);
          g = r(5);
          a = [];
          i = 0;
          while (i < l) {
            j = i * g;
            a.push(j);
            if (i) {
              a.unshift(-j);
            }
            i++;
          }
          return a;
        };
      })(this);
      return {
        x: q(),
        y: q()
      };
    };

    Alien.prototype.drawGrid = function(pixels) {
      var b, b1, colourFill, colourLine, column, g, g1, k, n, o, r, r1, ref, ref1, ref2, results, row;
      this.canvas.aliens = this.generateAlien();
      r = ~~(Math.random() * 128 + 64);
      g = ~~(Math.random() * 128 + 64);
      b = ~~(Math.random() * 128 + 64);
      colourLine = "rgba(" + r + ", " + g + ", " + b + ", 1)";
      r1 = ~~(r - Math.random() * 64);
      g1 = ~~(g - Math.random() * 64);
      b1 = ~~(b - Math.random() * 64);
      colourFill = "rgb(" + r1 + ", " + g1 + ", " + b1 + ")";
      for (row = k = 0, ref = this.numberOfRows; 0 <= ref ? k < ref : k > ref; row = 0 <= ref ? ++k : --k) {
        for (column = n = 0, ref1 = this.numberOfColumns; 0 <= ref1 ? n < ref1 : n > ref1; column = 0 <= ref1 ? ++n : --n) {
          this.drawCell(row, column, colourLine, 10);
        }
      }
      results = [];
      for (row = o = 0, ref2 = this.numberOfRows; 0 <= ref2 ? o < ref2 : o > ref2; row = 0 <= ref2 ? ++o : --o) {
        results.push((function() {
          var p, ref3, results1;
          results1 = [];
          for (column = p = 0, ref3 = this.numberOfColumns; 0 <= ref3 ? p < ref3 : p > ref3; column = 0 <= ref3 ? ++p : --p) {
            results1.push(this.drawCell(row, column, colourFill, 0));
          }
          return results1;
        }).call(this));
      }
      return results;
    };

    Alien.prototype.drawCell = function(y, x, fillStyle, strokeWidth) {
      var colReflected, isOn;
      if (x >= this.halfColumns) {
        colReflected = this.numberOfColumns - x - 1;
      } else {
        colReflected = x;
      }
      isOn = this.grid[y][colReflected];
      if (isOn) {
        this.drawingContext.fillStyle = fillStyle;
        return this.drawingContext.fillRect((1 + x) * this.cellSize - strokeWidth, (1 + y) * this.cellSize - strokeWidth, this.cellSize + strokeWidth * 2, this.cellSize + strokeWidth * 2);
      }
    };

    Alien.prototype.getAlien = function() {
      return this.canvas;
    };

    Alien.prototype.step = function() {};

    Alien.prototype.change = function() {
      this.clearCanvas();
      this.seed();
      return this.drawGrid();
    };

    return Alien;

  })();

  window.Alien = Aliencon = console;

  Analyse = (function() {
    Analyse.prototype.waves = null;

    Analyse.prototype.node = null;

    Analyse.prototype.analyser = null;

    Analyse.prototype.audio = null;

    Analyse.prototype.frameBufferSize = Math.pow(2, 11);

    Analyse.prototype.channels = 4;

    Analyse.prototype.bufferSize = 0;

    Analyse.prototype.signal = null;

    Analyse.prototype.fft = null;

    Analyse.prototype.amplitude = 0;

    Analyse.prototype.spectrum = [];

    function Analyse() {
      this.step = bind(this.step, this);
      this.processAudio = bind(this.processAudio, this);
      this.bufferSize = this.frameBufferSize / this.channels;
      this.signal = new Float32Array(this.bufferSize);
      this.fft = new FFT(this.bufferSize, 44100);
      this.newAudioContext();
      // this.loadAudio('https://dl.dropboxusercontent.com/u/729503/Codepen/usee.mp3');
      this.loadAudio('https://cf-media.sndcdn.com/CmhscBlMZl1W.128.mp3?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLW1lZGlhLnNuZGNkbi5jb20vQ21oc2NCbE1abDFXLjEyOC5tcDMiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0NzcxMzk3MDN9fX1dfQ__&Signature=mCistR~h5En7Nk92Kq~bQIVWaSPmhz-PZ8CMB4OkboW-7Kg6zBGJ3Q5XQ~KDt-uMVYvelxaIZnmoB5rfyELS-ck0hX0WV4RRzHKsJXAAvQjEYES0SSpSueJFh4Vy6Oeh-xuwRK1gApq0S89Oo-km31w3cho4ix10WPcVu0Cgn0P57inrnDEq6NATn3T9iqNI47poxtqZk02VhL5GDr5DnxTvrWy7IqCTlci82LNKZEX1mY6quBP11R8EM7fUDd1BfbgEeZyjiP2IrrXsqpi9q3KDKOZtIIK24Kg2pWJWKzeHZBQu8zc8YQW-eeJYkAWpLDT132jvZ4CE3g1o3zSJow__&Key-Pair-Id=APKAJAGZ7VMH2PFPW6UQ');


      con.log("Analyse constructor", this.signal.length);
    }

    Analyse.prototype.newAudioContext = function() {
      var fok;
      fok = (function(_this) {
        return function(f) {
          return window[f] != null;
        };
      })(this);
      this.waves = false;
      if (fok('AudioContext')) {
        con.log("AudioContext");
        this.waves = new AudioContext();
      } else if (fok('webkitAudioContext')) {
        con.log("webkitAudioContext");
        this.waves = new webkitAudioContext();
      }
      return con.log("waves", this.waves);
    };

    Analyse.prototype.loadAudio = function(url) {
      con.log("loadAudio", url, this.waves);
      if (this.audio) {
        this.audio.remove();
      }
      if (this.node) {
        this.node.disconnect();
      }
      this.audio = new Audio();
      this.audio.crossOrigin = "anonymous";
      this.audio.preload = 'auto';
      this.audio.controls = true;
      document.body.appendChild(this.audio);
      if (this.waves) {
        con.log("waves created");
        this.audio.addEventListener('canplay', (function(_this) {
          return function(e) {
            return _this.setupAudioNodes(e);
          };
        })(this));
      } else {

      }
      return this.audio.src = url;
    };

    Analyse.prototype.setupAudioNodes = function(e) {
      con.log("setupAudioNodes");
      if (this.analyser == null) {
        this.analyser = this.analyser || this.waves.createScriptProcessor(this.bufferSize, 2, 2);
        this.analyser.onaudioprocess = this.processAudio;
        this.node = this.waves.createMediaElementSource(this.audio);
        this.node.connect(this.analyser);
        this.analyser.connect(this.waves.destination);
      }
      return this.audio.play();
    };

    Analyse.prototype.processAudio = function(e) {
      var i, inputArrayL, inputArrayR, k, outputArrayL, outputArrayR, ref;
      inputArrayL = e.inputBuffer.getChannelData(0);
      inputArrayR = e.inputBuffer.getChannelData(1);
      outputArrayL = e.outputBuffer.getChannelData(0);
      outputArrayR = e.outputBuffer.getChannelData(1);
      for (i = k = 0, ref = this.bufferSize; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
        outputArrayL[i] = inputArrayL[i];
        outputArrayR[i] = inputArrayR[i];
        this.signal[i] = (inputArrayL[i] + inputArrayR[i]) / 2;
      }
      this.fft.forward(this.signal);
      return this.fftChanged();
    };

    Analyse.prototype.fftChanged = function() {
      var i, k, magSum, ref, specLength;
      specLength = this.fft.spectrum.length;
      magSum = 0;
      for (i = k = 0, ref = specLength; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
        magSum += this.fft.spectrum[i];
      }
      return this.amplitude = 100 * magSum / specLength;
    };

    Analyse.prototype.step = function() {
      var bandTotal, currentBand, i, k, ref, results, specLength, totalBands;
      specLength = this.fft.spectrum.length;
      totalBands = specLength / 32;
      this.spectrum = [];
      currentBand = -1;
      results = [];
      for (i = k = 0, ref = specLength; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
        if (i % totalBands === 0) {
          bandTotal = 0;
          currentBand++;
        }
        bandTotal += this.fft.spectrum[i];
        if (i % totalBands === totalBands - 1) {
          results.push(this.spectrum[currentBand] = bandTotal);
        } else {
          results.push(void 0);
        }
      }
      return results;
    };

    Analyse.prototype.getAnalysis = function() {
      return {
        spectrum: this.spectrum,
        amplitude: this.amplitude,
        waveform: this.signal
      };
    };

    return Analyse;

  })();

  window.Analyse = Analyse;


//------------------------------------------//
//-------- STUFF FOR AUDIO ANALYSIS --------//
function FourierTransform(bufferSize, sampleRate)
{
        this.bufferSize = bufferSize;
        this.sampleRate = sampleRate;
        this.bandwidth  = bufferSize * sampleRate;
        this.spectrum   = new Float32Array(bufferSize/2);
        this.real       = new Float32Array(bufferSize);
        this.imag       = new Float32Array(bufferSize);
        this.peakBand   = 0;
        this.peak       = 0;
        this.getBandFrequency = function(index)
        {
                return this.bandwidth * index + this.bandwidth / 2;
        };
        this.calculateSpectrum = function()
        {
                var spectrum  = this.spectrum,
                real      = this.real,
                imag      = this.imag,
                bSi       = 2 / this.bufferSize,
                rval, ival, mag;
                this.peak = this.peakBand = 0;
                for (var i = 0, N = bufferSize*0.5; i < N; i++)
                {
                        rval = real[i];
                        ival = imag[i];
                        mag = bSi * Math.sqrt(rval * rval + ival * ival);
                        if (mag > this.peak)
                        {
                                this.peakBand = i;
                                this.peak = mag;
                        }
                        spectrum[i] = mag;
                }
        };
}
function FFT(bufferSize, sampleRate)
{
        FourierTransform.call(this, bufferSize, sampleRate);
        this.reverseTable = new Uint32Array(bufferSize);
        var limit = 1;
        var bit = bufferSize >> 1;
        var i;
        while (limit < bufferSize)
        {
                for (i = 0; i < limit; i++)
                this.reverseTable[i + limit] = this.reverseTable[i] + bit;
                limit = limit << 1;
                bit = bit >> 1;
        }
        this.sinTable = new Float32Array(bufferSize);
        this.cosTable = new Float32Array(bufferSize);
        for (i = 0; i < bufferSize; i++)
        {
                this.sinTable[i] = Math.sin(-Math.PI/i);
                this.cosTable[i] = Math.cos(-Math.PI/i);
        }
}
FFT.prototype.forward = function(buffer)
{
  var bufferSize      = this.bufferSize,
      cosTable        = this.cosTable,
      sinTable        = this.sinTable,
      reverseTable    = this.reverseTable,
      real            = this.real,
      imag            = this.imag,
      spectrum        = this.spectrum;
        var k = Math.floor(Math.log(bufferSize) / Math.LN2);
        if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; }
        if (bufferSize !== buffer.length)  { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }
        var halfSize = 1,
                phaseShiftStepReal,
                phaseShiftStepImag,
                currentPhaseShiftReal,
                currentPhaseShiftImag,
                off,
                tr,
                ti,
                tmpReal,
                i;
        for (i = 0; i < bufferSize; i++)
        {
                real[i] = buffer[reverseTable[i]];
                imag[i] = 0;
        }
        while (halfSize < bufferSize)
        {
                phaseShiftStepReal = cosTable[halfSize];
                phaseShiftStepImag = sinTable[halfSize];
                currentPhaseShiftReal = 1;
                currentPhaseShiftImag = 0;
                for (var fftStep = 0; fftStep < halfSize; fftStep++)
                {
                        i = fftStep;
                        while (i < bufferSize)
                        {
                                off = i + halfSize;
                                tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
                                ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
                                real[off] = real[i] - tr;
                                imag[off] = imag[i] - ti;
                                real[i] += tr;
                                imag[i] += ti;
                                i += halfSize << 1;
                        }
                        tmpReal = currentPhaseShiftReal;
                        currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
                        currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
                }
                halfSize = halfSize << 1;
        }
        return this.calculateSpectrum();
};

window.FFT = FFT;
;

  con = console;

  Music = (function() {
    Music.prototype.analyser = null;

    Music.prototype.aliendude = null;

    Music.prototype.canvas = null;

    Music.prototype.pixels = null;

    Music.prototype.canvasWidth = 1000;

    Music.prototype.canvasHeight = 500;

    Music.prototype.centreX = 0;

    Music.prototype.centreY = 0;

    Music.prototype.scale = 1;

    Music.prototype.float = 20;

    function Music() {
      this.step = bind(this.step, this);
      con.log("Music constructor");
      this.createCanvas();
      this.analyser = new Analyse(this.pixels, this.centreX, this.centreY);
      this.aliendude = new Alien();
      if (window.requestAnimationFrame) {
        con.log("native requestAnimationFrame");
      } else {
        con.log("creating requestAnimationFrame");
        window.requestAnimationFrame = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
      }
      this.step();
    }

    Music.prototype.createCanvas = function() {
      this.centreX = this.canvasWidth / 2;
      this.centreY = this.canvasHeight / 2;
      this.canvas = document.createElement('canvas');
      document.body.appendChild(this.canvas);
      this.pixels = this.canvas.getContext('2d');
      this.canvas.width = this.canvasWidth;
      return this.canvas.height = this.canvasHeight;
    };

    Music.prototype.clearCanvas = function() {
      this.pixels.fillStyle = "rgb(0, 0, 0)";
      return this.pixels.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
    };

    Music.prototype.alienScale = 2;

    Music.prototype.alienRotate = 0.01;

    Music.prototype.step = function() {
      var amplitude, anal, b, bandWidth, c, g, i, img, k, len, len1, level, n, o, p, padding, r, ref, ref1, ref2, ref3, sc, specLength, spectrum, waveform, waveformLength, x, y;
      this.clearCanvas();
      this.analyser.step();
      this.aliendude.step();
      anal = this.analyser.getAnalysis();
      spectrum = anal.spectrum;
      amplitude = anal.amplitude;
      waveform = anal.waveform;
      this.pixels.fillStyle = "rgb(0, 20, 40)";
      this.pixels.fillRect(0, this.canvasHeight, this.canvasWidth, -amplitude * this.canvasHeight);
      specLength = spectrum.length;
      bandWidth = this.canvasWidth / specLength;
      padding = 5;
      for (i = k = 0, ref = specLength; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
        c = ~~(i / specLength * 55);
        r = 100;
        g = 200 + c;
        b = 255 - c;
        level = spectrum[i];
        this.pixels.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", 0.5)";
        this.pixels.fillRect(i * bandWidth + padding, this.canvasHeight, bandWidth - padding * 2, -level * this.centreY);
      }
      waveformLength = waveform.length;
      bandWidth = this.canvasWidth / waveformLength;
      this.pixels.beginPath();
      for (i = n = 0, ref1 = waveformLength; 0 <= ref1 ? n < ref1 : n > ref1; i = 0 <= ref1 ? ++n : --n) {
        this.pixels.strokeStyle = '#0fe';
        this.pixels.lineWidth = 1;
        x = i * bandWidth;
        y = this.centreY + waveform[i] * this.centreY;
        if (i === 0) {
          this.pixels.moveTo(x, y);
        } else {
          this.pixels.lineTo(x, y);
        }
      }
      this.pixels.stroke();
      this.float += this.alienRotate;
      this.scale = spectrum[0];
      if (this.scale > this.alienScale * 4) {
        this.aliendude.change();
        this.alienScale = this.scale;
        this.alienRotate = (Math.random() - 0.5) * 0.1;
      } else {
        this.alienScale *= 0.9;
      }
      sc = this.alienScale * 1;
      img = this.aliendude.getAlien();
      this.pixels.save();
      this.pixels.translate(this.centreX, this.centreY);
      this.pixels.rotate(this.float);
      this.pixels.scale(sc, sc);
      this.pixels.translate(-img.width / 2, -img.height / 2);
      ref2 = img.aliens.x;
      for (o = 0, len = ref2.length; o < len; o++) {
        x = ref2[o];
        ref3 = img.aliens.y;
        for (p = 0, len1 = ref3.length; p < len1; p++) {
          y = ref3[p];
          this.pixels.drawImage(img, img.width * x, img.height * y);
        }
      }
      this.pixels.restore();
      return requestAnimationFrame(this.step);
    };

    return Music;

  })();

  window.Music = Music;

  new Music();

}).call(this);
</script>[/code]

happy886rr 发表于 2016-10-22 22:36

居然是傅里叶变换

523066680 发表于 2016-10-22 22:49

[i=s] 本帖最后由 523066680 于 2016-10-22 22:59 编辑 [/i]

要是结合分形想必是极美妙的
我觉得图像方面我可以玩的有趣一些,但是不懂音频解析,所以就当吹吹牛啦。模拟信号处理是不是一定会涉及博里叶变换?

恰巧就在今天看到知乎的一个帖子:有哪些计算机生成的精美图形?
某个回答里介绍了一款强大的音乐可视化插件——MilkDrop
[url]https://www.zhihu.com/question/19935130/answer/127674701[/url]

aa77dd@163.com 发表于 2016-10-22 22:57

[i=s] 本帖最后由 aa77dd@163.com 于 2016-10-22 23:01 编辑 [/i]

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=192085&ptid=42121]3#[/url] [i]523066680[/i] [/b]

自学过一点高数, 早忘光了, 不懂 傅里叶变换

想起了 N 年前 有增强版的 nullsoft winamp 就自带  MilkDrop  plug-in

523066680 发表于 2016-10-22 22:58

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=192087&ptid=42121]4#[/url] [i]aa77dd@163.com[/i] [/b]


    准备辞职深造,趁年轻。

aa77dd@163.com 发表于 2016-10-22 23:08

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=192088&ptid=42121]5#[/url] [i]523066680[/i] [/b]

哈哈, 同意

MilkDrop 源码?
[url]http://forums.winamp.com/showthread.php?t=214971[/url]

aa77dd@163.com 发表于 2016-10-22 23:32

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=192085&ptid=42121]3#[/url] [i]523066680[/i] [/b]

回味经典  含 MilkDrop2 可视化效果插件
[url]http://meggamusic.co.uk/winamp/Winamp_Download.htm[/url]

Winamp 所属公司 Nullsoft 的吉祥物是 一只 大羊驼
[img]http://www.winamp.com/images/bg01.jpg[/img]

页: [1]

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.