Jump To …

main.js

/*global trurl */

(function (global) {
  function onload () {
    var defaultBackgroundColor = '#ffffff', 
      defaultDeclickTime = 0.005,
      conf = { sampleRate: 44100, channels: 2, bufferSize: 8192, 
      displayWidth: 512, displayHeight: 288, frameRate: 30, 
      declickTime: defaultDeclickTime, 
      backgroundColor: defaultBackgroundColor },
      request, plotCanvas, scoreUrl, display, thePlot, 
      audioIsEnabled = false,
      displayIsEnabled = false, 
      isRunning = false,
      i,
      displayApi, 
      numTests, scoreList = {}, defaultScorePath, defaultScoreIndex,

the main objects

      ao, dout, c = conf, ctrls, plt, l, s,

convenience variables for score opcodes, scheduler.register, cardano (p for probability)

      o, r;


    numTests = 6;
    for (i=0; i<numTests; i++) {    
      scoreList[i] = 'test/' + i + '.js';
    }
    defaultScoreIndex = 4;
    defaultScorePath = scoreList[defaultScoreIndex];

Main UI transport

    function doutRunOnSamplesDone () {
      s.removeEventListener('samplesDone', doutRunOnSamplesDone);
      dout.run();
    }

    function run () {
      s.init();
      conf.declickTime = defaultDeclickTime;
      conf.backgroundColor = defaultBackgroundColor;
      if (ao || dout) {
        l.info('Running.');
        eval(document.getElementById('score').value);
        s.run();
        if (audioIsEnabled) {
          ao.init();
          ao.run();
        }
        if (displayIsEnabled) {
          dout.init();

if audio is enabled, synchronize first audio callback with start of display

          if (audioIsEnabled) {
            s.addEventListener('samplesDone', doutRunOnSamplesDone);
          } else {
            dout.run();
          }
        }
        isRunning = true;
        ctrls.run();
      } else {
        l.info("No display or audio out. Not running.");
      }
    }

    function pause () {
      if (s.pause()) {
        ctrls.setPauseText('pause   ');
      } else {
        ctrls.setPauseText('cntnu');
      }
    }

    function checkAllStopped () {
      if ((!ao || !ao.isRunning()) && (!dout || !dout.isRunning())) {
        isRunning = false;
        ctrls.stop();
      }
    }

    function audioStop (e) {
      l.info('main audioStop() ' + e.time);
      if (!ao.stop()) {
        l.info('audioout already stopped.');
      }
      checkAllStopped();
    }

    function displayStop (e) {
      l.info('main displayStop() ' + e.time);
      if (!dout.stop()) {
        l.info('displayout already stopped.');
      }
      checkAllStopped();
    }

    function stop () {
      if (ao && ao.isRunning()) {
        audioStop({ time: s.getTime() });
      }
      if (dout && dout.isRunning()) {
        displayStop({ time: s.getTime() });
      }
    }

Log

    function initLog () {
      l = trurl.log('log');
      document.getElementById('logDiv').appendChild(l.init());
      l.setLogLevel('info');
    }

Scheduler

    function initScheduler () {
      s = trurl.scheduler(conf);
      r = s.register;
      o = trurl.opcodes;
    }

    function connectSchedulerDisplay () {
      var displayApi;

      if (dout) {
        s.addEventListener('displayNoEvents', displayStop);
        s.addEventListener('displayStart', function (e) { 
            l.debug('displayStart ' + e.id + ' ' + e.time);
        });
        s.addEventListener('displayStop', function (e) { 
            l.debug('displayStop ' + e.id + ' ' + e.time);
        });
        displayApi = dout.getApi();
        if (displayApi == 'requestAnimationFrame') {
          s.setFrameTiming('time');
          ctrls.checkAnimRequest();
          ctrls.checkFrameTimingTime();
          ctrls.disableFrameTimingFrame();
        } else if (displayApi == 'interval') {
          ctrls.enableFrameTimingFrame();
        }
        dout.setDisplayCallback(s.displayCallback);
      } else {
        l.info("ERROR: could not connect scheduler display.");
      }
    }

    function disconnectSchedulerDisplay () {
      s.removeEventListeners('displayNoEvents');
      s.removeEventListeners('displayStart');
      s.removeEventListeners('displayStop');
    }

    function connectSchedulerAudio () {
      if (ao) {
        s.addEventListener('audioNoEvents', audioStop);
        s.addEventListener('audioStart', function (e) { 
          l.debug('audioStart ' + e.id + ' ' + e.time);
        });
        s.addEventListener('audioStop', function (e) { 
          l.debug('audioStop ' + e.id + ' ' + e.time);
        });
        ao.setAudioCallback(s.audioCallback);
      } else {
        l.info('ERROR: could not connect scheduler audio.');
      }
      if (ao.getApi() == 'webAudio') {
        ctrls.checkLatency();
        s.setCorrectAudioLatency(true);
      } else {
        ctrls.uncheckLatency();
        s.setCorrectAudioLatency(false);
      }
    }

    function disconnectSchedulerAudio () {
      s.removeEventListeners('audioNoEvents');
      s.removeEventListeners('audioStart');
      s.removeEventListeners('audioStop');

ao.rmAudioCallback();

    }

    function connectSchedulerWaveform () {
      s.addEventListener('samplesDone', function (e) { 
        plt.draw(e.buf, e.buf2);
      });
    }

    function disconnectSchedulerWaveform () {
      s.removeEventListeners('samplesDone');
      s.removeEventListeners('frameDone');
    }

    function connectSchedulerProfile () {
      s.addEventListener('samplesDone', function (e) { 
        console.log('audio', e.time, e.buf.length);
      });
      s.addEventListener('frameDone', function (e) { 
        console.log('display', e.time);
      });
      if (plt) {
        plt.addEventListener('waveformDone', function (e) {
          console.log('plot', e.time);
        });
      }
    }

Timing

    function setFrameTimingTime () {
      stop();
      l.info("Set frame timing to 'time'");
      s.setFrameTiming('time');
    }
    function setFrameTimingFrame () {
      stop();
      l.info("Set frame timing to 'frame'");
      s.setFrameTiming('frame');
    }

Latency

    function enableLatency () {
      stop();
      l.info("Enabling audio latency correction.");
      l.info("(recommended for WebAudioApi only!)");
      s.setCorrectAudioLatency(true);
    }
    function disableLatency () {
      stop();
      l.info("Disabling audio latency correction.");
      s.setCorrectAudioLatency(false);
    }

Display

    function checkDisplay () {
      var testDout = trurl.displayout(conf, 'display');
      if (!testDout.hasApi()) {
        l.info('No valid Display API found.');
        l.info('You must be on IE 8 or earlier,\n' + 
               'or some really ancient browser.\n' +
               'You\'re missing out!');
        l.info('Display disabled.');
      } else {
        if (!testDout.hasApi('requestAnimationFrame')) {
          ctrls.disableAnimRequest();
        }
        return true;
      }
    }

    function disableDisplay () {
      if (dout.isRunning) { stop(); }
      if (display) {
        document.getElementById('displayDiv').removeChild(display);
        display = null;
      }
      displayIsEnabled = false;
    }

    function enableDisplay () {
      if (isRunning) { stop(); }
      if (!dout) {
        dout = trurl.displayout(conf, 'display');
        l.info('Set display API to ' + dout.setApi());
      }
      disableDisplay();
      display = dout.init();
      l.info('Initialized display');
      document.getElementById('displayDiv').appendChild(display);
      connectSchedulerDisplay();
      displayIsEnabled = true;
    }

Waveform

    function disableWaveform () {
      if (isRunning) { stop(); }
      disconnectSchedulerWaveform();
      plt = null;
      if (thePlot) {
        document.getElementById('plotDiv').removeChild(thePlot);
      }
      thePlot = null;
    }

    function enableWaveform () {
      if (isRunning) { stop(); }
      disableWaveform();
      plt = trurl.plot('plot');
      plt.setSize(400, 100);
      thePlot = plt.init();
      document.getElementById('plotDiv').appendChild(thePlot);
      connectSchedulerWaveform();

connectSchedulerProfile();

    }

Audio

    function checkAudio () {
      var testAo = trurl.audioout(conf);
      if (!testAo.hasApi()) {
        l.info('No valid Audio API found.');
        l.info('Recent versions of Chrome (Web Audio API)\n' + 
               'and Firefox (Audio Data API) are known to work.');
        l.info('Audio disabled.');
      } else {
        return true;
      }
    }

    function enableAudio () {
      if (isRunning) { stop(); }
      if (!ao) {
        ao = trurl.audioout(conf);
        l.info('Set audio API to ' + ao.setApi());
        conf = ao.init();
      }
      connectSchedulerAudio();
      ctrls.enableWaveform();
      audioIsEnabled = true;
    }

    function disableAudio () {
      if (isRunning) { stop(); }

disconnectSchedulerAudio();

      disableWaveform();
      ctrls.disableWaveform();
      audioIsEnabled = false;
    }

Animation method

    function setAnimInterval () {
      disableDisplay();
      l.info('Set animation method to ' + dout.setApi('interval'));
      enableDisplay();
    } 

    function setAnimRequest () {
      disableDisplay();
      l.info('Set animation method to ' + dout.setApi('requestAnimationFrame'));
      enableDisplay();
    } 

    function changeAnimFrameRate (e) {
      disableDisplay();
      dout.setFrameRate(e.frameRate);
      enableDisplay();
    }

Score loading

    function loadScore (path) {
      scoreUrl = path;
      request = new XMLHttpRequest();
      request.onreadystatechange = function () {
        if (request.readyState == 4) {
          if (request.status == 200) {
            document.getElementById('score').value = request.responseText;
            l.info('score loaded.');
          }
        }
      };    
      l.info('loading score from ' + scoreUrl + '...');
      request.open('GET', scoreUrl);
      request.send(null);
    }

Controls

    function changeSelectSco (e) {
      var path;
      path = e.target.options[e.target.selectedIndex].value;
      loadScore(path);
    }

    function initControls () {
      var selectParam;
      selectParam = { name: 'SelectSco', opts: scoreList, 
         defaultIndex: defaultScoreIndex };
      ctrls = trurl.controls('controls');
      ctrls.init();
      document.getElementById('transportDiv').appendChild(ctrls.getTransport());
      document.getElementById('optionsDiv').appendChild(ctrls.getOptions());
      document.getElementById('selectDiv').appendChild(document.createTextNode('tests: '));
      document.getElementById('selectDiv').appendChild(
          ctrls.makeSelect(selectParam));

      document.getElementById('advancedDiv').appendChild(ctrls.getAdvanced());
      document.getElementById('scoreDiv').appendChild(ctrls.getScore());
      ctrls.setAnimFrameRate(conf.frameRate);
      ctrls.addEventListener('run', run);
      ctrls.addEventListener('pause', pause);
      ctrls.addEventListener('stop', stop);
      ctrls.addEventListener('enableAudio', enableAudio);
      ctrls.addEventListener('disableAudio', disableAudio);
      ctrls.addEventListener('enableWaveform', enableWaveform);
      ctrls.addEventListener('disableWaveform', disableWaveform);
      ctrls.addEventListener('enableDisplay', enableDisplay);
      ctrls.addEventListener('disableDisplay', disableDisplay);
      ctrls.addEventListener('enableLatency', enableLatency);
      ctrls.addEventListener('disableLatency', disableLatency);
      ctrls.addEventListener('setFrameTimingTime', setFrameTimingTime);
      ctrls.addEventListener('setFrameTimingFrame', setFrameTimingFrame);
      ctrls.addEventListener('setAnimInterval', setAnimInterval);
      ctrls.addEventListener('setAnimRequest', setAnimRequest);
      ctrls.addEventListener('changeAnimFrameRate', changeAnimFrameRate);
      ctrls.addEventListener('changeSelectSco', changeSelectSco);
    }

    initLog();
    initControls();
    initScheduler();

    if (checkAudio()) {
      enableWaveform();
      enableAudio();
    } else {
      ctrls.disableAudio();
      disableAudio();
    }
    if (checkDisplay()) {
      enableDisplay();
    } else {
      ctrls.disableDisplay();
      disableDisplay();
    }

    loadScore(defaultScorePath);
  }

  if (global.addEventListener) {
    global.addEventListener("load", onload, false);
  } else if (global.attachEvent) {
    global.attachEvent("onload", onload);
  } else {
    global.onload = onload;
  }
}(this));