summaryrefslogtreecommitdiff
path: root/libs/surfaces/mackie/scripts
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2007-03-18 06:07:08 +0000
committerDavid Robillard <d@drobilla.net>2007-03-18 06:07:08 +0000
commit99904735e066804358f1d0bd138a84f1e9ecda91 (patch)
tree71a924cf1660b5b00231275bd481bbd27094dd9b /libs/surfaces/mackie/scripts
parenteb270e70a12c410cdd98585ad25bb6d8e384a4f5 (diff)
Merged with trunk R1612.
git-svn-id: svn://localhost/ardour2/branches/midi@1614 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/surfaces/mackie/scripts')
-rw-r--r--libs/surfaces/mackie/scripts/bank.rb32
-rw-r--r--libs/surfaces/mackie/scripts/bcf-controls.csv96
-rw-r--r--libs/surfaces/mackie/scripts/controls.rb208
-rwxr-xr-xlibs/surfaces/mackie/scripts/dump.rb11
-rw-r--r--libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb59
-rw-r--r--libs/surfaces/mackie/scripts/generate-button-handlers-h.erb54
-rwxr-xr-xlibs/surfaces/mackie/scripts/generate-surface.rb26
-rwxr-xr-xlibs/surfaces/mackie/scripts/host.rb133
-rw-r--r--libs/surfaces/mackie/scripts/mackie-controls.csv93
-rw-r--r--libs/surfaces/mackie/scripts/mackie.rb119
-rw-r--r--libs/surfaces/mackie/scripts/parse.rb61
-rw-r--r--libs/surfaces/mackie/scripts/signals.rb137
-rw-r--r--libs/surfaces/mackie/scripts/simple_host.rb137
-rw-r--r--libs/surfaces/mackie/scripts/surface-cc-template.erb95
-rw-r--r--libs/surfaces/mackie/scripts/surface-h-template.erb27
-rwxr-xr-xlibs/surfaces/mackie/scripts/test_controls.rb9
-rw-r--r--libs/surfaces/mackie/scripts/transform.rb26
-rw-r--r--libs/surfaces/mackie/scripts/write.rb10
18 files changed, 1333 insertions, 0 deletions
diff --git a/libs/surfaces/mackie/scripts/bank.rb b/libs/surfaces/mackie/scripts/bank.rb
new file mode 100644
index 0000000000..e1482545ee
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/bank.rb
@@ -0,0 +1,32 @@
+#! /usr/bin/ruby
+
+class Bank
+ attr_accessor :routes, :strips, :current
+
+ def initialize( routes = 17, strips = 8, current = 0 )
+ @routes = routes
+ @strips = strips
+ @current = current
+ end
+
+ def left
+ new_initial = current - routes
+ if new_initial < 0
+ new_initial = 0
+ end
+ current = new_initial
+ self
+ end
+
+ def right
+ delta = routes - ( strips + current ) - 1
+ puts "delta: #{delta}"
+ if delta > strips
+ delta = strips
+ end
+ @current += delta
+ self
+ end
+end
+
+b=Bank.new
diff --git a/libs/surfaces/mackie/scripts/bcf-controls.csv b/libs/surfaces/mackie/scripts/bcf-controls.csv
new file mode 100644
index 0000000000..6a6d66f6ac
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/bcf-controls.csv
@@ -0,0 +1,96 @@
+type,count,group,name,switch,led,id
+# faders
+fader,7,strip,gain,1,0,0x00
+fader,1,master,gain,1,0,0x07
+
+# pots
+pot,7,strip,vpot,1,1,0x10
+pot,1,,jog,1,0,0x17
+pot,1,,external,1,0,0x2e
+
+# strip buttons
+button,7,strip,recenable,1,1,0x18
+button,7,strip,solo,1,1,0x20
+button,7,strip,mute,1,1,0x10
+button,7,strip,select,1,1,0x0
+button,7,strip,vselect,1,0,0x08
+
+# overlay buttons
+button,1,assignment,io,1,1,0x28
+button,1,assignment,sends,1,1,0x5a
+button,1,assignment,pan,1,1,0x59
+button,1,assignment,plugin,1,1,0x57
+button,1,assignment,eq,1,1,0x58
+button,1,assignment,dyn,1,1,0x2d
+button,1,bank,left,1,0,0x2e
+button,1,bank,right,1,0,0x2f
+button,1,bank,channel_left,1,0,0x30
+button,1,bank,channel_right,1,0,0x31
+button,1,,flip,1,1,0x32
+button,1,,edit,1,1,0x56
+
+button,1,display,name_value,1,0,0x34
+button,1,display,smpte_beats,1,0,0x35
+button,1,,F1,1,0,0x36
+button,1,,F2,1,0,0x37
+button,1,,F3,1,0,0x38
+button,1,,F4,1,0,0x39
+button,1,,F5,1,0,0x3a
+button,1,,F6,1,0,0x3b
+button,1,,F7,1,0,0x3c
+button,1,,F8,1,0,0x3d
+button,1,,F9,1,0,0x3e
+button,1,,F10,1,0,0x3f
+button,1,,F11,1,0,0x40
+button,1,,F12,1,0,0x41
+button,1,,F13,1,0,0x42
+button,1,,F14,1,0,0x43
+button,1,,F15,1,0,0x44
+button,1,,F16,1,0,0x45
+# turn on/off all solos
+button,1,,global_solo,1,0,0x27
+button,1,modifiers,option,1,0,0x47
+button,1,modifiers,control,1,0,0x48
+button,1,modifiers,cmd_alt,1,0,0x49
+button,1,automation,on,1,1,0x4a
+button,1,automation,rec_ready,1,1,0x4b
+button,1,functions,undo,1,1,0x4c
+button,1,automation,snapshot,1,1,0x4d
+button,1,automation,touch,1,1,0x4e
+button,1,functions,redo,1,1,0x4f
+button,1,functions,marker,1,1,0x50
+button,1,functions,enter,1,1,0x51
+button,1,functions,cancel,1,0,0x52
+button,1,functions,mixer,1,0,0x53
+button,1,transport,frm_left,1,1,0x54
+button,1,transport,frm_right,1,1,0x55
+button,1,transport,loop,1,1,0x46
+button,1,transport,punch_in,1,1,0x2c
+button,1,transport,punch_out,1,1,0x2b
+button,1,transport,home,1,1,0x2a
+button,1,transport,end,1,1,0x29
+
+# transport buttons
+button,1,transport,"rewind",1,1,0x5b
+button,1,transport,"ffwd",1,1,0x5c
+button,1,transport,"stop",1,1,0x5d
+button,1,transport,"play",1,1,0x5e
+button,1,transport,"record",1,1,0x1f
+button,1,cursor,"cursor_up",1,0,0x60
+button,1,cursor,"cursor_down",1,0,0x61
+button,1,cursor,"cursor_left",1,0,0x62
+button,1,cursor,"cursor_right",1,0,0x63
+button,1,,"zoom",1,1,0x64
+button,1,,"scrub",1,1,0x65
+button,1,user,"user_a",1,0,0x66
+button,1,user,"user_b",1,0,0x67
+
+button,7,strip,"fader_touch",1,0,0x68
+button,1,master,"fader_touch",1,0,0x6f
+button,1,master,mute,1,0,0x17
+button,1,,clicking,1,1,0x33
+
+button,1,,"smpte",0,1,0x71
+button,1,,"beats",0,1,0x72
+button,1,,"solo",0,1,0x73
+button,1,,"relay_click",0,1,0x76
diff --git a/libs/surfaces/mackie/scripts/controls.rb b/libs/surfaces/mackie/scripts/controls.rb
new file mode 100644
index 0000000000..b56fd6010d
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/controls.rb
@@ -0,0 +1,208 @@
+#! /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 'faster_csv'
+require 'mackie.rb'
+
+class Control
+ attr_accessor :id, :led, :group, :name, :ordinal, :switch
+
+ def initialize( obj, group )
+ @id = obj.id
+ @name = obj.name
+ @ordinal = obj.ordinal
+ @switch = obj.switch
+ @group = group
+ end
+
+ def ordinal_name
+ end
+end
+
+class Fader < Control
+ def self.midi_zero_byte
+ 0xe0
+ end
+
+ def self.mask_for_id( bytes )
+ bytes[0] & 0b00001111
+ end
+end
+
+class Button < Control
+ def self.midi_zero_byte
+ 0x90
+ end
+
+ def self.mask_for_id( bytes )
+ bytes[1]
+ end
+end
+
+class Led < Control
+end
+
+class LedRing < Led
+end
+
+class Pot < Control
+ def self.midi_zero_byte
+ 0xb0
+ end
+
+ def self.mask_for_id( bytes )
+ bytes[1] & 0b00011111
+ end
+
+ def led=( rhs )
+ @led = LedRing.new( rhs, group )
+ end
+end
+
+class Group < Array
+ attr_accessor :name, :controls
+
+ def initialize( name )
+ @name = name
+ end
+
+ def add_control( control )
+ @controls ||= Array.new
+ @controls << control
+ end
+end
+
+class Strip < Group
+
+ attr_accessor :ordinal
+ def initialize( name, ordinal )
+ super( name )
+ @ordinal = ordinal
+ end
+
+ def name
+ @name == 'master' ? @name : "#{@name}_#{@ordinal}"
+ end
+
+ def is_master
+ name == 'master'
+ end
+
+end
+
+types = { 0xe0 => Fader, 0x90 => Button, 0xb0 => Pot }
+
+# number of controls, name, switch, led, id
+# anything that doesn't have the correct number
+# of columns will be ignored
+# actually, 'switch' means it generates data
+# whereas 'led' means it receives data
+
+class Row
+ attr_accessor :count, :name, :switch, :led, :start_id, :type, :group
+ attr_accessor :id, :ordinal_name, :ordinal_group, :ordinal
+
+ def initialize( hash )
+ @count = hash['count'].to_i
+ @name = hash['name']
+ @switch = hash['switch'].to_b
+ @led = hash['led'].to_b
+ @start_id = hash['id'].hex
+ @type = hash['type']
+ @group = hash['group']
+
+ @hash = hash
+ end
+
+ def each_ordinal( &block )
+ for i in 0...count
+ @ordinal = i + 1
+ @ordinal_name = count > 1 ? "#{name}_#{ordinal}" : name
+ @ordinal_group = count > 1 ? "#{group}_#{ordinal}" : group
+ @id = start_id + i
+
+ @hash['ordinal_name'] = @ordinal_name
+ @hash['ordinal_group'] = @ordinal_group
+
+ yield( self )
+ end
+ self
+ end
+
+ def to_hash
+ @hash
+ end
+end
+
+class Surface
+ attr_reader :groups, :controls_by_id, :types, :midis, :controls, :name
+
+ def initialize( name = 'none' )
+ @name = name
+ @types = Hash.new
+ @groups = Hash.new
+ @controls = Array.new
+ @controls_by_id = Hash.new
+ @midis = Hash.new
+ end
+
+ def add_or_create_group( name, ordinal = nil )
+ if name.nil?
+ @groups['none'] = Group.new('none')
+ else
+ group = name =~ /strip/ || name == 'master' ? Strip.new( name, ordinal ) : Group.new( name )
+ @groups[group.name] ||= group
+ end
+ end
+
+ def parse( control_data )
+ FasterCSV.parse( control_data, :headers => true ) do |csv_row|
+ next if csv_row.entries.size < 5 || csv_row[0] =~ /^\s*#/ || csv_row['id'].nil?
+ row = Row.new( csv_row )
+
+ row.each_ordinal do |row|
+ group = add_or_create_group( row.group, row.ordinal )
+ if row.switch
+ # for controls
+ control = eval "#{row.type.capitalize}.new( row, group )"
+
+ # for controls with leds
+ control.led = Led.new( row, group ) if row.led
+ else
+ # for LED-only entries
+ if row.led
+ control = Led.new( row, group )
+ control.led = control
+ end
+ end
+
+ # add the new control to the various lookups
+ @controls_by_id[row.id] = control
+ @controls << control
+ group << control
+
+ # add incoming midi bytes
+ if row.switch
+ types[control.class.midi_zero_byte] = control.class
+ midis[control.class.midi_zero_byte] ||= Hash.new
+ midis[control.class.midi_zero_byte][row.id] = control
+ end
+ end
+ end
+ self
+ end
+end
diff --git a/libs/surfaces/mackie/scripts/dump.rb b/libs/surfaces/mackie/scripts/dump.rb
new file mode 100755
index 0000000000..f1e341fb34
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/dump.rb
@@ -0,0 +1,11 @@
+#! /usr/bin/ruby
+
+while !File.exist? ARGV[0]
+ sleep 0.010
+end
+
+file = File.open ARGV[0], 'r'
+
+while bytes = file.sysread( 3 )
+ puts "%02x %02x %02x" % [ bytes[0], bytes[1], bytes[2] ]
+end
diff --git a/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb b/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb
new file mode 100644
index 0000000000..62bc65d0c3
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb
@@ -0,0 +1,59 @@
+<%#
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-%>
+<%-
+require 'controls.rb'
+
+sf = Surface.new
+sf.parse( File.open "mackie-controls.csv" )
+buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
+-%>
+/*
+ Generated by scripts/generate-button-handlers.erb
+*/
+#include "mackie_button_handler.h"
+#include "controls.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace Mackie;
+
+LedState MackieButtonHandler::default_button_press( Button & button )
+{
+ cout << "press: " << button << endl;
+ return on;
+}
+LedState MackieButtonHandler::default_button_release( Button & button )
+{
+ cout << "release: " << button << endl;
+ return off;
+}
+
+<%-
+buttons.each do |button|
+%>
+LedState MackieButtonHandler::<%=button.name%>_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::<%=button.name%>_release( Button & button )
+{
+ return default_button_release( button );
+}
+<% end %>
diff --git a/libs/surfaces/mackie/scripts/generate-button-handlers-h.erb b/libs/surfaces/mackie/scripts/generate-button-handlers-h.erb
new file mode 100644
index 0000000000..605b6c29dc
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/generate-button-handlers-h.erb
@@ -0,0 +1,54 @@
+<%#
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-%>
+<%-
+require 'controls.rb'
+
+sf = Surface.new
+sf.parse( File.open "mackie-controls.csv" )
+buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
+-%>
+#ifndef mackie_button_handler_h
+#define mackie_button_handler_h
+/*
+ Generated by scripts/generate-button-handlers.erb
+*/
+
+#include "types.h"
+
+namespace Mackie
+{
+
+class MackieButtonHandler
+{
+public:
+ virtual ~MackieButtonHandler() {}
+
+ virtual LedState default_button_press( Button & button );
+ virtual LedState default_button_release( Button & button );
+
+ virtual void update_led( Button & button, LedState ls ) = 0;
+
+<%- buttons.each do |button| %>
+ virtual LedState <%=button.name%>_press( Button & );
+ virtual LedState <%=button.name%>_release( Button & );
+<% end %>
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/scripts/generate-surface.rb b/libs/surfaces/mackie/scripts/generate-surface.rb
new file mode 100755
index 0000000000..c6a028804a
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/generate-surface.rb
@@ -0,0 +1,26 @@
+#! /usr/bin/ruby
+
+require 'erb'
+
+require File.dirname(__FILE__) + '/controls.rb'
+
+cc_template = ''
+File.open( File.dirname(__FILE__) + "/surface-cc-template.erb", "r" ) { |f| cc_template = f.read }
+
+h_template = ''
+File.open( File.dirname(__FILE__) + "/surface-h-template.erb", "r" ) { |f| h_template = f.read }
+
+sf = Surface.new( ARGV[0] )
+control_data = ''
+File.open( File.dirname(__FILE__) + "/#{sf.name.downcase}-controls.csv", "r") { |f| control_data = f.read }
+sf.parse control_data
+
+@result = ""
+erb = ERB.new( cc_template , 0, "%<>-", "@result" )
+erb.result
+File.open( "#{sf.name.downcase}_surface.cc", "w" ) { |f| f.write @result }
+
+erb = ERB.new( h_template , 0, "%<>-", "@result" )
+erb.result
+File.open( "#{sf.name.downcase}_surface.h", "w" ) { |f| f.write @result }
+
diff --git a/libs/surfaces/mackie/scripts/host.rb b/libs/surfaces/mackie/scripts/host.rb
new file mode 100755
index 0000000000..8972cba137
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/host.rb
@@ -0,0 +1,133 @@
+#! /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 'controls.rb'
+require 'mackie.rb'
+
+while !File.exist? ARGV[0]
+ sleep 0.010
+end
+
+#mapping_csv = ARGV[1] || "mackie-controls.csv"
+mapping_csv = ARGV[1]
+puts "mapping_csv: #{mapping_csv}"
+puts ""
+
+file = File.open ARGV[0], 'r+'
+mck = Mackie.new( file )
+
+# send device query
+response = mck.sysex( "\x00" )
+puts "response: #{response.to_hex}"
+
+# decode host connection query
+status = response[0]
+if status != 1
+ puts "expected 01, got " + response.to_hex.inspect
+ exit(1)
+end
+serial = response[1..7]
+challenge = response[8..11]
+puts <<EOF
+serial: #{serial.to_hex.inspect}
+challenge: #{challenge.to_hex.inspect}
+EOF
+
+# send host connection reply
+response = mck.sysex( "\x02" + serial.pack('C*') + challenge.pack('C*') )
+
+# decode host connection confirmation
+status = response[0]
+if status != 3
+ puts "expected 03, got " + response.to_hex.inspect
+ exit(1)
+end
+
+serial = response[1..7]
+puts <<EOF
+serial: #{serial.to_hex.inspect}
+EOF
+
+# faders to minimum. bcf2000 doesn't respond
+#file.write( hdr + "\x61\xf7" )
+
+# all leds off. bcf2000 doesn't respond
+#file.write( hdr + "\x62\xf7" )
+
+# get version. comes back as ASCII bytes
+version = mck.sysex( "\x13\x00" )
+puts "version: #{version.map{|x| x.chr}}"
+
+# write a welcome message. bcf2000 responds with exact
+# string but doesn't display anything
+# 0 offset,
+#~ file.write hdr + "\x12\x3fLCDE\xf7"
+#~ file.flush
+#~ answer = read_sysex file
+#~ puts "answer: #{answer[hdr.length..-1].map{|x| x.chr}}"
+
+# write to BBT display
+#~ file.write hdr + "\x10LCDE\xf7"
+#~ file.flush
+#~ bbt = []
+#~ while ( nc = file.read( 1 ) )[0] != 0xf7
+ #~ bbt << nc[0]
+#~ end
+#~ puts "bbt: #{bbt[hdr.length..-1].map{|x| x.chr}}"
+
+# write 7-segment display
+#~ file.write hdr + "\x11LCDE\xf7"
+#~ file.flush
+
+# go offline. bcf2000 doesn't respond
+#~ file.write( hdr + "\x0f\x7f\xf7" )
+#~ file.flush
+
+sf = Surface.new
+control_data = ""
+File.open( mapping_csv ) { |f| control_data = f.read }
+sf.parse( control_data )
+
+# send all faders to 0, but bounce them first
+# otherwise the bcf gets confused
+sf.midis[0xe0].values.find_all{|x| x.class == Fader}.each do |x|
+ bytes = Array.new
+ bytes[0] = 0xe0 + x.ordinal - 1
+ bytes[1] = 0x1
+ bytes[2] = 0x1
+ file.write bytes.pack( 'C*' )
+ bytes[0] = 0xe0 + x.ordinal - 1
+ bytes[1] = 0x0
+ bytes[2] = 0x0
+ file.write bytes.pack( 'C*' )
+end
+file.flush
+
+# respond to control movements
+while bytes = mck.file.read( 3 )
+ print "received: %02.x %02.x %02.x" % [ bytes[0], bytes[1], bytes[2] ]
+ midi_type = bytes[0] & 0b11110000
+
+ control_id = sf.types[midi_type].mask_for_id( bytes )
+ control = sf.midis[midi_type][control_id]
+
+ print " Control Type: %-7s, " % sf.types[midi_type]
+ print "id: %4i" % control_id
+ print ", control: %15s" % ( control ? control.name : "nil control" )
+ print ", %15s" % ( control ? control.group.name : "nil group" )
+ print "\n"
+end
diff --git a/libs/surfaces/mackie/scripts/mackie-controls.csv b/libs/surfaces/mackie/scripts/mackie-controls.csv
new file mode 100644
index 0000000000..5dbb6297f8
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/mackie-controls.csv
@@ -0,0 +1,93 @@
+type,count,group,name,switch,led,id
+# faders
+fader,8,strip,gain,1,0,0x00
+fader,1,master,gain,1,0,0x08
+
+# pots
+pot,8,strip,vpot,1,1,0x10
+pot,1,,jog,1,0,0x3c
+pot,1,,external,1,0,0x2e
+
+# strip buttons
+button,8,strip,recenable,1,1,0x0
+button,8,strip,solo,1,1,0x08
+button,8,strip,mute,1,1,0x10
+button,8,strip,select,1,1,0x18
+button,8,strip,vselect,1,0,0x20
+
+# overlay buttons
+button,1,assignment,io,1,1,0x28
+button,1,assignment,sends,1,1,0x29
+button,1,assignment,pan,1,1,0x2a
+button,1,assignment,plugin,1,1,0x2b
+button,1,assignment,eq,1,1,0x2c
+button,1,assignment,dyn,1,1,0x2d
+button,1,bank,left,1,0,0x2e
+button,1,bank,right,1,0,0x2f
+button,1,bank,channel_left,1,0,0x30
+button,1,bank,channel_right,1,0,0x31
+button,1,,flip,1,1,0x32
+button,1,,edit,1,1,0x33
+
+button,1,display,name_value,1,0,0x34
+button,1,display,smpte_beats,1,0,0x35
+button,1,,F1,1,0,0x36
+button,1,,F2,1,0,0x37
+button,1,,F3,1,0,0x38
+button,1,,F4,1,0,0x39
+button,1,,F5,1,0,0x3a
+button,1,,F6,1,0,0x3b
+button,1,,F7,1,0,0x3c
+button,1,,F8,1,0,0x3d
+button,1,,F9,1,0,0x3e
+button,1,,F10,1,0,0x3f
+button,1,,F11,1,0,0x40
+button,1,,F12,1,0,0x41
+button,1,,F13,1,0,0x42
+button,1,,F14,1,0,0x43
+button,1,,F15,1,0,0x44
+button,1,,F16,1,0,0x45
+button,1,modifiers,shift,1,0,0x46
+button,1,modifiers,option,1,0,0x47
+button,1,modifiers,control,1,0,0x48
+button,1,modifiers,cmd_alt,1,0,0x49
+button,1,automation,on,1,1,0x4a
+button,1,automation,rec_ready,1,1,0x4b
+button,1,functions,undo,1,1,0x4c
+button,1,automation,snapshot,1,1,0x4d
+button,1,automation,touch,1,1,0x4e
+button,1,functions,redo,1,1,0x4f
+button,1,functions,marker,1,1,0x50
+button,1,functions,enter,1,1,0x51
+button,1,functions,cancel,1,0,0x52
+button,1,functions,mixer,1,0,0x53
+button,1,transport,frm_left,1,1,0x54
+button,1,transport,frm_right,1,1,0x55
+button,1,transport,loop,1,1,0x56
+button,1,transport,punch_in,1,1,0x57
+button,1,transport,punch_out,1,1,0x58
+button,1,transport,home,1,1,0x59
+button,1,transport,end,1,1,0x5a
+
+# transport buttons
+button,1,transport,"rewind",1,1,0x5b
+button,1,transport,"ffwd",1,1,0x5c
+button,1,transport,"stop",1,1,0x5d
+button,1,transport,"play",1,1,0x5e
+button,1,transport,"record",1,1,0x5f
+button,1,cursor,"cursor_up",1,0,0x60
+button,1,cursor,"cursor_down",1,0,0x61
+button,1,cursor,"cursor_left",1,0,0x62
+button,1,cursor,"cursor_right",1,0,0x63
+button,1,,"zoom",1,1,0x64
+button,1,,"scrub",1,1,0x65
+button,1,user,"user_a",1,0,0x66
+button,1,user,"user_b",1,0,0x67
+
+button,8,strip,"fader_touch",1,0,0x68
+button,1,master,"fader_touch",1,0,0x70
+
+button,1,,"smpte",0,1,0x71
+button,1,,"beats",0,1,0x72
+button,1,,"solo",0,1,0x73
+button,1,,"relay_click",0,1,0x76
diff --git a/libs/surfaces/mackie/scripts/mackie.rb b/libs/surfaces/mackie/scripts/mackie.rb
new file mode 100644
index 0000000000..4c4080ad10
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/mackie.rb
@@ -0,0 +1,119 @@
+class String
+ def to_bytes
+ arr = []
+ each_byte{|x| arr << x}
+ arr
+ end
+end
+
+class Array
+ def to_hex
+ map{|x| "%2.0x" % x}
+ end
+
+ alias as_hex to_hex
+end
+
+class String
+ def to_b
+ to_i != 0 || %w{true t yes y}.include?( self.downcase )
+ end
+end
+
+class Fixnum
+ def to_hex
+ "%02x" % self
+ end
+end
+
+class Mackie
+ attr_accessor :file
+
+ def initialize( file )
+ @file = file
+ end
+
+ # send and receive a sysex message
+ # after wrapping in the header and the eox byte
+ def sysex( msg )
+ puts "Mackie write: #{msg.unpack('C*').to_hex.inspect}"
+ write_sysex( msg )
+ response = read_sysex
+ puts "Mackie response: #{response.to_hex.inspect}"
+ response[5..-1]
+ end
+
+ # returns an array of bytes
+ def read_sysex
+ buf = []
+ while ( nc = @file.read( 1 ) )[0] != 0xf7
+ buf << nc[0]
+ end
+ buf
+ end
+
+ # send and flush a sysex message
+ # after wrapping in the header and the eox byte
+ def write_sysex( msg )
+ @file.write( hdrlc + msg + "\xf7" )
+ @file.flush
+ end
+
+ def write( msg )
+ @file.write msg
+ @file.flush
+ end
+
+ def translate_seven_segment( char )
+ case char
+ when 0x40..0x60
+ char - 0x40
+ when 0x21..0x3f
+ char
+ else
+ 0x00
+ end
+ end
+
+ # display the msg (which can be only 2 characters)
+ # append the number of stops. Options are '..', '. ', '. ', ' '
+ def two_char( msg, stops = ' ' )
+ two = Array.new
+ two << translate_seven_segment( msg.upcase[0] )
+ two << translate_seven_segment( msg.upcase[1] )
+
+ two[0] += 0x40 if stops[0] == '.'[0]
+ two[1] += 0x40 if stops[1] == '.'[0]
+
+ midi_msg = [0xb0, 0x4a, two[1], 0x4b, two[0] ]
+ write midi_msg.pack( 'C*' )
+ end
+
+ # send and receive the device initialisation
+ def init
+ response = sysex( "\x00" )
+
+ # decode host connection query
+ status = response[0]
+ raise( "expected 01, got " + response.inspect ) if status != 1
+
+ serial = response[1..7]
+ challenge = response[8..11]
+
+ # send host connection reply
+ reply = "\x02" + serial.pack('C*') + challenge.pack('C*')
+ response = sysex reply
+
+ # decode host connection confirmation
+ status = response[0]
+ raise ( "expected 03, got " + response.inspect ) if status != 3
+ end
+
+ def hdrlc
+ "\xf0\x00\x00\x66\x10"
+ end
+
+ def hdrlcxt
+ "\xf0\x00\x00\x66\x11"
+ end
+end
diff --git a/libs/surfaces/mackie/scripts/parse.rb b/libs/surfaces/mackie/scripts/parse.rb
new file mode 100644
index 0000000000..3a225a5756
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/parse.rb
@@ -0,0 +1,61 @@
+#! /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require "rexml/document"
+file = File.new( ARGV[0] )
+doc = REXML::Document.new file
+
+# fetch the node containing the controls
+controls = XPath.first( doc, 'Session/ControlProtocols/Protocol[@name="Generic MIDI"]/controls' )
+
+channel = 1
+
+# A Control is a button or slider. It has an internal ID
+# an incoming MIDI message, and an outgoing midi message
+class Control
+
+end
+
+# Strips have solo,rec,mute,pan,fader
+# Strips have midi input
+# Strips have midi output
+# Strips have an XML representation, or something like that
+class Strip
+ def initialize( node )
+ @solo = node.elements['solo']
+ @mute = node.elements['mute']
+ @rec = node.elements['recenable']
+ @fader = node.elements['IO/gaincontrol']
+ @panner = node.elements['IO/Panner/StreamPanner/panner']
+ end
+end
+
+# This knows how to extract a set of controls from a Route
+
+doc.elements.each( 'Session/Routes/Route' ) do |node|
+ strip = Strip.new( node )
+
+ controls.add_element( 'mute',
+ 'id' => mute.attribute('id').value,
+ 'event' => "0xb0",
+ 'channel' => channel.to_s,
+ 'additional' => "0x41"
+ )
+
+end
+
+pp controls.elements
diff --git a/libs/surfaces/mackie/scripts/signals.rb b/libs/surfaces/mackie/scripts/signals.rb
new file mode 100644
index 0000000000..8182e562a3
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/signals.rb
@@ -0,0 +1,137 @@
+#~ /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 'erb'
+
+signals = %w{
+solo_changed
+mute_changed
+record_enable_changed
+gain_changed
+name_changed
+panner_changed
+}
+
+@signal_calls = { 'panner_changed' => 'panner()[0]->Changed' }
+
+def connection_call( x )
+ if @signal_calls.include? x
+ @signal_calls[x]
+ else
+ x
+ end
+end
+
+signals.each do |x|
+ puts <<EOF
+void MackieControlProtocol::notify_#{x}( void *, ARDOUR::Route * route )
+{
+ try
+ {
+ strip_from_route( route ).#{x.gsub( /_changed/, '' )}();
+ }
+ catch( exception & e )
+ {
+ cout << e.what() << endl;
+ }
+}
+
+EOF
+end
+
+class_def = <<EOF
+#ifndef route_signal_h
+#define route_signal_h
+
+#include <sigc++/sigc++.h>
+
+class MackieControlProtocol;
+
+namespace ARDOUR {
+ class Route;
+}
+
+namespace Mackie
+{
+
+class Strip;
+
+/**
+ This class is intended to easily create and destroy the set of
+ connections from a route to a control surface strip. Instanting
+ it will connect the signals, and destructing it will disconnect
+ the signals.
+*/
+class RouteSignal
+{
+public:
+ RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip )
+ : _route( route ), _mcp( mcp ), _strip( strip )
+ {
+ connect();
+ }
+
+ ~RouteSignal()
+ {
+ disconnect();
+ }
+
+private:
+ ARDOUR::Route & _route;
+ MackieControlProtocol & _mcp;
+ Strip & _strip;
+
+<% signals.each do |x| -%>
+ sigc::connection _<%= x %>_connection;
+<% end -%>
+};
+
+}
+
+#endif
+EOF
+
+erb = ERB.new( class_def, 0, ">-" )
+erb.run
+
+impl_def = <<EOF
+#include "route_signal.h"
+
+#include <ardour/route.h>
+#include <ardour/panner.h>
+
+#include "mackie_control_protocol.h"
+
+using namespace Mackie;
+
+void RouteSignal::connect()
+{
+<% signals.each do |x| -%>
+ _<%=x%>_connection = _route.<%=connection_call(x)%>.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_<%=x%> ), &_route ) );
+<% end -%>
+}
+
+void RouteSignal::disconnect()
+{
+<% signals.each do |x| -%>
+ _<%= x %>_connection.disconnect();
+<% end -%>
+}
+EOF
+
+erb = ERB.new( impl_def, 0, ">-" )
+erb.run
diff --git a/libs/surfaces/mackie/scripts/simple_host.rb b/libs/surfaces/mackie/scripts/simple_host.rb
new file mode 100644
index 0000000000..a5c07f2abb
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/simple_host.rb
@@ -0,0 +1,137 @@
+#! /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 'mackie'
+
+buttons = {}
+pots = {}
+
+while !File.exist? ARGV[0]
+ sleep 0.010
+end
+
+file = File.open( ARGV[0], 'r+' )
+mck = Mackie.new( file )
+
+# faders to minimum. bcf2000 doesn't respond
+mck.write_sysex "\x61"
+
+# all leds off. bcf2000 doesn't respond
+mck.write_sysex "\x62"
+
+# get version. comes back as ASCII bytes
+version = mck.sysex "\x13\x00"
+puts "version: #{version.map{|x| x.chr}}"
+
+# respond to control movements
+while bytes = file.read( 3 )
+ puts "received: %02.x %02.x %02.x" % [ bytes[0], bytes[1], bytes[2] ]
+ output = nil
+ case bytes[0] & 0b11110000
+ when 0xe0
+ # fader moved, so respond if move is OK
+ output = bytes
+ when 0x90
+ # button pressed
+ case bytes[1]
+ when 0x68..0x6f
+ # do nothing - touch detection
+ puts "touch detect: %02.x" % bytes[2]
+ else
+ # treat all buttons as toggles
+ button_id = bytes[1]
+
+ # only toggle on release. Not working. All buttons send press
+ # and then release signals
+ if bytes[2] == 0
+ if buttons.include?( button_id )
+ # toggle button state
+ puts "button id #{buttons[button_id]} to #{!buttons[button_id]}"
+ buttons[button_id] = !buttons[button_id]
+ else
+ # create a new button as on
+ puts "adding button id #{button_id}"
+ buttons[button_id] = true
+ end
+ bytes[2] = buttons[button_id] ? 0x7f : 0
+ output = bytes
+ end
+ end
+ when 0xb0
+ # pots, jog wheel, external
+ case bytes[1]
+ when 0x10..0x17
+ #pot turned
+ pot_id = bytes[1] & 0b00000111
+ direction = bytes[2] & 0b01000000
+ delta = bytes[2] & 0b00111111
+ sign = direction == 0 ? 1 : -1
+
+ if pots.include? pot_id
+ current_led_pos = pots[pot_id]
+ else
+ current_led_pos = pots[pot_id] = 6
+ end
+ new_led_pos = current_led_pos + sign
+ new_led_pos = case
+ when new_led_pos <= 0
+ 0
+ when new_led_pos >= 11
+ 11
+ else
+ new_led_pos
+ end
+
+ pots[pot_id] = new_led_pos
+
+ puts "pot #{pot_id} turned #{sign} #{direction == 0 ? 'clockwise' : 'widdershins'}: %02.x to #{new_led_pos}" % delta
+
+ output = bytes
+ output[1] += 0x20
+ output[2] = 0b01000000
+ #~ modes:
+ #~ 0 - single dot
+ #~ 1 - boost/cut
+ #~ 2 - wrap
+ #~ 3 - spread
+ mode = pot_id < 4 ? pot_id : 0
+ output[2] |= ( mode << 4 )
+ output[2] += ( new_led_pos ) & 0b00001111
+ when 0x2e
+ # external controller
+ when 0x3c
+ # jog wheel
+ end
+ else
+ puts "don't know what this means"
+ end
+
+ # output bytes
+ if output
+ #sleep 0.1
+ puts "sending: %02.x %02.x %02.x" % [ output[0], output[1], output[2] ]
+ begin
+ res = file.write output
+ puts "res: #{res}"
+ file.flush
+ rescue => e
+ puts "oops #{e}"
+ file.close
+ file = File.open ARGV[0], 'r+'
+ end
+ end
+end
diff --git a/libs/surfaces/mackie/scripts/surface-cc-template.erb b/libs/surfaces/mackie/scripts/surface-cc-template.erb
new file mode 100644
index 0000000000..3b29be3249
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/surface-cc-template.erb
@@ -0,0 +1,95 @@
+<%#
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-%>
+/*
+ Generated by scripts/generate-surface.rb
+*/
+
+#include "<%= sf.name.downcase %>_surface.h"
+
+#include "controls.h"
+#include "mackie_button_handler.h"
+
+using namespace Mackie;
+
+void Mackie::<%= sf.name %>Surface::init_controls()
+{
+ // intialise groups and strips
+ Group * group = 0;
+
+ // make sure there are enough strips
+ strips.resize( <%= sf.groups.values.find_all{|x| x.name =~ /strip/}.size %> );
+
+% sf.groups.values.each do |group|
+ <%- if group.class == Strip -%>
+ <%- if group.name == 'master' -%>
+ group = new MasterStrip ( "<%=group.name%>", 0 );
+ <%- else -%>
+ group = new <%= group.class.name %> ( "<%=group.name%>", <%=group.ordinal - 1%> );
+ <%- end -%>
+ <%- else -%>
+ group = new <%= group.class.name %> ( "<%=group.name%>" );
+ <%- end -%>
+ groups["<%=group.name%>"] = group;
+ <%- if group.class == Strip -%>
+ strips[<%=group.ordinal - 1%>] = dynamic_cast<Strip*>( group );
+ <%- end -%>
+
+% end
+
+ // initialise controls
+ Control * control = 0;
+
+% sf.controls.each do |control|
+ group = groups["<%=control.group.name%>"];
+ control = new <%= control.class.name %> ( <%= control.id %>, <%= control.ordinal %>, "<%=control.name%>", *group );
+ <%=control.class.name.downcase%>s[0x<%=control.id.to_hex %>] = control;
+ controls.push_back( control );
+ <%- if control.group.class != Strip -%>
+ controls_by_name["<%= control.name %>"] = control;
+ <%- end -%>
+ group->add( *control );
+
+% end
+}
+
+void Mackie::<%= sf.name %>Surface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button )
+{
+ if ( bs != press && bs != release )
+ {
+ mbh.update_led( button, none );
+ return;
+ }
+
+ LedState ls;
+ switch ( button.id() )
+ {
+<%-
+buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
+buttons.each do |button|
+%>
+ case 0x<%= button.id.to_hex %>: // <%= button.name %>
+ switch ( bs ) {
+ case press: ls = mbh.<%= button.name %>_press( button ); break;
+ case release: ls = mbh.<%= button.name %>_release( button ); break;
+ case neither: break;
+ }
+ break;
+<% end %>
+ }
+ mbh.update_led( button, ls );
+}
diff --git a/libs/surfaces/mackie/scripts/surface-h-template.erb b/libs/surfaces/mackie/scripts/surface-h-template.erb
new file mode 100644
index 0000000000..2f54f66c47
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/surface-h-template.erb
@@ -0,0 +1,27 @@
+#ifndef mackie_surface_<%= sf.name.downcase %>_h
+#define mackie_surface_<%= sf.name.downcase %>_h
+/*
+ Generated by scripts/generate-surface.rb
+*/
+
+#include "surface.h"
+
+namespace Mackie
+{
+
+class MackieButtonHandler;
+
+class <%= sf.name %>Surface : public Surface
+{
+public:
+ <%= sf.name %>Surface( uint32_t max_strips ) : Surface( max_strips )
+ {
+ }
+
+ virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
+ virtual void init_controls();
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/scripts/test_controls.rb b/libs/surfaces/mackie/scripts/test_controls.rb
new file mode 100755
index 0000000000..782b0d427c
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/test_controls.rb
@@ -0,0 +1,9 @@
+#! /usr/bin/ruby
+
+require 'controls.rb'
+require 'pp'
+
+sf = Surface.new
+sf.parse
+sf.types.each{|k,v| puts "%02.x #{v}" % k}
+
diff --git a/libs/surfaces/mackie/scripts/transform.rb b/libs/surfaces/mackie/scripts/transform.rb
new file mode 100644
index 0000000000..e0221e188b
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/transform.rb
@@ -0,0 +1,26 @@
+class ElementHandler
+
+ def apply( anElement )
+ anElement.each {|e| handle(e)} if anElement
+ end
+
+ def handle( aNode )
+ if aNode.kind_of? REXML::Text
+ handleTextNode(aNode)
+ elsif aNode.kind_of? REXML::Element
+ handle_element aNode
+ else
+ return #ignore comments and processing instructions
+ end
+ end
+
+ def handle_element( anElement )
+ handler_method = "handle_" + anElement.name.tr("-","_")
+ if self.respond_to? handler_method
+ self.send(handler_method, anElement)
+ else
+ default_handler(anElement)
+ end
+ end
+
+end
diff --git a/libs/surfaces/mackie/scripts/write.rb b/libs/surfaces/mackie/scripts/write.rb
new file mode 100644
index 0000000000..20b72477e8
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/write.rb
@@ -0,0 +1,10 @@
+#! /usr/bin/ruby
+
+require 'mackie.rb'
+
+@file = File.open '/dev/snd/midiC2D0', 'r+'
+
+@led_8_on = [ 0x90, 0x18, 0x7f ]
+@hci = [ 0, 0xf7 ]
+@version_req = [ 0x13, 0, 0xf7 ]
+