Web Launchpad - Chelajs

Web Launchpad - Chelajs

Talk I gave at December's 15 Chela.js. It's about how I use Web Audio API + Web MIDI API + a Novation Launchpad to be a Web Madeon.

A0574e5093df6afd68f1c109c5a9bf22?s=128

Orlando Del Aguila

December 02, 2015
Tweet

Transcript

  1. Web Launchpad

  2. None
  3. eatcodetravel.com orlando @ github @eatcodetravel

  4. Web Launchpad

  5. Web MIDI Web Audio Launchpad

  6. Web MIDI Web Audio Launchpad

  7. None
  8. 80 buttons 3 leds 4 channels

  9. Web MIDI Web Audio Launchpad

  10. Web MIDI Web Audio Launchpad

  11. Midi SamplePlayer SamplePackStore UIMapper PubSub

  12. SamplePlayer SamplePackStore UIMapper PubSub Midi

  13. Web Audio Launchpad Web MIDI

  14. and Opera

  15. Inputs / Outputs

  16. How do we use it?

  17. 20 _initMidi: function () { 21 navigator.requestMIDIAccess({ 22 sysex: false

    23 }).then(this._onMidiSuccessHandler); 24 }, 25 26 _onMidiMessageHandler: function _onMidiMessageHandler(message) { 27 console.log(message.data); 28 PubSub.emitEvent('onMidiMessage', [message]); 29 }, 30 31 _onMidiSuccessHandler: function _onMidiSuccessHandler(midi) { 32 var inputs = midi.inputs.values(); 33 // loop over all available inputs and listen for any MIDI input 34 for (var input = inputs.next(); input && !input.done; input = inputs.next()) { 35 // each time there is a midi message call the onMIDIMessage function 36 input.value.onmidimessage = this._onMidiMessageHandler; 37 }
  18. 20 _initMidi: function () { 21 navigator.requestMIDIAccess({ 22 sysex: false

    23 }).then(this._onMidiSuccessHandler); 24 }, 25 26 _onMidiMessageHandler: 27 28 PubSub.emitEvent( 29 }, 30 31 _onMidiSuccessHandler: 32 33 // loop over all available inputs and listen for any MIDI input 34 inputs.next()) { 35 // each time there is a midi message call the onMIDIMessage function 36 input.value.onmidimessage 37 }
  19. 31 _onMidiSuccessHandler: function _onMidiSuccessHandler(midi) { 32 var inputs = midi.inputs.values();

    33 // loop over all available inputs and listen for any MIDI input 34 for (var input = inputs.next(); input && !input.done; input = inputs.next()) { 35 // each time there is a midi message call the onMIDIMessage function 36 input.value.onmidimessage = this._onMidiMessageHandler; 37 } 20 _initMidi: 21 navigator.requestMIDIAccess({ 22 23 }).then( 24 }, 25 26 _onMidiMessageHandler: 27 28 PubSub.emitEvent( 29 }, 30
  20. 20 _initMidi: 21 navigator.requestMIDIAccess({ 22 23 }).then( 24 }, 25

    30 31 _onMidiSuccessHandler: 32 33 // loop over all available inputs and listen for any MIDI input 34 inputs.next()) { 35 // each time there is a midi message call the onMIDIMessage function 36 input.value.onmidimessage 37 } 26 _onMidiMessageHandler: function _onMidiMessageHandler(message) { 27 console.log(message.data); 28 PubSub.emitEvent('onMidiMessage', [message]); 29 },
  21. [144, 48, 127]

  22. [144, 48, 127] Command or Channel Note Velocity

  23. [144, 48, 127] 127

  24. [144, 48, ] 0

  25. Web MIDI Launchpad Web Audio

  26. None
  27. Web Audio

  28. How do we use it?

  29. Midi SamplePackStore UIMapper PubSub SamplePlayer

  30. 1 (function () { 2 var SamplePlayer = function SamplePlayer(config)

    { 3 this.init(config); 4 }; 5 6 SamplePlayer.prototype = Object.create(Base); 7 8 Object.assign(SamplePlayer.prototype, { 9 events: { 10 'onMidiMessage': '_onMidiMessageHandler', 11 'samplePackChange': '_loadSamples' 12 }, 13 samplePackName: null, 14 samples: null, 15 buffers: null, 16 17 init: function init(config) { 18 this._bindAll(); 19 this.samples = {}; 20 this.buffers = {}; 21 this.audioContext = new AudioContext(); 22 this._loadSamples(SamplePackStore.activeSamplePack); 23 this._listenEvents(); 24 }, 25
  31. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 21 this.audioContext = new AudioContext();
  32. AudioContext

  33. .createBuffer() .createBufferSource() .decodeAudioData() .createStereoPanner() .createAnalyser() .createBiquadFilter()

  34. .createStereoPanner() .createAnalyser() .createBiquadFilter() .createBuffer() .createBufferSource() .decodeAudioData()

  35. 1 (function () { 2 var SamplePackStore = window.SamplePackStore =

    Object.create({}); 3 4 Object.assign(SamplePackStore, { 5 samplePacks: {}, 6 samplePacksData: {}, 7 activeSamplePack: null, 8 set: function set(key) { 9 if (this.samplePacks[key]) { 10 this.activeSamplePack = this.samplePacks[key]; 11 PubSub.emitEvent('samplePackChange', [{name: key, data: this.activeSamplePack}]); 12 } 13 } 14 }); 15 16 SamplePackStore.samplePacks.firstOfTheYear = { 17 48: '/samples/firstyear/kick1.wav', 18 49: '/samples/firstyear/freeze1.wav', 19 50: '/samples/firstyear/freeze2.wav', 20 51: '/samples/firstyear/freeze3.wav', 21 33: '/samples/firstyear/freeze4.wav', 22 34: '/samples/firstyear/freeze5.wav', 23 35: '/samples/firstyear/freeze6.wav', 24 17: '/samples/firstyear/freeze7.wav', 25 18: '/samples/firstyear/freeze8.wav', 26 19: '/samples/firstyear/freeze9.wav', 27 2: '/samples/firstyear/freeze10.wav',
  36. 1 (function () { 2 var SamplePlayer = function SamplePlayer(config)

    { 3 this.init(config); 4 }; 5 6 SamplePlayer.prototype = Object.create(Base); 7 8 Object.assign(SamplePlayer.prototype, { 9 events: { 10 'onMidiMessage': '_onMidiMessageHandler', 11 'samplePackChange': '_loadSamples' 12 }, 13 samplePackName: null, 14 samples: null, 15 buffers: null, 16 17 init: function init(config) { 18 this._bindAll(); 19 this.samples = {}; 20 this.buffers = {}; 21 this.audioContext = new AudioContext(); 22 this._loadSamples(SamplePackStore.activeSamplePack); 23 this._listenEvents(); 24 }, 25 26 _loadSamples: function _loadSamples(samplePack) { 27 var samplePlayer = this; 28 var name = this.samplePackName = samplePack.name; 29 var data = samplePack.data;
  37. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 19 20 21 23 24 25 26 27 28 29 22 this._loadSamples(SamplePackStore.activeSamplePack);
  38. 26 _loadSamples: function _loadSamples(samplePack) { 27 var samplePlayer = this;

    28 var name = this.samplePackName = samplePack.name; 29 var data = samplePack.data; 30 var urls = []; 31 32 if (this.samples[name]) { 33 return this.samples[name]; 34 } 35 36 this.samples[name] = {}; 37 this.buffers[name] = {}; 38 39 Object.keys(data).forEach(function (key) { 40 var url = data[key]; 41 urls.push(url); 42 }); 43 44 new BufferLoader(this.audioContext, urls, function (bufferList) { 45 var keys = Object.keys(data); 46 47 keys.forEach(function (key, index) { 48 var buffer = bufferList[index]; 49 50 // Store the buffer to use it later 51 samplePlayer.buffers[name][key] = buffer; 52 }); 53 }).load(); 54 },
  39. 1 _onMidiMessageHandler: function _onMidiMessageHandler(message) { 2 var data = message.data;

    3 4 var key = data[1]; 5 var command = data[2]; 6 7 var samplePackName = this.samplePackName; 8 9 if (command === 127) { 10 var buffer = this.buffers[samplePackName][key]; 11 12 if (buffer) { 13 var sample = this._createNode(buffer, this.loop); 14 this.samples[samplePackName][key] = sample; 15 16 sample.start(0); 17 } 18 19 this._ledOn(key, 'green', true); 20 } else { 21 var sample = this.samples[samplePackName][key]; 22 23 sample && sample.stop(0); 24 25 this._ledOff(key); 26 } 27 },
  40. 81 82 _createNode: function (buffer) { 83 var node =

    this.audioContext.createBufferSource(); 84 node.buffer = buffer; 85 node.connect(this.audioContext.destination); 86 87 return node; 88 }, 89 90 _ledOn: function _ledOn(key, color, full) { 91 PubSub.emitEvent('ledOn', [key, color, full]); 92 }, 93 94 _ledOff: function _ledOff(key) { 95 PubSub.emitEvent('ledOff', [key]); 96 } 97 }); 98 99 window.SamplePlayer = SamplePlayer; 100 }());
  41. Demo

  42. Resources

  43. orlando/web-launchpad html5rocks.com developer.mozilla.org splice.com

  44. Thanks!

  45. QA

  46. %