surface_master 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +17 -0
 - data/docs/Numark_Orbit_QuickRef.md +5 -0
 - data/examples/{launchpad_testbed.rb → launchpad_playground.rb} +61 -21
 - data/examples/{orbit_testbed.rb → orbit_device.rb} +0 -6
 - data/examples/orbit_interaction.rb +71 -0
 - data/examples/orbit_playground.rb +135 -0
 - data/examples/{monitor.rb → system_monitor.rb} +2 -2
 - data/lib/surface_master/device.rb +5 -2
 - data/lib/surface_master/interaction.rb +15 -53
 - data/lib/surface_master/launchpad/device.rb +1 -1
 - data/lib/surface_master/orbit/device.rb +146 -98
 - data/lib/surface_master/orbit/interaction.rb +62 -12
 - data/lib/surface_master/orbit/midi_codes.rb +19 -19
 - data/lib/surface_master/version.rb +1 -1
 - data/lib/surface_master.rb +1 -0
 - metadata +7 -5
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: f2b72c0db5f531114165b1bf813b7723de8daa62
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 816e060bcc90c233e3fe80f994295a0d04222334
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 7d7e478d03cb7f4bfbb8dd29c527c374d4ad798054b04a3a9c6326d4ed42e56cd971c2830642715df5e9d851de8631342f6d02f215b4cd6c0394ec83e6af7206
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 3252e00acc136cf55dee085e073266f65f7cdb63494f84ddcead0fb6bac150e73598fa9e922fc8aefdc58ec6cb9e5e7a608b6c8a4798e2b0141bfc66b8ca0147
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,5 +1,22 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Changes
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## v0.4.0
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            * Jettison threaded input handling.
         
     | 
| 
      
 6 
     | 
    
         
            +
            * More Numark Orbit handling (output is still a problem).
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## v0.3.0
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            * __BREAKING CHANGE__: Change Orbit interface to more closely follow conventions established with Novation Launchpad driver.
         
     | 
| 
      
 12 
     | 
    
         
            +
            * Add preliminary `Interaction` class for Numark Orbit.
         
     | 
| 
      
 13 
     | 
    
         
            +
                * See `examples/orbit_interaction.rb`.
         
     | 
| 
      
 14 
     | 
    
         
            +
            * Fix exclusive binding in `Interaction` to not nuke coarser bindings entirely.
         
     | 
| 
      
 15 
     | 
    
         
            +
            * Rename `examples/orbit_testbed.rb` to `examples/orbit_device.rb`.
         
     | 
| 
      
 16 
     | 
    
         
            +
            * Rename `examples/monitor.rb` to `examples/system_monitor.rb`.
         
     | 
| 
      
 17 
     | 
    
         
            +
            * Rename `examples/launchpad_testbed.rb` to `examples/launchpad_playground.rb`.
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       3 
20 
     | 
    
         
             
            ## v0.2.1
         
     | 
| 
       4 
21 
     | 
    
         | 
| 
       5 
22 
     | 
    
         
             
            * Missed a file rename.
         
     | 
| 
         @@ -1,5 +1,10 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Numark Orbit Quick Reference
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## Mappings
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Use the file `mappings/Orbit_Preset.json`, and be sure to hit `Send` several times, as it sometimes does not actually take.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
       3 
8 
     | 
    
         
             
            ## Low Battery Indicator
         
     | 
| 
       4 
9 
     | 
    
         | 
| 
       5 
10 
     | 
    
         
             
            Any blinking `Pad Bank Selectors` indicates the battery is low.
         
     | 
| 
         @@ -1,32 +1,52 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            # This file provides an example of using the Novation Launchpad Mark 2 using
         
     | 
| 
      
 4 
     | 
    
         
            +
            # event handlers to respond to buttons, and batch LED updates to update every
         
     | 
| 
      
 5 
     | 
    
         
            +
            # button on the board quite rapidly.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # Controls:
         
     | 
| 
      
 8 
     | 
    
         
            +
            #
         
     | 
| 
      
 9 
     | 
    
         
            +
            # * Press any grid button and observe that the color changes to white while it's held.  Any number
         
     | 
| 
      
 10 
     | 
    
         
            +
            #   of pads may be pressed simultaneously.
         
     | 
| 
      
 11 
     | 
    
         
            +
            # * The control buttons on the right will each channel-flip a quadrant of the board.  They act as
         
     | 
| 
      
 12 
     | 
    
         
            +
            #   toggles, to pressing them again undoes the effect.
         
     | 
| 
      
 13 
     | 
    
         
            +
            # * The `Mixer` button will terminate the simulation.
         
     | 
| 
      
 14 
     | 
    
         
            +
            #
         
     | 
| 
      
 15 
     | 
    
         
            +
            # Effects:
         
     | 
| 
      
 16 
     | 
    
         
            +
            #
         
     | 
| 
      
 17 
     | 
    
         
            +
            # * The color of a pad is applied in layers additively, with values clamped to white at the end:
         
     | 
| 
      
 18 
     | 
    
         
            +
            #     1. The base color for each grid pad is defined by its position with the color getting more
         
     | 
| 
      
 19 
     | 
    
         
            +
            #        green on the X axis, and more blue along the Y axis.  See `SCALE` for how steep the change
         
     | 
| 
      
 20 
     | 
    
         
            +
            #        is.
         
     | 
| 
      
 21 
     | 
    
         
            +
            #     2. Each quadrant adds in a specific color (see `QUADRANTS` below).
         
     | 
| 
      
 22 
     | 
    
         
            +
            #     3. The red/green/blue channels may be rotated for any given quadrant, if the relevant toggle
         
     | 
| 
      
 23 
     | 
    
         
            +
            #        is active.
         
     | 
| 
      
 24 
     | 
    
         
            +
            #     4. A sine-wave is applied to one or more channels (see `TIME_SCALE` for the speed of the sine
         
     | 
| 
      
 25 
     | 
    
         
            +
            #        wave).
         
     | 
| 
      
 26 
     | 
    
         
            +
            #     5. If a particular pad is pressed, the color is set to white.
         
     | 
| 
      
 27 
     | 
    
         
            +
            #
         
     | 
| 
      
 28 
     | 
    
         
            +
            # TODO: Input handling seems to interfere with frame rendering, as our FPS seems to "jump" to
         
     | 
| 
      
 29 
     | 
    
         
            +
            # TODO: insane values when a button is pressed.
         
     | 
| 
      
 30 
     | 
    
         
            +
            #
         
     | 
| 
       2 
31 
     | 
    
         
             
            require "rubygems"
         
     | 
| 
       3 
32 
     | 
    
         
             
            require "bundler/setup"
         
     | 
| 
       4 
33 
     | 
    
         
             
            Bundler.require(:default, :development)
         
     | 
| 
       5 
34 
     | 
    
         | 
| 
       6 
35 
     | 
    
         
             
            require "surface_master"
         
     | 
| 
       7 
36 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
            #  
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
            # Set all:  F0h 00h 20h 29h 02h 18h 0Eh <Colour> F7h
         
     | 
| 
       11 
     | 
    
         
            -
            # Set row:  F0h 00h 20h 29h 02h 18h 0Dh <Row> <Colour> F7h
         
     | 
| 
       12 
     | 
    
         
            -
            # Set col:  F0h 00h 20h 29h 02h 18h 0Ch <Column> <Colour> F7h
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
            # Configuration
         
     | 
| 
       15 
     | 
    
         
            -
            SCALE       = 2
         
     | 
| 
      
 37 
     | 
    
         
            +
            # Animation Configuration
         
     | 
| 
      
 38 
     | 
    
         
            +
            SCALE       = [2, 2]
         
     | 
| 
       16 
39 
     | 
    
         
             
            TIME_SCALE  = 4.0
         
     | 
| 
      
 40 
     | 
    
         
            +
            TIME_MASK   = { red: 0x3F, green: 0x00, blue: 0x00 }
         
     | 
| 
      
 41 
     | 
    
         
            +
            QUADRANTS = [[{ red: 0x2F, green: 0x00, blue: 0x00 }, { red: 0x00, green: 0x2F, blue: 0x00 }],
         
     | 
| 
      
 42 
     | 
    
         
            +
                         [{ red: 0x00, green: 0x00, blue: 0x2F }, { red: 0x2F, green: 0x2F, blue: 0x00 }]]
         
     | 
| 
       17 
43 
     | 
    
         | 
| 
       18 
44 
     | 
    
         
             
            # State
         
     | 
| 
       19 
     | 
    
         
            -
            QUADRANTS = [
         
     | 
| 
       20 
     | 
    
         
            -
              [{ red: 0x2F, green: 0x00, blue: 0x00 }, { red: 0x00, green: 0x2F, blue: 0x00 }],
         
     | 
| 
       21 
     | 
    
         
            -
              [{ red: 0x00, green: 0x00, blue: 0x2F }, { red: 0x2F, green: 0x2F, blue: 0x00 }],
         
     | 
| 
       22 
     | 
    
         
            -
            ]
         
     | 
| 
       23 
45 
     | 
    
         
             
            FLIPPED = [[false, false], [false, false]]
         
     | 
| 
       24 
46 
     | 
    
         
             
            PRESSED = (0..7).map { |_x| (0..7).map { |_y| false } }
         
     | 
| 
       25 
47 
     | 
    
         
             
            NOW     = [Time.now.to_f]
         
     | 
| 
       26 
48 
     | 
    
         | 
| 
       27 
49 
     | 
    
         
             
            # Helpers
         
     | 
| 
       28 
     | 
    
         
            -
            CC      = %i(up down left right session user1 user2 scene1 scene2 scene3 scene4 scene5 scene6 scene7
         
     | 
| 
       29 
     | 
    
         
            -
                         scene8)
         
     | 
| 
       30 
50 
     | 
    
         
             
            GRID    = (0..7).map { |x| (0..7).map { |y| { grid: [x, y] } } }.flatten
         
     | 
| 
       31 
51 
     | 
    
         
             
            WHITE   = { red: 0x3F, green: 0x3F, blue: 0x3F }
         
     | 
| 
       32 
52 
     | 
    
         
             
            BLACK   = { red: 0x00, green: 0x00, blue: 0x00 }
         
     | 
| 
         @@ -41,15 +61,15 @@ end 
     | 
|
| 
       41 
61 
     | 
    
         | 
| 
       42 
62 
     | 
    
         
             
            def positional_color(x, y)
         
     | 
| 
       43 
63 
     | 
    
         
             
              { red:   0x00,
         
     | 
| 
       44 
     | 
    
         
            -
                green: (x * SCALE),
         
     | 
| 
       45 
     | 
    
         
            -
                blue:  (y * SCALE) }
         
     | 
| 
      
 64 
     | 
    
         
            +
                green: (x * SCALE[0]),
         
     | 
| 
      
 65 
     | 
    
         
            +
                blue:  (y * SCALE[1]) }
         
     | 
| 
       46 
66 
     | 
    
         
             
            end
         
     | 
| 
       47 
67 
     | 
    
         | 
| 
       48 
68 
     | 
    
         
             
            def temporal_color(_x, _y)
         
     | 
| 
       49 
69 
     | 
    
         
             
              s_t = (Math.sin(NOW[0] * TIME_SCALE) * 0.5) + 0.5
         
     | 
| 
       50 
     | 
    
         
            -
              { red:   (s_t *  
     | 
| 
       51 
     | 
    
         
            -
                green:  
     | 
| 
       52 
     | 
    
         
            -
                blue:   
     | 
| 
      
 70 
     | 
    
         
            +
              { red:   (s_t * TIME_MASK[:red]).round,
         
     | 
| 
      
 71 
     | 
    
         
            +
                green: (s_t * TIME_MASK[:green]).round,
         
     | 
| 
      
 72 
     | 
    
         
            +
                blue:  (s_t * TIME_MASK[:blue]).round }
         
     | 
| 
       53 
73 
     | 
    
         
             
            end
         
     | 
| 
       54 
74 
     | 
    
         | 
| 
       55 
75 
     | 
    
         
             
            def clamp_color(color)
         
     | 
| 
         @@ -155,18 +175,38 @@ interaction.response_to(:mixer, :down) do |_interaction, _action| 
     | 
|
| 
       155 
175 
     | 
    
         
             
              interaction.stop
         
     | 
| 
       156 
176 
     | 
    
         
             
            end
         
     | 
| 
       157 
177 
     | 
    
         
             
            interaction.change(red: 0x03, green: 0x00, blue: 0x00, cc: :mixer)
         
     | 
| 
       158 
     | 
    
         
            -
            BTN_COL = { red: 0x03, green: 0x03, blue: 0x03 
     | 
| 
      
 178 
     | 
    
         
            +
            BTN_COL = { red: 0x03, green: 0x03, blue: 0x03 }
         
     | 
| 
       159 
179 
     | 
    
         
             
            interaction.changes(%i(scene1 scene2 scene3 scene4).map { |cc| BTN_COL.merge(cc: cc) })
         
     | 
| 
       160 
180 
     | 
    
         | 
| 
       161 
181 
     | 
    
         
             
            init_board(interaction)
         
     | 
| 
       162 
182 
     | 
    
         
             
            input_thread = Thread.new do
         
     | 
| 
       163 
183 
     | 
    
         
             
              interaction.start
         
     | 
| 
       164 
184 
     | 
    
         
             
            end
         
     | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
      
 185 
     | 
    
         
            +
            cumulative_time   = 0.0
         
     | 
| 
      
 186 
     | 
    
         
            +
            frame_times       = []
         
     | 
| 
      
 187 
     | 
    
         
            +
            min_frame_time    = Float::INFINITY
         
     | 
| 
      
 188 
     | 
    
         
            +
            max_frame_time    = 0.0
         
     | 
| 
      
 189 
     | 
    
         
            +
            animation_thread  = Thread.new do
         
     | 
| 
       166 
190 
     | 
    
         
             
              loop do
         
     | 
| 
       167 
191 
     | 
    
         
             
                begin
         
     | 
| 
       168 
192 
     | 
    
         
             
                  NOW[0] = Time.now.to_f
         
     | 
| 
       169 
193 
     | 
    
         
             
                  init_board(interaction)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  elapsed           = Time.now.to_f - NOW[0]
         
     | 
| 
      
 195 
     | 
    
         
            +
                  cumulative_time  += elapsed
         
     | 
| 
      
 196 
     | 
    
         
            +
                  min_frame_time    = elapsed if elapsed < min_frame_time
         
     | 
| 
      
 197 
     | 
    
         
            +
                  max_frame_time    = elapsed if elapsed > max_frame_time
         
     | 
| 
      
 198 
     | 
    
         
            +
                  frame_times.push(elapsed)
         
     | 
| 
      
 199 
     | 
    
         
            +
                  frame_times.shift if frame_times.length > 60
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                  # Show avg FPS once per second.
         
     | 
| 
      
 202 
     | 
    
         
            +
                  if cumulative_time >= 1.0
         
     | 
| 
      
 203 
     | 
    
         
            +
                    cumulative_time = 0.0
         
     | 
| 
      
 204 
     | 
    
         
            +
                    avg_frame_time = frame_times.inject(0.0) { |a, e| a + e } / frame_times.length
         
     | 
| 
      
 205 
     | 
    
         
            +
                    avg_rate = (1.0 / avg_frame_time)
         
     | 
| 
      
 206 
     | 
    
         
            +
                    max_rate = (1.0 / min_frame_time)
         
     | 
| 
      
 207 
     | 
    
         
            +
                    min_rate = (1.0 / max_frame_time)
         
     | 
| 
      
 208 
     | 
    
         
            +
                    puts "FPS: Average = %0.1f, Min = %0.1f, Max = %0.1f" % [avg_rate, min_rate, max_rate]
         
     | 
| 
      
 209 
     | 
    
         
            +
                  end
         
     | 
| 
       170 
210 
     | 
    
         
             
                rescue StandardError => e
         
     | 
| 
       171 
211 
     | 
    
         
             
                  puts e.inspect
         
     | 
| 
       172 
212 
     | 
    
         
             
                  puts e.backtrace.join("\n")
         
     | 
| 
         @@ -1,16 +1,10 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            #!/usr/bin/env ruby
         
     | 
| 
       2 
     | 
    
         
            -
            # require "bignum"
         
     | 
| 
       3 
2 
     | 
    
         
             
            require "rubygems"
         
     | 
| 
       4 
3 
     | 
    
         
             
            require "bundler/setup"
         
     | 
| 
       5 
4 
     | 
    
         
             
            Bundler.require(:default, :development)
         
     | 
| 
       6 
5 
     | 
    
         | 
| 
       7 
6 
     | 
    
         
             
            require "surface_master"
         
     | 
| 
       8 
7 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            # Monkey-patching to make debugging easier.
         
     | 
| 
       10 
     | 
    
         
            -
            class Fixnum
         
     | 
| 
       11 
     | 
    
         
            -
              def to_hex; "%02X" % self; end
         
     | 
| 
       12 
     | 
    
         
            -
            end
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
8 
     | 
    
         
             
            SurfaceMaster.init!
         
     | 
| 
       15 
9 
     | 
    
         
             
            device = SurfaceMaster::Orbit::Device.new
         
     | 
| 
       16 
10 
     | 
    
         
             
            loop do
         
     | 
| 
         @@ -0,0 +1,71 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "rubygems"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "bundler/setup"
         
     | 
| 
      
 4 
     | 
    
         
            +
            Bundler.require(:default, :development)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require "surface_master"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            SurfaceMaster.init!
         
     | 
| 
      
 9 
     | 
    
         
            +
            interaction = SurfaceMaster::Orbit::Interaction.new
         
     | 
| 
      
 10 
     | 
    
         
            +
            interaction.response_to(:pad, :down) do |_inter, action|
         
     | 
| 
      
 11 
     | 
    
         
            +
              puts "PAD DOWN: #{action.inspect}"
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
            interaction.response_to(:pad, :up) do |_inter, action|
         
     | 
| 
      
 14 
     | 
    
         
            +
              puts "PAD UP: #{action.inspect}"
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
            interaction.response_to(:pad, :up, button: 1..4) do |_inter, action|
         
     | 
| 
      
 17 
     | 
    
         
            +
              puts "LEFT COLUMN PAD UP: #{action.inspect}"
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
            interaction.response_to(:pad, :up, button: 5..8, bank: 4, exclusive: true) do |_inter, action|
         
     | 
| 
      
 20 
     | 
    
         
            +
              puts "SECOND-FROM-LEFT COLUMN PAD UP: #{action.inspect}"
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            interaction.response_to(:vknob, :update) do |_inter, action|
         
     | 
| 
      
 24 
     | 
    
         
            +
              puts "ANY KNOB, ANY BANK (EXCEPT 2) TURNED: #{action.inspect}"
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
      
 26 
     | 
    
         
            +
            interaction.response_to(:vknob, :update, vknob: 3) do |_inter, action|
         
     | 
| 
      
 27 
     | 
    
         
            +
              puts "KNOB 3, ANY BANK (EXCEPT 2) TURNED: #{action.inspect}"
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
            interaction.response_to(:vknob, :update, bank: 2, exclusive: true) do |_inter, action|
         
     | 
| 
      
 30 
     | 
    
         
            +
              puts "ANY KNOB, BANK 2 TURNED: #{action.inspect}"
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
      
 32 
     | 
    
         
            +
            interaction.response_to(:vknob, :update, bank: 4, vknob: 1) do |_inter, action|
         
     | 
| 
      
 33 
     | 
    
         
            +
              puts "KNOB 1, BANK 4 TURNED: #{action.inspect}"
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            interaction.response_to(:accelerometer, :tilt) do |_inter, action|
         
     | 
| 
      
 37 
     | 
    
         
            +
              puts "ANY AXIS TILT: #{action.inspect}"
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
      
 39 
     | 
    
         
            +
            interaction.response_to(:accelerometer, :tilt, axis: :x) do |_inter, action|
         
     | 
| 
      
 40 
     | 
    
         
            +
              puts "X-AXIS TILT: #{action.inspect}"
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            interaction.response_to(:vknobs, :down) do |_inter, action|
         
     | 
| 
      
 44 
     | 
    
         
            +
              puts "ANY VKNOB SELECTOR DOWN: #{action.inspect}"
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
      
 46 
     | 
    
         
            +
            interaction.response_to(:vknobs, :down, button: 2) do |_inter, action|
         
     | 
| 
      
 47 
     | 
    
         
            +
              puts "VKNOB 2 SELECTOR DOWN: #{action.inspect}"
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            interaction.response_to(:banks, :down) do |_inter, action|
         
     | 
| 
      
 51 
     | 
    
         
            +
              puts "ANY BANK SELECTOR DOWN: #{action.inspect}"
         
     | 
| 
      
 52 
     | 
    
         
            +
            end
         
     | 
| 
      
 53 
     | 
    
         
            +
            interaction.response_to(:banks, :down, button: 3) do |_inter, action|
         
     | 
| 
      
 54 
     | 
    
         
            +
              puts "BANK 3 SELECTOR DOWN: #{action.inspect}"
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            interaction.response_to(:shoulder, :down) do |_inter, action|
         
     | 
| 
      
 58 
     | 
    
         
            +
              puts "ANY SHOULDER DOWN: #{action.inspect}"
         
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
      
 60 
     | 
    
         
            +
            interaction.response_to(:shoulder, :up) do |_inter, action|
         
     | 
| 
      
 61 
     | 
    
         
            +
              puts "ANY SHOULDER UP: #{action.inspect}"
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
| 
      
 63 
     | 
    
         
            +
            interaction.response_to(:shoulder, :down, button: :left) do |_inter, action|
         
     | 
| 
      
 64 
     | 
    
         
            +
              puts "LEFT SHOULDER DOWN: #{action.inspect}"
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
      
 66 
     | 
    
         
            +
            interaction.response_to(:shoulder, :up, button: :left) do |_inter, action|
         
     | 
| 
      
 67 
     | 
    
         
            +
              puts "LEFT SHOULDER UP: #{action.inspect}"
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
            puts "Starting input loop..."
         
     | 
| 
      
 71 
     | 
    
         
            +
            interaction.start
         
     | 
| 
         @@ -0,0 +1,135 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Cycle Numark Orbit colors through a wheel.
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # IMPORTANT: Set `MODE` below to `:wired`, or `:wireless`, as appropriate to
         
     | 
| 
      
 5 
     | 
    
         
            +
            # IMPORTANT: how your Numark Orbit is connected!
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # NOTE: If the lights do not blank out when this starts, your device is in a
         
     | 
| 
      
 8 
     | 
    
         
            +
            # NOTE: bad state.  Push a config to it from the Numark Orbit Editor.  If that
         
     | 
| 
      
 9 
     | 
    
         
            +
            # NOTE: doesn't work, power-cycle it and try again!
         
     | 
| 
      
 10 
     | 
    
         
            +
            require "rubygems"
         
     | 
| 
      
 11 
     | 
    
         
            +
            require "bundler/setup"
         
     | 
| 
      
 12 
     | 
    
         
            +
            Bundler.require(:default, :development)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            require "surface_master"
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            device = SurfaceMaster::Orbit::Device.new
         
     | 
| 
      
 17 
     | 
    
         
            +
            # The device seems to be notably less able to accept updates over wireless, and
         
     | 
| 
      
 18 
     | 
    
         
            +
            # perhaps because CoreMIDI has no backpressure, we can easily wind up hosed.
         
     | 
| 
      
 19 
     | 
    
         
            +
            # Before settling on new values here, run the process for *several full minutes*
         
     | 
| 
      
 20 
     | 
    
         
            +
            # and make sure the device continues accepting updates at the end!
         
     | 
| 
      
 21 
     | 
    
         
            +
            #
         
     | 
| 
      
 22 
     | 
    
         
            +
            # TODO: More thorough stress testing.
         
     | 
| 
      
 23 
     | 
    
         
            +
            #
         
     | 
| 
      
 24 
     | 
    
         
            +
            # TODO: Can we determine if the connection is wired/wireless automatically?
         
     | 
| 
      
 25 
     | 
    
         
            +
            #
         
     | 
| 
      
 26 
     | 
    
         
            +
            # TODO: Can we safely get input simultaneously?
         
     | 
| 
      
 27 
     | 
    
         
            +
            MODE      = :wired
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            CONFIGS   = { wireless: { delay: 0.75,  offset: 0x03, use_read: true,   read_delay: 0.1 },
         
     | 
| 
      
 30 
     | 
    
         
            +
                          wired:    { delay: 0.1,  offset: 0x01, use_read: false,  read_delay: 0 } }
         
     | 
| 
      
 31 
     | 
    
         
            +
            MAPPINGS  =  [0x03, 0x01, 0x70,
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                          0x00, 0x00, 0x00,
         
     | 
| 
      
 34 
     | 
    
         
            +
                          0x00, 0x04, 0x04,
         
     | 
| 
      
 35 
     | 
    
         
            +
                          0x00, 0x08, 0x08,
         
     | 
| 
      
 36 
     | 
    
         
            +
                          0x00, 0x0C, 0x0C,
         
     | 
| 
      
 37 
     | 
    
         
            +
                          0x00, 0x01, 0x01,
         
     | 
| 
      
 38 
     | 
    
         
            +
                          0x00, 0x05, 0x05,
         
     | 
| 
      
 39 
     | 
    
         
            +
                          0x00, 0x09, 0x09,
         
     | 
| 
      
 40 
     | 
    
         
            +
                          0x00, 0x0D, 0x0D,
         
     | 
| 
      
 41 
     | 
    
         
            +
                          0x00, 0x02, 0x02,
         
     | 
| 
      
 42 
     | 
    
         
            +
                          0x00, 0x06, 0x06,
         
     | 
| 
      
 43 
     | 
    
         
            +
                          0x00, 0x0A, 0x0A,
         
     | 
| 
      
 44 
     | 
    
         
            +
                          0x00, 0x0E, 0x0E,
         
     | 
| 
      
 45 
     | 
    
         
            +
                          0x00, 0x03, 0x03,
         
     | 
| 
      
 46 
     | 
    
         
            +
                          0x00, 0x07, 0x07,
         
     | 
| 
      
 47 
     | 
    
         
            +
                          0x00, 0x0B, 0x0B,
         
     | 
| 
      
 48 
     | 
    
         
            +
                          0x00, 0x0F, 0x0F,
         
     | 
| 
      
 49 
     | 
    
         
            +
                          0x01, 0x00, 0x10,
         
     | 
| 
      
 50 
     | 
    
         
            +
                          0x01, 0x04, 0x14,
         
     | 
| 
      
 51 
     | 
    
         
            +
                          0x01, 0x08, 0x18,
         
     | 
| 
      
 52 
     | 
    
         
            +
                          0x01, 0x0C, 0x1C,
         
     | 
| 
      
 53 
     | 
    
         
            +
                          0x01, 0x01, 0x11,
         
     | 
| 
      
 54 
     | 
    
         
            +
                          0x01, 0x05, 0x15,
         
     | 
| 
      
 55 
     | 
    
         
            +
                          0x01, 0x09, 0x19,
         
     | 
| 
      
 56 
     | 
    
         
            +
                          0x01, 0x0D, 0x1D,
         
     | 
| 
      
 57 
     | 
    
         
            +
                          0x01, 0x02, 0x12,
         
     | 
| 
      
 58 
     | 
    
         
            +
                          0x01, 0x06, 0x16,
         
     | 
| 
      
 59 
     | 
    
         
            +
                          0x01, 0x0A, 0x1A,
         
     | 
| 
      
 60 
     | 
    
         
            +
                          0x01, 0x0E, 0x1E,
         
     | 
| 
      
 61 
     | 
    
         
            +
                          0x01, 0x03, 0x13,
         
     | 
| 
      
 62 
     | 
    
         
            +
                          0x01, 0x07, 0x17,
         
     | 
| 
      
 63 
     | 
    
         
            +
                          0x01, 0x0B, 0x1B,
         
     | 
| 
      
 64 
     | 
    
         
            +
                          0x01, 0x0F, 0x1F,
         
     | 
| 
      
 65 
     | 
    
         
            +
                          0x02, 0x00, 0x20,
         
     | 
| 
      
 66 
     | 
    
         
            +
                          0x02, 0x04, 0x24,
         
     | 
| 
      
 67 
     | 
    
         
            +
                          0x02, 0x08, 0x28,
         
     | 
| 
      
 68 
     | 
    
         
            +
                          0x02, 0x0C, 0x2C,
         
     | 
| 
      
 69 
     | 
    
         
            +
                          0x02, 0x01, 0x21,
         
     | 
| 
      
 70 
     | 
    
         
            +
                          0x02, 0x05, 0x25,
         
     | 
| 
      
 71 
     | 
    
         
            +
                          0x02, 0x09, 0x29,
         
     | 
| 
      
 72 
     | 
    
         
            +
                          0x02, 0x0D, 0x2D,
         
     | 
| 
      
 73 
     | 
    
         
            +
                          0x02, 0x02, 0x22,
         
     | 
| 
      
 74 
     | 
    
         
            +
                          0x02, 0x06, 0x26,
         
     | 
| 
      
 75 
     | 
    
         
            +
                          0x02, 0x0A, 0x2A,
         
     | 
| 
      
 76 
     | 
    
         
            +
                          0x02, 0x0E, 0x2E,
         
     | 
| 
      
 77 
     | 
    
         
            +
                          0x02, 0x03, 0x23,
         
     | 
| 
      
 78 
     | 
    
         
            +
                          0x02, 0x07, 0x27,
         
     | 
| 
      
 79 
     | 
    
         
            +
                          0x02, 0x0B, 0x2B,
         
     | 
| 
      
 80 
     | 
    
         
            +
                          0x02, 0x0F, 0x2F,
         
     | 
| 
      
 81 
     | 
    
         
            +
                          0x03, 0x00, 0x30,
         
     | 
| 
      
 82 
     | 
    
         
            +
                          0x03, 0x04, 0x34,
         
     | 
| 
      
 83 
     | 
    
         
            +
                          0x03, 0x08, 0x38,
         
     | 
| 
      
 84 
     | 
    
         
            +
                          0x03, 0x0C, 0x3C,
         
     | 
| 
      
 85 
     | 
    
         
            +
                          0x03, 0x01, 0x31,
         
     | 
| 
      
 86 
     | 
    
         
            +
                          0x03, 0x05, 0x35,
         
     | 
| 
      
 87 
     | 
    
         
            +
                          0x03, 0x09, 0x39,
         
     | 
| 
      
 88 
     | 
    
         
            +
                          0x03, 0x0D, 0x3D,
         
     | 
| 
      
 89 
     | 
    
         
            +
                          0x03, 0x02, 0x32,
         
     | 
| 
      
 90 
     | 
    
         
            +
                          0x03, 0x06, 0x36,
         
     | 
| 
      
 91 
     | 
    
         
            +
                          0x03, 0x0A, 0x3A,
         
     | 
| 
      
 92 
     | 
    
         
            +
                          0x03, 0x0E, 0x3E,
         
     | 
| 
      
 93 
     | 
    
         
            +
                          0x03, 0x03, 0x33,
         
     | 
| 
      
 94 
     | 
    
         
            +
                          0x03, 0x07, 0x37,
         
     | 
| 
      
 95 
     | 
    
         
            +
                          0x03, 0x0B, 0x3B,
         
     | 
| 
      
 96 
     | 
    
         
            +
                          0x03, 0x0F, 0x3F,
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                          0x00, 0x00, 0x01,
         
     | 
| 
      
 99 
     | 
    
         
            +
                          0x00, 0x02, 0x00,
         
     | 
| 
      
 100 
     | 
    
         
            +
                          0x03, 0x00, 0x00,
         
     | 
| 
      
 101 
     | 
    
         
            +
                          0x01, 0x01, 0x01,
         
     | 
| 
      
 102 
     | 
    
         
            +
                          0x02, 0x01, 0x03,
         
     | 
| 
      
 103 
     | 
    
         
            +
                          0x01, 0x00, 0x02,
         
     | 
| 
      
 104 
     | 
    
         
            +
                          0x01, 0x02, 0x02,
         
     | 
| 
      
 105 
     | 
    
         
            +
                          0x02, 0x03, 0x02,
         
     | 
| 
      
 106 
     | 
    
         
            +
                          0x00, 0x03, 0x01,
         
     | 
| 
      
 107 
     | 
    
         
            +
                          0x03, 0x02, 0x03,
         
     | 
| 
      
 108 
     | 
    
         
            +
                          0x03, 0x03, 0x0C,
         
     | 
| 
      
 109 
     | 
    
         
            +
                          0x00, 0x0D, 0x00,
         
     | 
| 
      
 110 
     | 
    
         
            +
                          0x0C, 0x00, 0x0D,
         
     | 
| 
      
 111 
     | 
    
         
            +
                          0x00, 0x0C, 0x00,
         
     | 
| 
      
 112 
     | 
    
         
            +
                          0x0D, 0x00, 0x0C,
         
     | 
| 
      
 113 
     | 
    
         
            +
                          0x00, 0x0D, 0x00]
         
     | 
| 
      
 114 
     | 
    
         
            +
            READ_STATE = [0x01, 0x00, 0x00]
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
            delay       = CONFIGS[MODE][:delay]
         
     | 
| 
      
 117 
     | 
    
         
            +
            offset      = CONFIGS[MODE][:offset]
         
     | 
| 
      
 118 
     | 
    
         
            +
            use_read    = CONFIGS[MODE][:use_read]
         
     | 
| 
      
 119 
     | 
    
         
            +
            read_delay  = CONFIGS[MODE][:read_delay]
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
            sleep delay
         
     | 
| 
      
 122 
     | 
    
         
            +
            puts "Starting..."
         
     | 
| 
      
 123 
     | 
    
         
            +
            indices = (0..63).map { |n| 5 + (3 * n) }
         
     | 
| 
      
 124 
     | 
    
         
            +
            loop do
         
     | 
| 
      
 125 
     | 
    
         
            +
              indices.each do |i|
         
     | 
| 
      
 126 
     | 
    
         
            +
                MAPPINGS[i] = (MAPPINGS[i] + offset) % 0x3F
         
     | 
| 
      
 127 
     | 
    
         
            +
              end
         
     | 
| 
      
 128 
     | 
    
         
            +
              device.sysex!(*MAPPINGS)
         
     | 
| 
      
 129 
     | 
    
         
            +
              if use_read
         
     | 
| 
      
 130 
     | 
    
         
            +
                sleep read_delay
         
     | 
| 
      
 131 
     | 
    
         
            +
                device.sysex!(*READ_STATE)
         
     | 
| 
      
 132 
     | 
    
         
            +
              end
         
     | 
| 
      
 133 
     | 
    
         
            +
              printf "."
         
     | 
| 
      
 134 
     | 
    
         
            +
              sleep delay
         
     | 
| 
      
 135 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -39,14 +39,14 @@ end 
     | 
|
| 
       39 
39 
     | 
    
         | 
| 
       40 
40 
     | 
    
         
             
            interaction = SurfaceMaster::Launchpad::Interaction.new
         
     | 
| 
       41 
41 
     | 
    
         
             
            cpu_bar     = Bar.new(interaction, 0, red: 0x3F, green: 0x00, blue: 0x00)
         
     | 
| 
       42 
     | 
    
         
            -
            io_bar      = Bar.new(interaction,  
     | 
| 
      
 42 
     | 
    
         
            +
            io_bar      = Bar.new(interaction, 1, red: 0x00, green: 0x3F, blue: 0x00)
         
     | 
| 
       43 
43 
     | 
    
         
             
            monitor     = Thread.new do
         
     | 
| 
       44 
44 
     | 
    
         
             
              loop do
         
     | 
| 
       45 
45 
     | 
    
         
             
                fields      = `iostat -c 2 disk0`.split(/\n/).last.strip.split(/\s+/)
         
     | 
| 
       46 
46 
     | 
    
         
             
                cpu_pct     = 100 - fields[-4].to_i
         
     | 
| 
       47 
47 
     | 
    
         
             
                cpu_usage   = ((cpu_pct / 100.0) * 8.0).round.to_i
         
     | 
| 
       48 
48 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                disk_pct    = (fields[2].to_f / 750.0) * 100.0
         
     | 
| 
      
 49 
     | 
    
         
            +
                disk_pct    = ((fields[2].to_f / 750.0) * 100.0).round.to_i
         
     | 
| 
       50 
50 
     | 
    
         
             
                disk_usage  = ((disk_pct / 100.0) * 8.0).round.to_i
         
     | 
| 
       51 
51 
     | 
    
         | 
| 
       52 
52 
     | 
    
         
             
                puts "I/O=#{disk_pct}%, CPU=#{cpu_pct}%"
         
     | 
| 
         @@ -51,10 +51,13 @@ module SurfaceMaster 
     | 
|
| 
       51 
51 
     | 
    
         
             
                  fail NoOutputAllowedError unless output_enabled?
         
     | 
| 
       52 
52 
     | 
    
         
             
                  msg = sysex_msg(payload)
         
     | 
| 
       53 
53 
     | 
    
         
             
                  logger.debug { "#{msg.length}: 0x#{msg.map { |b| '%02X' % b }.join(' ')}" }
         
     | 
| 
       54 
     | 
    
         
            -
                  @output.write_sysex(msg)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  result = @output.write_sysex(msg)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  if result != 0
         
     | 
| 
      
 56 
     | 
    
         
            +
                    puts "Sysex Error: #{Portmidi::PM_Map.Pm_GetErrorText(result)}"
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  result
         
     | 
| 
       55 
59 
     | 
    
         
             
                end
         
     | 
| 
       56 
60 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
61 
     | 
    
         
             
                def create_input_device(opts)
         
     | 
| 
       59 
62 
     | 
    
         
             
                  return nil unless opts[:input]
         
     | 
| 
       60 
63 
     | 
    
         
             
                  create_device(Portmidi.input_devices,
         
     | 
| 
         @@ -12,14 +12,11 @@ module SurfaceMaster 
     | 
|
| 
       12 
12 
     | 
    
         
             
                  self.logger = opts[:logger]
         
     | 
| 
       13 
13 
     | 
    
         
             
                  logger.debug "Initializing #{self.class}##{object_id} with #{opts.inspect}"
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                  @use_threads  = opts[:use_threads] || true
         
     | 
| 
       16 
15 
     | 
    
         
             
                  @device       = opts[:device] || @device_class.new(opts.merge(input: true,
         
     | 
| 
       17 
16 
     | 
    
         
             
                                                                                output: true,
         
     | 
| 
       18 
17 
     | 
    
         
             
                                                                                logger: opts[:logger]))
         
     | 
| 
       19 
18 
     | 
    
         
             
                  @latency      = (opts[:latency] || 0.001).to_f.abs
         
     | 
| 
       20 
19 
     | 
    
         
             
                  @active       = false
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                  @action_threads = ThreadGroup.new
         
     | 
| 
       23 
20 
     | 
    
         
             
                end
         
     | 
| 
       24 
21 
     | 
    
         | 
| 
       25 
22 
     | 
    
         
             
                def change(opts); @device.change(opts); end
         
     | 
| 
         @@ -33,36 +30,29 @@ module SurfaceMaster 
     | 
|
| 
       33 
30 
     | 
    
         | 
| 
       34 
31 
     | 
    
         
             
                def closed?; @device.closed?; end
         
     | 
| 
       35 
32 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
                def start 
     | 
| 
      
 33 
     | 
    
         
            +
                def start
         
     | 
| 
       37 
34 
     | 
    
         
             
                  logger.debug "Starting #{self.class}##{object_id}"
         
     | 
| 
       38 
35 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
                   
     | 
| 
       40 
     | 
    
         
            -
                   
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
      
 36 
     | 
    
         
            +
                  @active = true
         
     | 
| 
      
 37 
     | 
    
         
            +
                  guard_input_and_reset_at_end! do
         
     | 
| 
      
 38 
     | 
    
         
            +
                    while @active
         
     | 
| 
      
 39 
     | 
    
         
            +
                      @device.read.each { |action| respond_to_action(action) }
         
     | 
| 
      
 40 
     | 
    
         
            +
                      sleep @latency if @latency && @latency > 0.0
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
       44 
43 
     | 
    
         
             
                end
         
     | 
| 
       45 
44 
     | 
    
         | 
| 
       46 
45 
     | 
    
         
             
                def stop
         
     | 
| 
       47 
46 
     | 
    
         
             
                  logger.debug "Stopping #{self.class}##{object_id}"
         
     | 
| 
       48 
47 
     | 
    
         
             
                  @active = false
         
     | 
| 
       49 
     | 
    
         
            -
                  if @reader_thread
         
     | 
| 
       50 
     | 
    
         
            -
                    # run (resume from sleep) and wait for @reader_thread to end
         
     | 
| 
       51 
     | 
    
         
            -
                    @reader_thread.run if @reader_thread.alive?
         
     | 
| 
       52 
     | 
    
         
            -
                    @reader_thread.join
         
     | 
| 
       53 
     | 
    
         
            -
                    @reader_thread = nil
         
     | 
| 
       54 
     | 
    
         
            -
                  end
         
     | 
| 
       55 
     | 
    
         
            -
                ensure
         
     | 
| 
       56 
     | 
    
         
            -
                  kill_action_threads!
         
     | 
| 
       57 
     | 
    
         
            -
                  nil
         
     | 
| 
       58 
48 
     | 
    
         
             
                end
         
     | 
| 
       59 
49 
     | 
    
         | 
| 
       60 
50 
     | 
    
         
             
                def response_to(types = :all, state = :both, opts = nil, &block)
         
     | 
| 
       61 
51 
     | 
    
         
             
                  logger.debug "Setting response to #{types.inspect} for state #{state.inspect} with"\
         
     | 
| 
       62 
52 
     | 
    
         
             
                    " #{opts.inspect}"
         
     | 
| 
       63 
     | 
    
         
            -
                  types 
     | 
| 
       64 
     | 
    
         
            -
                  opts 
     | 
| 
       65 
     | 
    
         
            -
                  no_response_to(types, state) if opts[:exclusive] == true
         
     | 
| 
      
 53 
     | 
    
         
            +
                  types   = Array(types)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  opts  ||= {}
         
     | 
| 
      
 55 
     | 
    
         
            +
                  no_response_to(types, state, opts) if opts[:exclusive] == true
         
     | 
| 
       66 
56 
     | 
    
         
             
                  expand_states(state).each do |st|
         
     | 
| 
       67 
57 
     | 
    
         
             
                    add_response_for_state!(types, opts, st, block)
         
     | 
| 
       68 
58 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -71,7 +61,8 @@ module SurfaceMaster 
     | 
|
| 
       71 
61 
     | 
    
         | 
| 
       72 
62 
     | 
    
         
             
                def no_response_to(types = nil, state = :both, opts = nil)
         
     | 
| 
       73 
63 
     | 
    
         
             
                  logger.debug "Removing response to #{types.inspect} for state #{state.inspect}"
         
     | 
| 
       74 
     | 
    
         
            -
                  types 
     | 
| 
      
 64 
     | 
    
         
            +
                  types   = Array(types)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  opts  ||= {}
         
     | 
| 
       75 
66 
     | 
    
         
             
                  expand_states(state).each do |st|
         
     | 
| 
       76 
67 
     | 
    
         
             
                    clear_responses_for_state!(types, opts, st)
         
     | 
| 
       77 
68 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -84,15 +75,8 @@ module SurfaceMaster 
     | 
|
| 
       84 
75 
     | 
    
         | 
| 
       85 
76 
     | 
    
         
             
              protected
         
     | 
| 
       86 
77 
     | 
    
         | 
| 
       87 
     | 
    
         
            -
                def  
     | 
| 
       88 
     | 
    
         
            -
                   
     | 
| 
       89 
     | 
    
         
            -
                    guard_input_and_reset_at_end! do
         
     | 
| 
       90 
     | 
    
         
            -
                      while @active
         
     | 
| 
       91 
     | 
    
         
            -
                        @device.read.each { |action| handle_action(action) }
         
     | 
| 
       92 
     | 
    
         
            -
                        sleep @latency if @latency && @latency > 0.0
         
     | 
| 
       93 
     | 
    
         
            -
                      end
         
     | 
| 
       94 
     | 
    
         
            -
                    end
         
     | 
| 
       95 
     | 
    
         
            -
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                def expand(list)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  list.map { |ll| ll.respond_to?(:to_a) ? ll.to_a : ll }.flatten
         
     | 
| 
       96 
80 
     | 
    
         
             
                end
         
     | 
| 
       97 
81 
     | 
    
         | 
| 
       98 
82 
     | 
    
         
             
                def guard_input_and_reset_at_end!(&block)
         
     | 
| 
         @@ -107,17 +91,6 @@ module SurfaceMaster 
     | 
|
| 
       107 
91 
     | 
    
         
             
                  @device.reset!
         
     | 
| 
       108 
92 
     | 
    
         
             
                end
         
     | 
| 
       109 
93 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
                def kill_action_threads!
         
     | 
| 
       111 
     | 
    
         
            -
                  @action_threads.list.each do |thread|
         
     | 
| 
       112 
     | 
    
         
            -
                    begin
         
     | 
| 
       113 
     | 
    
         
            -
                      thread.kill
         
     | 
| 
       114 
     | 
    
         
            -
                      thread.join
         
     | 
| 
       115 
     | 
    
         
            -
                    rescue StandardException => e # TODO: RuntimeError, Exception, or this?
         
     | 
| 
       116 
     | 
    
         
            -
                      logger.error "Error when killing action thread: #{e.inspect}"
         
     | 
| 
       117 
     | 
    
         
            -
                    end
         
     | 
| 
       118 
     | 
    
         
            -
                  end
         
     | 
| 
       119 
     | 
    
         
            -
                end
         
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
94 
     | 
    
         
             
                def add_response_for_state!(types, opts, state, block)
         
     | 
| 
       122 
95 
     | 
    
         
             
                  response_groups_for(types, opts, state) do |responses|
         
     | 
| 
       123 
96 
     | 
    
         
             
                    responses << block
         
     | 
| 
         @@ -138,17 +111,6 @@ module SurfaceMaster 
     | 
|
| 
       138 
111 
     | 
    
         | 
| 
       139 
112 
     | 
    
         
             
                def expand_states(state); Array(state == :both ? %i(down up) : state); end
         
     | 
| 
       140 
113 
     | 
    
         | 
| 
       141 
     | 
    
         
            -
                def handle_action(action)
         
     | 
| 
       142 
     | 
    
         
            -
                  if @use_threads
         
     | 
| 
       143 
     | 
    
         
            -
                    action_thread = Thread.new(action) do |act|
         
     | 
| 
       144 
     | 
    
         
            -
                      respond_to_action(act)
         
     | 
| 
       145 
     | 
    
         
            -
                    end
         
     | 
| 
       146 
     | 
    
         
            -
                    @action_threads.add(action_thread)
         
     | 
| 
       147 
     | 
    
         
            -
                  else
         
     | 
| 
       148 
     | 
    
         
            -
                    respond_to_action(action)
         
     | 
| 
       149 
     | 
    
         
            -
                  end
         
     | 
| 
       150 
     | 
    
         
            -
                end
         
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
114 
     | 
    
         
             
                def responses
         
     | 
| 
       153 
115 
     | 
    
         
             
                  # TODO: Generalize for arbitrary actions...
         
     | 
| 
       154 
116 
     | 
    
         
             
                  @responses ||= Hash.new { |hash, key| hash[key] = { down: [], up: [] } }
         
     | 
| 
         @@ -11,11 +11,52 @@ module SurfaceMaster 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  end
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                  def reset!
         
     | 
| 
       14 
     | 
    
         
            -
                    #  
     | 
| 
       15 
     | 
    
         
            -
                     
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
                    # Skip Sysex begin, vendor header, command code, aaaaand sysex end --
         
     | 
| 
      
 15 
     | 
    
         
            +
                    # this will let us compare command vs. response payloads to determine
         
     | 
| 
      
 16 
     | 
    
         
            +
                    # if the state of the device is what we want.  Of course, sometimes it
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # lies, but we can't do much about that.
         
     | 
| 
      
 18 
     | 
    
         
            +
                    expected_state = MAPPINGS[6..-2]
         
     | 
| 
      
 19 
     | 
    
         
            +
                    sysex!(MAPPINGS)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    sleep 0.1
         
     | 
| 
      
 21 
     | 
    
         
            +
                    sysex!(READ_STATE)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    current_state = nil
         
     | 
| 
      
 23 
     | 
    
         
            +
                    started_at    = Time.now.to_f
         
     | 
| 
      
 24 
     | 
    
         
            +
                    attempts      = 1
         
     | 
| 
      
 25 
     | 
    
         
            +
                    loop do
         
     | 
| 
      
 26 
     | 
    
         
            +
                      # TODO: It appears that accessing `buffer` is HIGHLY unsafe!  We may
         
     | 
| 
      
 27 
     | 
    
         
            +
                      # TODO: be OK if everyone's waiting on us to come back from this
         
     | 
| 
      
 28 
     | 
    
         
            +
                      # TODO: method before they begin clamoring for input, but that's just
         
     | 
| 
      
 29 
     | 
    
         
            +
                      # TODO: a guess right now.
         
     | 
| 
      
 30 
     | 
    
         
            +
                      if @input.buffer.length == 0
         
     | 
| 
      
 31 
     | 
    
         
            +
                        elapsed = Time.now.to_f - started_at
         
     | 
| 
      
 32 
     | 
    
         
            +
                        if elapsed > 4.0
         
     | 
| 
      
 33 
     | 
    
         
            +
                          logger.warn { "Timeout fetching state of Numark Orbit!" }
         
     | 
| 
      
 34 
     | 
    
         
            +
                          break
         
     | 
| 
      
 35 
     | 
    
         
            +
                        elsif elapsed > (1.0 * attempts)
         
     | 
| 
      
 36 
     | 
    
         
            +
                          logger.warn { "Asking for current state of Numark Orbit again!" }
         
     | 
| 
      
 37 
     | 
    
         
            +
                          attempts += 1
         
     | 
| 
      
 38 
     | 
    
         
            +
                          @output.puts(READ_STATE)
         
     | 
| 
      
 39 
     | 
    
         
            +
                          next
         
     | 
| 
      
 40 
     | 
    
         
            +
                        end
         
     | 
| 
      
 41 
     | 
    
         
            +
                        sleep 0.01
         
     | 
| 
      
 42 
     | 
    
         
            +
                        next
         
     | 
| 
      
 43 
     | 
    
         
            +
                      end
         
     | 
| 
      
 44 
     | 
    
         
            +
                      raw             = @input.gets
         
     | 
| 
      
 45 
     | 
    
         
            +
                      current_state   = raw.find { |ii| ii[:data][0] == 0xF0 }
         
     | 
| 
      
 46 
     | 
    
         
            +
                      break unless current_state.nil?
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    return unless current_state
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    current_state = current_state[:data][6..-2].dup
         
     | 
| 
      
 52 
     | 
    
         
            +
                    logger.debug { "Got state info from Numark Orbit!" }
         
     | 
| 
      
 53 
     | 
    
         
            +
                    if expected_state != current_state
         
     | 
| 
      
 54 
     | 
    
         
            +
                      logger.error { "UH OH!  Numark Orbit state didn't match what we sent!" }
         
     | 
| 
      
 55 
     | 
    
         
            +
                      logger.error { "Expected: #{expected_state.inspect}" }
         
     | 
| 
      
 56 
     | 
    
         
            +
                      logger.error { "Got:      #{current_state.inspect}" }
         
     | 
| 
      
 57 
     | 
    
         
            +
                    else
         
     | 
| 
      
 58 
     | 
    
         
            +
                      logger.debug { "Your Numark Orbit should be in the right state now." }
         
     | 
| 
       17 
59 
     | 
    
         
             
                    end
         
     | 
| 
       18 
     | 
    
         
            -
                    sysex!(0x01, 0x00, 0x00)
         
     | 
| 
       19 
60 
     | 
    
         
             
                  end
         
     | 
| 
       20 
61 
     | 
    
         | 
| 
       21 
62 
     | 
    
         
             
                  def read
         
     | 
| 
         @@ -26,133 +67,140 @@ module SurfaceMaster 
     | 
|
| 
       26 
67 
     | 
    
         | 
| 
       27 
68 
     | 
    
         
             
                protected
         
     | 
| 
       28 
69 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                  MAPPINGS = 
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
      
 70 
     | 
    
         
            +
                  MAPPINGS =   [0x03, 0x01, 0x70,
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                                0x00, 0x00, 0x00,
         
     | 
| 
      
 73 
     | 
    
         
            +
                                0x00, 0x04, 0x00,
         
     | 
| 
      
 74 
     | 
    
         
            +
                                0x00, 0x08, 0x00,
         
     | 
| 
      
 75 
     | 
    
         
            +
                                0x00, 0x0C, 0x00,
         
     | 
| 
      
 76 
     | 
    
         
            +
                                0x00, 0x01, 0x00,
         
     | 
| 
      
 77 
     | 
    
         
            +
                                0x00, 0x05, 0x00,
         
     | 
| 
      
 78 
     | 
    
         
            +
                                0x00, 0x09, 0x00,
         
     | 
| 
      
 79 
     | 
    
         
            +
                                0x00, 0x0D, 0x00,
         
     | 
| 
      
 80 
     | 
    
         
            +
                                0x00, 0x02, 0x00,
         
     | 
| 
      
 81 
     | 
    
         
            +
                                0x00, 0x06, 0x00,
         
     | 
| 
      
 82 
     | 
    
         
            +
                                0x00, 0x0A, 0x00,
         
     | 
| 
      
 83 
     | 
    
         
            +
                                0x00, 0x0E, 0x00,
         
     | 
| 
      
 84 
     | 
    
         
            +
                                0x00, 0x03, 0x00,
         
     | 
| 
      
 85 
     | 
    
         
            +
                                0x00, 0x07, 0x00,
         
     | 
| 
      
 86 
     | 
    
         
            +
                                0x00, 0x0B, 0x00,
         
     | 
| 
      
 87 
     | 
    
         
            +
                                0x00, 0x0F, 0x00,
         
     | 
| 
      
 88 
     | 
    
         
            +
                                0x01, 0x00, 0x00,
         
     | 
| 
      
 89 
     | 
    
         
            +
                                0x01, 0x04, 0x00,
         
     | 
| 
      
 90 
     | 
    
         
            +
                                0x01, 0x08, 0x00,
         
     | 
| 
      
 91 
     | 
    
         
            +
                                0x01, 0x0C, 0x00,
         
     | 
| 
      
 92 
     | 
    
         
            +
                                0x01, 0x01, 0x00,
         
     | 
| 
      
 93 
     | 
    
         
            +
                                0x01, 0x05, 0x00,
         
     | 
| 
      
 94 
     | 
    
         
            +
                                0x01, 0x09, 0x00,
         
     | 
| 
      
 95 
     | 
    
         
            +
                                0x01, 0x0D, 0x00,
         
     | 
| 
      
 96 
     | 
    
         
            +
                                0x01, 0x02, 0x00,
         
     | 
| 
      
 97 
     | 
    
         
            +
                                0x01, 0x06, 0x00,
         
     | 
| 
      
 98 
     | 
    
         
            +
                                0x01, 0x0A, 0x00,
         
     | 
| 
      
 99 
     | 
    
         
            +
                                0x01, 0x0E, 0x00,
         
     | 
| 
      
 100 
     | 
    
         
            +
                                0x01, 0x03, 0x00,
         
     | 
| 
      
 101 
     | 
    
         
            +
                                0x01, 0x07, 0x00,
         
     | 
| 
      
 102 
     | 
    
         
            +
                                0x01, 0x0B, 0x00,
         
     | 
| 
      
 103 
     | 
    
         
            +
                                0x01, 0x0F, 0x00,
         
     | 
| 
      
 104 
     | 
    
         
            +
                                0x02, 0x00, 0x00,
         
     | 
| 
      
 105 
     | 
    
         
            +
                                0x02, 0x04, 0x00,
         
     | 
| 
      
 106 
     | 
    
         
            +
                                0x02, 0x08, 0x00,
         
     | 
| 
      
 107 
     | 
    
         
            +
                                0x02, 0x0C, 0x00,
         
     | 
| 
      
 108 
     | 
    
         
            +
                                0x02, 0x01, 0x00,
         
     | 
| 
      
 109 
     | 
    
         
            +
                                0x02, 0x05, 0x00,
         
     | 
| 
      
 110 
     | 
    
         
            +
                                0x02, 0x09, 0x00,
         
     | 
| 
      
 111 
     | 
    
         
            +
                                0x02, 0x0D, 0x00,
         
     | 
| 
      
 112 
     | 
    
         
            +
                                0x02, 0x02, 0x00,
         
     | 
| 
      
 113 
     | 
    
         
            +
                                0x02, 0x06, 0x00,
         
     | 
| 
      
 114 
     | 
    
         
            +
                                0x02, 0x0A, 0x00,
         
     | 
| 
      
 115 
     | 
    
         
            +
                                0x02, 0x0E, 0x00,
         
     | 
| 
      
 116 
     | 
    
         
            +
                                0x02, 0x03, 0x00,
         
     | 
| 
      
 117 
     | 
    
         
            +
                                0x02, 0x07, 0x00,
         
     | 
| 
      
 118 
     | 
    
         
            +
                                0x02, 0x0B, 0x00,
         
     | 
| 
      
 119 
     | 
    
         
            +
                                0x02, 0x0F, 0x00,
         
     | 
| 
      
 120 
     | 
    
         
            +
                                0x03, 0x00, 0x00,
         
     | 
| 
      
 121 
     | 
    
         
            +
                                0x03, 0x04, 0x00,
         
     | 
| 
      
 122 
     | 
    
         
            +
                                0x03, 0x08, 0x00,
         
     | 
| 
      
 123 
     | 
    
         
            +
                                0x03, 0x0C, 0x00,
         
     | 
| 
      
 124 
     | 
    
         
            +
                                0x03, 0x01, 0x00,
         
     | 
| 
      
 125 
     | 
    
         
            +
                                0x03, 0x05, 0x00,
         
     | 
| 
      
 126 
     | 
    
         
            +
                                0x03, 0x09, 0x00,
         
     | 
| 
      
 127 
     | 
    
         
            +
                                0x03, 0x0D, 0x00,
         
     | 
| 
      
 128 
     | 
    
         
            +
                                0x03, 0x02, 0x00,
         
     | 
| 
      
 129 
     | 
    
         
            +
                                0x03, 0x06, 0x00,
         
     | 
| 
      
 130 
     | 
    
         
            +
                                0x03, 0x0A, 0x00,
         
     | 
| 
      
 131 
     | 
    
         
            +
                                0x03, 0x0E, 0x00,
         
     | 
| 
      
 132 
     | 
    
         
            +
                                0x03, 0x03, 0x00,
         
     | 
| 
      
 133 
     | 
    
         
            +
                                0x03, 0x07, 0x00,
         
     | 
| 
      
 134 
     | 
    
         
            +
                                0x03, 0x0B, 0x00,
         
     | 
| 
      
 135 
     | 
    
         
            +
                                0x03, 0x0F, 0x00,
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                                0x00, 0x00, 0x01,
         
     | 
| 
      
 138 
     | 
    
         
            +
                                0x00, 0x02, 0x00,
         
     | 
| 
      
 139 
     | 
    
         
            +
                                0x03, 0x00, 0x00,
         
     | 
| 
      
 140 
     | 
    
         
            +
                                0x01, 0x01, 0x01,
         
     | 
| 
      
 141 
     | 
    
         
            +
                                0x02, 0x01, 0x03,
         
     | 
| 
      
 142 
     | 
    
         
            +
                                0x01, 0x00, 0x02,
         
     | 
| 
      
 143 
     | 
    
         
            +
                                0x01, 0x02, 0x02,
         
     | 
| 
      
 144 
     | 
    
         
            +
                                0x02, 0x03, 0x02,
         
     | 
| 
      
 145 
     | 
    
         
            +
                                0x00, 0x03, 0x01,
         
     | 
| 
      
 146 
     | 
    
         
            +
                                0x03, 0x02, 0x03,
         
     | 
| 
      
 147 
     | 
    
         
            +
                                0x03, 0x03, 0x0C,
         
     | 
| 
      
 148 
     | 
    
         
            +
                                0x00, 0x0D, 0x00,
         
     | 
| 
      
 149 
     | 
    
         
            +
                                0x0C, 0x00, 0x0D,
         
     | 
| 
      
 150 
     | 
    
         
            +
                                0x00, 0x0C, 0x00,
         
     | 
| 
      
 151 
     | 
    
         
            +
                                0x0D, 0x00, 0x0C,
         
     | 
| 
      
 152 
     | 
    
         
            +
                                0x00, 0x0D, 0x00]
         
     | 
| 
      
 153 
     | 
    
         
            +
                  READ_STATE = [0x01, 0x00, 0x00]
         
     | 
| 
       110 
154 
     | 
    
         | 
| 
       111 
155 
     | 
    
         
             
                  def sysex_prefix; @sysex_prefix ||= super + [0x00, 0x01, 0x3F, 0x2B]; end
         
     | 
| 
       112 
156 
     | 
    
         | 
| 
       113 
157 
     | 
    
         
             
                  def decode_shoulder(decoded, note, _velocity)
         
     | 
| 
       114 
     | 
    
         
            -
                    decoded[:control].merge 
     | 
| 
      
 158 
     | 
    
         
            +
                    decoded[:control] = decoded[:control].merge(SurfaceMaster::Orbit::Device::SHOULDERS[note])
         
     | 
| 
       115 
159 
     | 
    
         
             
                    decoded
         
     | 
| 
       116 
160 
     | 
    
         
             
                  end
         
     | 
| 
       117 
161 
     | 
    
         | 
| 
       118 
162 
     | 
    
         
             
                  def decode_pad(decoded, note, _velocity)
         
     | 
| 
       119 
     | 
    
         
            -
                    decoded[:control][:button 
     | 
| 
      
 163 
     | 
    
         
            +
                    decoded[:control] = decoded[:control].merge(button: note + 1)
         
     | 
| 
       120 
164 
     | 
    
         
             
                    decoded
         
     | 
| 
       121 
165 
     | 
    
         
             
                  end
         
     | 
| 
       122 
166 
     | 
    
         | 
| 
       123 
167 
     | 
    
         
             
                  def decode_knob(decoded, note, velocity)
         
     | 
| 
       124 
     | 
    
         
            -
                    decoded[:control][:bank 
     | 
| 
       125 
     | 
    
         
            -
                    decoded[:value] 
     | 
| 
      
 168 
     | 
    
         
            +
                    decoded[:control] = decoded[:control].merge(bank: note + 1)
         
     | 
| 
      
 169 
     | 
    
         
            +
                    decoded[:value]   = velocity
         
     | 
| 
       126 
170 
     | 
    
         
             
                    decoded
         
     | 
| 
       127 
171 
     | 
    
         
             
                  end
         
     | 
| 
       128 
172 
     | 
    
         | 
| 
       129 
173 
     | 
    
         
             
                  def decode_control(decoded, note, velocity)
         
     | 
| 
       130 
     | 
    
         
            -
                     
     | 
| 
       131 
     | 
    
         
            -
                     
     | 
| 
       132 
     | 
    
         
            -
                    decoded 
     | 
| 
      
 174 
     | 
    
         
            +
                    decoded           = decoded.merge(SurfaceMaster::Orbit::Device::SELECTORS[note])
         
     | 
| 
      
 175 
     | 
    
         
            +
                    decoded[:control] = { button: velocity }
         
     | 
| 
      
 176 
     | 
    
         
            +
                    decoded
         
     | 
| 
      
 177 
     | 
    
         
            +
                  end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                  def decode_accelerometer(decoded, _note, velocity)
         
     | 
| 
      
 180 
     | 
    
         
            +
                    decoded[:value] = velocity
         
     | 
| 
       133 
181 
     | 
    
         
             
                    decoded
         
     | 
| 
       134 
182 
     | 
    
         
             
                  end
         
     | 
| 
       135 
183 
     | 
    
         | 
| 
       136 
184 
     | 
    
         
             
                  def enrich_decoded_message(decoded, note, velocity, timestamp)
         
     | 
| 
       137 
185 
     | 
    
         
             
                    case decoded[:type]
         
     | 
| 
       138 
     | 
    
         
            -
                    when :shoulder 
     | 
| 
       139 
     | 
    
         
            -
                    when :pad 
     | 
| 
       140 
     | 
    
         
            -
                    when : 
     | 
| 
       141 
     | 
    
         
            -
                    when : 
     | 
| 
      
 186 
     | 
    
         
            +
                    when :shoulder      then decoded = decode_shoulder(decoded, note, velocity)
         
     | 
| 
      
 187 
     | 
    
         
            +
                    when :pad           then decoded = decode_pad(decoded, note, velocity)
         
     | 
| 
      
 188 
     | 
    
         
            +
                    when :vknob         then decoded = decode_knob(decoded, note, velocity)
         
     | 
| 
      
 189 
     | 
    
         
            +
                    when :accelerometer then decoded = decode_accelerometer(decoded, note, velocity)
         
     | 
| 
      
 190 
     | 
    
         
            +
                    else decoded = decode_control(decoded, note, velocity)
         
     | 
| 
       142 
191 
     | 
    
         
             
                    end
         
     | 
| 
       143 
192 
     | 
    
         
             
                    decoded[:timestamp] = timestamp
         
     | 
| 
       144 
193 
     | 
    
         
             
                    decoded
         
     | 
| 
       145 
194 
     | 
    
         
             
                  end
         
     | 
| 
       146 
195 
     | 
    
         | 
| 
       147 
196 
     | 
    
         
             
                  def decode_input(input)
         
     | 
| 
       148 
     | 
    
         
            -
            # puts [input[:code].to_hex, input[:note].to_hex, input[:velocity].to_hex].join(" ")
         
     | 
| 
       149 
197 
     | 
    
         
             
                    note      = input[:note]
         
     | 
| 
       150 
198 
     | 
    
         
             
                    velocity  = input[:velocity]
         
     | 
| 
       151 
199 
     | 
    
         
             
                    code_high = input[:code] & 0xF0
         
     | 
| 
       152 
200 
     | 
    
         
             
                    code_low  = input[:code] & 0x0F
         
     | 
| 
       153 
201 
     | 
    
         
             
                    raw       = SurfaceMaster::Orbit::Device::CONTROLS[code_high]
         
     | 
| 
       154 
202 
     | 
    
         
             
                    raw       = raw[code_low] if raw
         
     | 
| 
       155 
     | 
    
         
            -
                    raw       = enrich_decoded_message(raw, note, velocity, input[:timestamp]) if raw
         
     | 
| 
      
 203 
     | 
    
         
            +
                    raw       = enrich_decoded_message(raw.dup, note, velocity, input[:timestamp]) if raw
         
     | 
| 
       156 
204 
     | 
    
         
             
                    raw
         
     | 
| 
       157 
205 
     | 
    
         
             
                  end
         
     | 
| 
       158 
206 
     | 
    
         
             
                end
         
     | 
| 
         @@ -7,24 +7,74 @@ module SurfaceMaster 
     | 
|
| 
       7 
7 
     | 
    
         
             
                    super(opts)
         
     | 
| 
       8 
8 
     | 
    
         
             
                  end
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                 
     | 
| 
      
 10 
     | 
    
         
            +
                protected
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
                  def combined_types(type, opts = nil)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    tmp = case type
         
     | 
| 
      
 14 
     | 
    
         
            +
                          when :shoulder
         
     | 
| 
      
 15 
     | 
    
         
            +
                            [:"#{type}-#{opts[:button]}"]
         
     | 
| 
      
 16 
     | 
    
         
            +
                          when :accelerometer
         
     | 
| 
      
 17 
     | 
    
         
            +
                            [:"#{type}-#{opts[:axis]}"]
         
     | 
| 
      
 18 
     | 
    
         
            +
                          when :vknob
         
     | 
| 
      
 19 
     | 
    
         
            +
                            knobs = opts[:vknob].nil? ? [1..4] : [opts[:vknob]]
         
     | 
| 
      
 20 
     | 
    
         
            +
                            banks = opts[:bank].nil? ? [1..4] : [opts[:bank]]
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                            expand(knobs).product(expand(banks)).map { |k, b| :"#{type}-#{k}-#{b}" }
         
     | 
| 
      
 23 
     | 
    
         
            +
                          when :vknobs, :banks
         
     | 
| 
      
 24 
     | 
    
         
            +
                            buttons = opts[:button].nil? ? [1..4] : [opts[:button]]
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                            expand(buttons).map { |b| [:"#{type}-#{b}"] }
         
     | 
| 
      
 27 
     | 
    
         
            +
                          when :pad
         
     | 
| 
      
 28 
     | 
    
         
            +
                            banks   = opts[:bank].nil? ? [1..4] : [opts[:bank]]
         
     | 
| 
      
 29 
     | 
    
         
            +
                            buttons = opts[:button].nil? ? [1..16] : [opts[:button]]
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                            expand(buttons).product(expand(banks)).map { |p, b| :"#{type}-#{p}-#{b}" }
         
     | 
| 
      
 32 
     | 
    
         
            +
                          else
         
     | 
| 
      
 33 
     | 
    
         
            +
                            [type]
         
     | 
| 
      
 34 
     | 
    
         
            +
                          end
         
     | 
| 
      
 35 
     | 
    
         
            +
                    tmp.flatten.compact
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def responses_hash
         
     | 
| 
      
 39 
     | 
    
         
            +
                    { down:   [],
         
     | 
| 
      
 40 
     | 
    
         
            +
                      up:     [],
         
     | 
| 
      
 41 
     | 
    
         
            +
                      update: [],
         
     | 
| 
      
 42 
     | 
    
         
            +
                      tilt:   [] }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def responses
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @responses ||= Hash.new { |hash, key| hash[key] = responses_hash }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  # TODO: Allow catching ranges of pads...
         
     | 
| 
      
 50 
     | 
    
         
            +
                  #
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # TODO: Allow differentiating on bank/vknob/shoulder button...
         
     | 
| 
       12 
52 
     | 
    
         
             
                  def respond_to_action(action)
         
     | 
| 
       13 
     | 
    
         
            -
                     
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                     
     | 
| 
       16 
     | 
    
         
            -
                     
     | 
| 
       17 
     | 
    
         
            -
                    #   actions += responses[:"grid#{action[:x]}#{action[:y]}"][state]
         
     | 
| 
       18 
     | 
    
         
            -
                    #   actions += responses[:"grid#{action[:x]}-"][state]
         
     | 
| 
       19 
     | 
    
         
            -
                    #   actions += responses[:"grid-#{action[:y]}"][state]
         
     | 
| 
       20 
     | 
    
         
            -
                    # end
         
     | 
| 
       21 
     | 
    
         
            -
                    # actions += responses[type][state]
         
     | 
| 
       22 
     | 
    
         
            -
                    # actions += responses[:all][state]
         
     | 
| 
       23 
     | 
    
         
            -
                    # actions.compact.each {|block| block.call(self, action)}
         
     | 
| 
      
 53 
     | 
    
         
            +
                    mappings_for_action(action).each do |block|
         
     | 
| 
      
 54 
     | 
    
         
            +
                      block.call(self, action)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
                    nil
         
     | 
| 
       24 
57 
     | 
    
         
             
                  rescue Exception => e # TODO: StandardException, RuntimeError, or Exception?
         
     | 
| 
       25 
58 
     | 
    
         
             
                    logger.error "Error when responding to action #{action.inspect}: #{e.inspect}"
         
     | 
| 
       26 
59 
     | 
    
         
             
                    raise e
         
     | 
| 
       27 
60 
     | 
    
         
             
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  def mappings_for_action(action)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    combined_types  = combined_types(action[:type].to_sym, action[:control])
         
     | 
| 
      
 64 
     | 
    
         
            +
                    state           = action[:state].to_sym
         
     | 
| 
      
 65 
     | 
    
         
            +
                    actions         = []
         
     | 
| 
      
 66 
     | 
    
         
            +
                    actions        += combined_types.map { |ct| responses[ct][state] }
         
     | 
| 
      
 67 
     | 
    
         
            +
                    actions        += responses[:all][state]
         
     | 
| 
      
 68 
     | 
    
         
            +
                    actions.flatten.compact
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  def expand_states(state)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    case state
         
     | 
| 
      
 73 
     | 
    
         
            +
                    when :both  then %i(down up)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    when :all   then responses_hash.keys
         
     | 
| 
      
 75 
     | 
    
         
            +
                    else Array(state)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
       28 
78 
     | 
    
         
             
                end
         
     | 
| 
       29 
79 
     | 
    
         
             
              end
         
     | 
| 
       30 
80 
     | 
    
         
             
            end
         
     | 
| 
         @@ -6,28 +6,28 @@ module SurfaceMaster 
     | 
|
| 
       6 
6 
     | 
    
         
             
                module MIDICodes
         
     | 
| 
       7 
7 
     | 
    
         
             
                  # TODO: Use a lib to do a deep-freeze.
         
     | 
| 
       8 
8 
     | 
    
         
             
                  # rubocop:disable Metrics/LineLength
         
     | 
| 
       9 
     | 
    
         
            -
                  CONTROLS    = { 0x90 => { 0x00 => { type: :pad,            
     | 
| 
       10 
     | 
    
         
            -
                                            0x01 => { type: :pad,            
     | 
| 
       11 
     | 
    
         
            -
                                            0x02 => { type: :pad,            
     | 
| 
       12 
     | 
    
         
            -
                                            0x03 => { type: :pad,            
     | 
| 
       13 
     | 
    
         
            -
                                            0x0F => { type: :shoulder,       
     | 
| 
       14 
     | 
    
         
            -
                                  0x80 => { 0x00 => { type: :pad,            
     | 
| 
       15 
     | 
    
         
            -
                                            0x01 => { type: :pad,            
     | 
| 
       16 
     | 
    
         
            -
                                            0x02 => { type: :pad,            
     | 
| 
       17 
     | 
    
         
            -
                                            0x03 => { type: :pad,            
     | 
| 
       18 
     | 
    
         
            -
                                            0x0F => { type: :shoulder,       
     | 
| 
       19 
     | 
    
         
            -
                                  0xB0 => { 0x00 => { type: : 
     | 
| 
       20 
     | 
    
         
            -
                                            0x01 => { type: : 
     | 
| 
       21 
     | 
    
         
            -
                                            0x02 => { type: : 
     | 
| 
       22 
     | 
    
         
            -
                                            0x03 => { type: : 
     | 
| 
       23 
     | 
    
         
            -
                                            0x0C => { type: :accelerometer,  
     | 
| 
       24 
     | 
    
         
            -
                                            0x0D => { type: :accelerometer,  
     | 
| 
       25 
     | 
    
         
            -
                                            0x0F => { 
     | 
| 
      
 9 
     | 
    
         
            +
                  CONTROLS    = { 0x90 => { 0x00 => { type: :pad,           state: :down,    control: { bank: 1 } },
         
     | 
| 
      
 10 
     | 
    
         
            +
                                            0x01 => { type: :pad,           state: :down,    control: { bank: 2 } },
         
     | 
| 
      
 11 
     | 
    
         
            +
                                            0x02 => { type: :pad,           state: :down,    control: { bank: 3 } },
         
     | 
| 
      
 12 
     | 
    
         
            +
                                            0x03 => { type: :pad,           state: :down,    control: { bank: 4 } },
         
     | 
| 
      
 13 
     | 
    
         
            +
                                            0x0F => { type: :shoulder,      state: :down,    control: {} } },
         
     | 
| 
      
 14 
     | 
    
         
            +
                                  0x80 => { 0x00 => { type: :pad,           state: :up,      control: { bank: 1 } },
         
     | 
| 
      
 15 
     | 
    
         
            +
                                            0x01 => { type: :pad,           state: :up,      control: { bank: 2 } },
         
     | 
| 
      
 16 
     | 
    
         
            +
                                            0x02 => { type: :pad,           state: :up,      control: { bank: 3 } },
         
     | 
| 
      
 17 
     | 
    
         
            +
                                            0x03 => { type: :pad,           state: :up,      control: { bank: 4 } },
         
     | 
| 
      
 18 
     | 
    
         
            +
                                            0x0F => { type: :shoulder,      state: :up,      control: {} } },
         
     | 
| 
      
 19 
     | 
    
         
            +
                                  0xB0 => { 0x00 => { type: :vknob,         state: :update,  control: { vknob: 1 } },
         
     | 
| 
      
 20 
     | 
    
         
            +
                                            0x01 => { type: :vknob,         state: :update,  control: { vknob: 2 } },
         
     | 
| 
      
 21 
     | 
    
         
            +
                                            0x02 => { type: :vknob,         state: :update,  control: { vknob: 3 } },
         
     | 
| 
      
 22 
     | 
    
         
            +
                                            0x03 => { type: :vknob,         state: :update,  control: { vknob: 4 } },
         
     | 
| 
      
 23 
     | 
    
         
            +
                                            0x0C => { type: :accelerometer, state: :tilt,    control: { axis: :x } },
         
     | 
| 
      
 24 
     | 
    
         
            +
                                            0x0D => { type: :accelerometer, state: :tilt,    control: { axis: :y } },
         
     | 
| 
      
 25 
     | 
    
         
            +
                                            0x0F => {                       state: :down } } }.freeze
         
     | 
| 
       26 
26 
     | 
    
         
             
                  # rubocop:enable Metrics/LineLength
         
     | 
| 
       27 
27 
     | 
    
         
             
                  SHOULDERS   = { 0x03 => { button: :left },
         
     | 
| 
       28 
28 
     | 
    
         
             
                                  0x04 => { button: :right } }.freeze
         
     | 
| 
       29 
     | 
    
         
            -
                  SELECTORS   = { 0x01 => {  
     | 
| 
       30 
     | 
    
         
            -
                                  0x02 => {  
     | 
| 
      
 29 
     | 
    
         
            +
                  SELECTORS   = { 0x01 => { type:   :banks },
         
     | 
| 
      
 30 
     | 
    
         
            +
                                  0x02 => { type:   :vknobs } }.freeze
         
     | 
| 
       31 
31 
     | 
    
         
             
                end
         
     | 
| 
       32 
32 
     | 
    
         
             
              end
         
     | 
| 
       33 
33 
     | 
    
         
             
            end
         
     | 
    
        data/lib/surface_master.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: surface_master
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.4.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Jon Frisby
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2015-09- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2015-09-13 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: portmidi
         
     | 
| 
         @@ -73,9 +73,11 @@ files: 
     | 
|
| 
       73 
73 
     | 
    
         
             
            - debug_tools/decode.rb
         
     | 
| 
       74 
74 
     | 
    
         
             
            - debug_tools/extract_midi_monitor_sample.sh
         
     | 
| 
       75 
75 
     | 
    
         
             
            - docs/Numark_Orbit_QuickRef.md
         
     | 
| 
       76 
     | 
    
         
            -
            - examples/ 
     | 
| 
       77 
     | 
    
         
            -
            - examples/ 
     | 
| 
       78 
     | 
    
         
            -
            - examples/ 
     | 
| 
      
 76 
     | 
    
         
            +
            - examples/launchpad_playground.rb
         
     | 
| 
      
 77 
     | 
    
         
            +
            - examples/orbit_device.rb
         
     | 
| 
      
 78 
     | 
    
         
            +
            - examples/orbit_interaction.rb
         
     | 
| 
      
 79 
     | 
    
         
            +
            - examples/orbit_playground.rb
         
     | 
| 
      
 80 
     | 
    
         
            +
            - examples/system_monitor.rb
         
     | 
| 
       79 
81 
     | 
    
         
             
            - lib/surface_master.rb
         
     | 
| 
       80 
82 
     | 
    
         
             
            - lib/surface_master/device.rb
         
     | 
| 
       81 
83 
     | 
    
         
             
            - lib/surface_master/errors.rb
         
     |