Home Reference Source Repository

src/index.js

const midi = require('midi'),
      Button = require('./helpers/button'),
      EventEmitter = require('events'),
      util = require('util')

var layouts = {
  NOTE: 0,
  DRUM: 1,
  FADE: 2,
  PROGRAMMER: 3
}

/**
 * A Launchpad instance.
 * 
 * This class will emit two events:
 * 
 * - `press` - Emitted when a button is pressed. Example usage:
 * 
 * ```js
 * myLaunchpad.on("press", pressInfo => {
 *   console.log(pressInfo.button, pressInfo.velocity)
 * })
 * ```
 * 
 * - `release` - Emitted when a button is released. Example usage:
 * 
 * ```js
 * myLaunchpad.on("release", button => {
 *   console.log(button)
 * })
 * ```
 * 
 * @class Launchpad
 * @extends {EventEmitter}
 */
export default class Launchpad extends EventEmitter {
  /**
   * Creates an instance of Launchpad.
   * 
   * @param {Object} params The paramaters to instantiate the Launchpad.
   * @param {Number} params.in The input port of the Launchpad.
   * @param {Number} params.out The output port of the Launchpad.
   * 
   * @example
   * let myLaunchpad = new Launchpad({
   *   in: 1,
   *   out: 1
   * })
   * 
   * @memberOf Launchpad
   */
  constructor(params) {
    super()

    /**
     * All the buttons on this Launchpad. Get one by using `getButton`.
     * @type {Button[]}
     */
    this.buttons = [ ]
    
    this._input = new midi.input()
    this._output = new midi.output()

    this._input.openPort(params.in)

    this._input.on("message", (dTime, message) => {
      if(message[0] === 176 || message[0] === 144){
        // button message
        if(message[2] > 0)
          /**
           * Fired when a button is pressed.
           * 
           * @event Launchpad#press
           * @type {object}
           * @property {Button} button The button that was pressed
           * @property {Number} velocity The velocity at which the button was pressed
           */
          this.emit("press", {
            button: this.buttons[message[1]],
            velocity: message[2]
          })
        else
          /**
           * Fired when a button is released.
           * 
           * @event Launchpad#release
           * @type {Button}
           */
          this.emit("release", this.buttons[message[1]])
      }
    })

    this._output.openPort(params.out)

    for(var i=0;i<100;i++)
      this.buttons.push(new Button(self, i))
  }

  /**
   * Convenience method to add the system-exclusive message header to a message
   * and then send it to the Launchpad.
   * 
   * @param {Number[]} bytes The bytes of the SysEx message to send. Header is automatically included.
   * 
   * @example
   * myLaunchpad.sendSysEx([0, 1, 2, 3])
   * 
   * @memberOf Launchpad
   */
  sendSysEx(bytes){
    // sysex header
    var message = [240, 0, 32, 41, 2, 16]
    // sysex message
    bytes.forEach(byte => message.push(byte))
    // sysex terminator
    message.push(247)
    // console.log("sysex", message)
    this._output.sendMessage(message)
  }

  /**
   * Get a button on this Launchpad
   * 
   * @param {Number} x The x-coordinate of the button.
   * @param {Number} y The y-coordinate of the button.
   * @returns {Button}
   * 
   * @example
   * let button = myLaunchpad.getButton(1, 2)
   * 
   * // do whatever with the button
   * button.setRgbColor(10, 30, 10)
   * 
   * @memberOf Launchpad
   */
  getButton(x, y) {
    return this.buttons[(x.toString() === "0" ? "" : x.toString()) + y.toString()]
  }

  /**
   * Convenience method to light up all the buttons on the Launchpad a certain color.
   * 
   * @param {Number} color Note representation of the color.
   * 
   * @example
   * myLaunchpad.lightAll(23)
   * 
   * @memberOf Launchpad
   */
  lightAll(color) {
    this.sendSysEx([14, color])
  }

  /**
   * Convenience method to light up all the buttons on the Launchpad a certain color with RGB values.
   * 
   * @param {Number} r Red value. 0-63
   * @param {Number} g Green value. 0-63
   * @param {Number} b Blue value. 0-63
   * 
   * @example
   * myLaunchpad.lightAllRgb(10, 30, 10)
   * 
   * @memberOf Launchpad
   */
  lightAllRgb(r, g, b) {
    this.buttons.forEach(button => button.setRgbColor(r, g, b))
  }

  /**
   * Convenience method to darken all buttons on the Launchpad.
   * 
   * @example
   * myLaunchpad.darkAll()
   * 
   * @memberOf Launchpad
   */
  darkAll() {
    this.buttons.forEach(button => button.setColor(0))
  }

  /**
   * Set the Launchpad's layout.
   * 
   * @param {String} layout The layout you want to switch to. Can be one of _NOTE, DRUM, FADER, PROGRAMMER_.
   * 
   * @example
   * myLaunchpad.toLayout("PROGRAMMER")
   * 
   * @memberOf Launchpad
   */
  toLayout(layout) {
    this._output.sendMessage([240, 0, 32, 41, 2, 16, 44, layouts[layout], 247])
  }

  /**
   * Scroll text across the Launchpad.
   * 
   * @param {String} text The text to scroll.
   * @param {Number} color Note representation of the text color.
   * @param {Boolean} loop If true, will loop the text scrolling until another text scroll message is sent.
   * @param {Number} speed The speed of the text scrolling. 1-7
   * 
   * @example
   * myLaunchpad.scrollText("Hello node!", 23, true, 5)
   * 
   * @memberOf Launchpad
   */
  scrollText(text, color, loop, speed) {
    var message = [20, color, loop]

    loop = loop ? 1 : 0

    for(var i=0;i<text.length;i++)
      message.push(text.charCodeAt(i))
    
    this.sendSysEx(message)
    if(speed) this.sendSysEx([20, color, loop, speed])
  }
}