示例#1
0
function init() {
  audioCtx = createAudioContext()
  masterGain = audioCtx.createGain();
  masterGain.connect(audioCtx.destination);
  console.log(audioCtx);
  start()
}
示例#2
0
module.exports = function () {
  if (canPlayDDS) log('Dolby Digital Plus supported!');

  const audioCache = {};
  const audioTimeCache = {};
  let playlistCounter = 0;

  const audioContext = createAudioContext();
  setTimeout(() => resume(), 1000);

  // console.log(audioContext.sampleRate)
  //new (window.AudioContext || window.webkitAudioContext)();
  const analyserNode = audioContext.createAnalyser();
  const freqArray = new Uint8Array(analyserNode.frequencyBinCount);

  // If rate is not 44100, the reverb module bugs out
  const supportReverb = audioContext.sampleRate <= 96000; 
  const effectNode = createEffectNode(audioContext.destination);
  analyserNode.connect(effectNode);

  const sampleRate = audioContext.sampleRate;
  const freqBinCount = analyserNode.frequencyBinCount;

  let effect = 0;
  const player = new EventEmitter();

  let loadingAudio = false;
  let queueing = false;
  let waitingForNext = false;
  let queuedAudio, playingAudio;
  let dataIsInvalid = false;
  let dataValidationInterval = null;
  let fillWithFakeData = false;
  let lastTrackName;
  const VALIDATION_TIME = 3000;

  Object.defineProperty(player, 'effect', {
    get: function () {
      return effect;
    },
    set: function (val) {
      effect = val;
      effectNode.wet.value = val;
      effectNode.dry.value = 1 - val;
    }
  });

  player.update = update;
  player.binCount = NUM_BINS;
  player.beats = newArray(NUM_BINS, 0);

  player.queue = queue;
  player.playQueued = playQueued;
  player.skip = skip;
  return player;

  function skip () {
    playlistCounter++;
  }

  function resume () {
    if (audioContext.state === 'suspended' &&
        typeof audioContext.resume === 'function') {
      audioContext.resume();
    }
  }

  function createEffectNode (output) {
    if (supportReverb) {
      const reverb = Reverb(audioContext);
      reverb.time = 4.5; // seconds
      reverb.wet.value = 0;
      reverb.dry.value = 1;
      reverb.filterType = 'highpass';
      reverb.cutoff.value = 200; // Hz
      reverb.connect(output);
      return reverb;
    } else {
      const node = audioContext.createGain();
      const dry = audioContext.createGain();
      const wet = audioContext.createGain();
      const filter = audioContext.createBiquadFilter();

      node.connect(dry);
      node.connect(wet);

      filter.type = 'lowpass';
      filter.frequency.value = 1000;

      dry.connect(output);
      wet.connect(filter);
      filter.connect(output);

      Object.defineProperties(node, {
        wet: { get: () => wet.gain },
        dry: { get: () => dry.gain }
      });
      node.wet.value = 0;
      node.dry.value = 1;
      return node;
    }
  }

  function update (dt) {
    if (!playingAudio) return;
    analyserNode.getByteFrequencyData(freqArray);
    player.beats = playingAudio.detectBeats(freqArray);

    if (!isDataValid()) {
      dataIsInvalid = true;
    }

    if (fillWithFakeData) fillFakeData();
  }

  // Safari (iOS/Desktop) returns garbage audio
  // frequency data since we are using a media
  // element source, not a fully decoded source.
  // For these browsers we will just "fake" the
  // visualization.
  function isDataValid () {
    var test = freqArray[0];
    for (let i = 0; i < freqArray.length; i++) {
      if (freqArray[i] !== test) return true;
    }
    return false;
  }

  function dataValidation () {
    if (dataIsInvalid) {
      // console.log('Data has been invalid for X frames, filling with fake frequencies.');
      dataIsInvalid = false;
      fillWithFakeData = true;
    }
  }

  function fillFakeData () {
    for (let i = 0; i < freqArray.length; i++) {
      freqArray[i] = 127;
    }
  }

  function queue () {
    if (queueing) return lastTrackName;
    queueing = true;
    const newIdx = playlistCounter++ % playlists.length;
    const sources = playlists[newIdx];
    const frequencyBand = frequencies[newIdx];
    const sourceUrl = typeof sources[0] === 'string' ? sources[0] : sources[0].src;

    loadAudio(sources, frequencyBand, (audio) => {
      queuedAudio = audio;
      queueing = false;
      player.emit('ready');
    });
    lastTrackName = path.basename(sourceUrl, path.extname(sourceUrl));

    // Send original track name so we know what is being played
    if (window.ga) {
      window.ga('send', 'event', 'audio', 'queue', lastTrackName);
    }

    lastTrackName = lastTrackName.replace(/\_Dolby/i, '');
    lastTrackName = lastTrackName.replace(/\_/g, ' ');
    lastTrackName = lastTrackName.replace('Interlude', ' (Interlude)');
    lastTrackName = lastTrackName.replace('Fur Alina', '(Für Alina)');
    return lastTrackName.trim();
  }

  function playQueued () {
    // console.log('About to play...');
    if (waitingForNext) return;
    if (queueing) {
      stopLast();
      waitingForNext = true;
      player.once('ready', () => {
        waitingForNext = false;
        playQueued();
      });
      // console.log('Deferring next load...');
      return;
    }
    stopLast();
    dataIsInvalid = false;
    fillWithFakeData = false;
    queuedAudio.play();
    playingAudio = queuedAudio;
    if (dataValidationInterval) clearTimeout(dataValidationInterval);
    dataValidationInterval = setTimeout(dataValidation, VALIDATION_TIME);
    // console.log('Playing...');
  }
  
  function stopLast () {
    if (playingAudio) {
      audioTimeCache[playingAudio.urlKey] = playingAudio.element.currentTime;
      playingAudio.stop();

      const lastSources = [];
      const element = playingAudio.element;
      while (element.firstChild) {
        lastSources.push(element.firstChild);
        element.removeChild(element.firstChild);
      }

      playingAudio.lastSources = lastSources;
      playingAudio.element.load();
      playingAudio.node.disconnect();
    }
  }

  function loadAudio (sources, ranges, cb) {
    if (loadingAudio) return;
    if (!Array.isArray(sources)) sources = [ sources ];
    const urlKey = typeof sources[0] === 'string' ? sources[0] : sources[0].src;
    loadingAudio = true;

    // if (urlKey in audioCache) {
    //   const ret = audioCache[urlKey];
    //   ret.lastSources.forEach(source => {
    //     ret.element.appendChild(source);
    //   });
    //   ret.lastSources.length = 0;
    //   ret.element.currentTime = ret.lastTime;
    //   ret.element.load();
    //   process.nextTick(() => {
    //     cb(ret);
    //     loadingAudio = false;
    //   });
    //   return ret;
    // }

    // Fix Safari 9 bug
    resume();

    const audio = audioPlayer(sources, {
      loop: true,
      buffer: false,
      context: audioContext
    });
    audioCache[urlKey] = audio;
    audio.urlKey = urlKey;

    audio.on('error', err => {
      console.error(err);
    });

    const bins = ranges.map(range => {
      return {
        lo: frequencyToIndex(range[0], sampleRate, freqBinCount),
        hi: frequencyToIndex(range[1], sampleRate, freqBinCount),
        threshold: 100,
        decay: 0.001
      };
    });
    audio.detectBeats = createBeatDetection(bins);
    
    audio.on('decoding', () => {
      // console.log('Decoding', urlKey);
    });
    audio.on('load', () => {
      // console.log('Audio loaded...');
      // start playing audio file
      
      if (urlKey in audioTimeCache) {
        audio.element.currentTime = audioTimeCache[urlKey];
      }

      cb(audio);
      loadingAudio = false;
    });
    audio.node.connect(analyserNode);
    return audio;
  }
};