Skip to content

Instantly share code, notes, and snippets.

@RyanScottLewis
Created April 13, 2011 13:05
Show Gist options
  • Save RyanScottLewis/917499 to your computer and use it in GitHub Desktop.
Save RyanScottLewis/917499 to your computer and use it in GitHub Desktop.
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'midi-winmm'
# tested with midi-winmm, but I assume will work with alsa-rawmidi
class Numeric
def to_hex_str
"%02x" % self
end
end
class String
def to_hex_str
unpack('U' * length).collect { |x| "%02x" % x }.join
end
end
module MIDIWinMM
module Device
attr :id, :info, :type, :name, :enabled
end
end
# Every SysEx message sent to the BCR2000 or BCF2000
# uses a language called BCL or B-Control Language..
# Regrettably, Behringer never released the specs
# which makes it hard as hell to program.
#
# From here on, the BCR2000 and BCF2000 will simply
# be called "BCR" and "BCF", respectably.
# When refering to both devices, I'll simply write "BC".
module BControl
module Definitions
SysExStart = 0xf0
SysExEnd = 0xf7
SysExBehringer = [0x00, 0x20, 0x32]
Any = 0x7F
BCF2000 = 0x14
BCR2000 = 0x15
end
module SysEx
include Definitions
# Generates an array of bytes that results in a sysex message
def self.generate(id, model, command, data)
result = []
result << SysExStart
result = result + SysExBehringer
result << id - 1 << model << command
# TODO: if data.is_a?(Array) then ADD the data array, not CONCAT
result << data
result << SysExEnd
end
end
# Cached list of devices. Use DeviceList#refresh! to ..well.. refresh the list.
class DeviceList < Array
def initialize; refresh!; end
def refresh!; replace(MIDIWinMM::Device.all); end
def all_of_type(type); find_all { |d| d.type == type }; end
end
Devices = DeviceList.new
class Device
attr :id, :model, :opened
def initialize(&blk)
@input_device = nil
@output_device = nil
@opened = false
@id = Definitions::Any
@model = Definitions::Any
end
def input_device(&blk)
raise("Cannot change input device when opened") if @open
block_given? ? @input_device = Devices.all_of_type(:input).find(&blk) : @input_device
end
def output_device(&blk)
raise("Cannot change output device when opened") if @open
block_given? ? @output_device = Devices.all_of_type(:output).find(&blk) : @output_device
end
def input_device?; !!@input_device; end
def output_device?; !!@output_device; end
def open?; @opened; end
alias :opened? :open
def id=(val)
@id = (1..16).include?(val) ? val : Definitions::Any
end
def model=(val)
is_valid_model = [Definitions::BCR2000, Definitions::BCF2000].include?(val)
@model = is_valid_model ? val : Definitions::Any
end
def open
@opened = true
@input_device.open if input_device?
@output_device.open if output_device?
end
def close
@opened = false
@input_device.close if input_device?
@output_device.close if output_device?
end
private # protected
def generate_sysex(command, data)
SysEx.generate(@id, @model, command, data)
end
end
class BCR2000 < Device
def initialize(id = Definitions::Any)
@id = id
@model = Definitions::BCR2000
end
def select_preset(preset_number)
sysex_message = generate_sysex(0x22, preset_number - 1)
if output_device? && open?
@output_device.puts(sysex_message)
else
sysex_message
end
end
end
end
# ======================================================
require 'pp'
bcr2000 = BControl::BCR2000.new
bcr2000.id = 1
bcr2000.output_device { |d| d.name == "BCR2000" } # { |d| d.id == 0 }
bcr2000.open
bcr2000.select_preset(5)
bcr2000.close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment