$30 off During Our Annual Pro Sale. View Details »

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.

Orlando Del Aguila

December 02, 2015
Tweet

More Decks by Orlando Del Aguila

Other Decks in Programming

Transcript

  1. Web
    Launchpad

    View Slide

  2. View Slide

  3. eatcodetravel.com
    orlando @ github
    @eatcodetravel

    View Slide

  4. Web
    Launchpad

    View Slide

  5. Web MIDI
    Web Audio
    Launchpad

    View Slide

  6. Web MIDI
    Web Audio
    Launchpad

    View Slide

  7. View Slide

  8. 80 buttons
    3 leds
    4 channels

    View Slide

  9. Web MIDI
    Web Audio
    Launchpad

    View Slide

  10. Web MIDI
    Web Audio
    Launchpad

    View Slide

  11. Midi
    SamplePlayer
    SamplePackStore
    UIMapper
    PubSub

    View Slide

  12. SamplePlayer
    SamplePackStore
    UIMapper
    PubSub
    Midi

    View Slide

  13. Web Audio
    Launchpad
    Web MIDI

    View Slide

  14. and Opera

    View Slide

  15. Inputs / Outputs

    View Slide

  16. How do we use it?

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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

    View Slide

  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 },

    View Slide

  21. [144, 48, 127]

    View Slide

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

    View Slide

  23. [144, 48, 127]
    127

    View Slide

  24. [144, 48, ]
    0

    View Slide

  25. Web MIDI
    Launchpad
    Web Audio

    View Slide

  26. View Slide

  27. Web Audio

    View Slide

  28. How do we use it?

    View Slide

  29. Midi
    SamplePackStore
    UIMapper
    PubSub
    SamplePlayer

    View Slide

  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

    View Slide

  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();

    View Slide

  32. AudioContext

    View Slide

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

    View Slide

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

    View Slide

  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',

    View Slide

  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;

    View Slide

  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);

    View Slide

  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 },

    View Slide

  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 },

    View Slide

  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 }());

    View Slide

  41. Demo

    View Slide

  42. Resources

    View Slide

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

    View Slide

  44. Thanks!

    View Slide

  45. QA

    View Slide

  46. %

    View Slide