class CoreMIDI::Source
Type of endpoint used for input
Attributes
Public Class Methods
All input endpoints @return [Array<Source>]
# File lib/shmidi/ffi-coremidi-patch.rb, line 103 def self.all Endpoint.all_by_type[:source] end
Shortcut to the first available input endpoint @return [Source]
# File lib/shmidi/ffi-coremidi-patch.rb, line 91 def self.first Endpoint.first(:source) end
Shortcut to the last available input endpoint @return [Source]
# File lib/shmidi/ffi-coremidi-patch.rb, line 97 def self.last Endpoint.last(:source) end
Public Instance Methods
Close this input @return [Boolean]
# File lib/shmidi/ffi-coremidi-patch.rb, line 72 def close #error = API.MIDIPortDisconnectSource( @handle, @resource ) #raise "MIDIPortDisconnectSource returned error code #{error}" unless error.zero? #error = API.MIDIClientDispose(@handle) #raise "MIDIClientDispose returned error code #{error}" unless error.zero? #error = API.MIDIPortDispose(@handle) #raise "MIDIPortDispose returned error code #{error}" unless error.zero? #error = API.MIDIEndpointDispose(@resource) #raise "MIDIEndpointDispose returned error code #{error}" unless error.zero? if @enabled @enabled = false true else false end end
Enable this the input for use; can be passed a block @return [Source]
# File lib/shmidi/ffi-coremidi-patch.rb, line 56 def enable(options = {}, &block) @enabled = true unless @enabled if block_given? begin yield(self) ensure close end end self end
An array of MIDI event hashes as such:
[ { :data => [144, 60, 100], :timestamp => 1024 }, { :data => [128, 60, 100], :timestamp => 1100 }, { :data => [144, 40, 120], :timestamp => 1200 } ]
The data is an array of Numeric bytes The timestamp is the number of millis since this input was enabled
@return [Array<Hash>]
# File lib/shmidi/ffi-coremidi-patch.rb, line 22 def gets # SINM ADDED @queue.pop # SINM COMMENTED OUT # until queued_messages? # # per https://github.com/arirusso/unimidi/issues/20#issuecomment-44761318 # sleep(0.0001) # patch to prevent 100% CPU issue with some midi controllers # end # messages = queued_messages # @pointer = @buffer.length # messages end
Same as Source#gets
except that it returns message data as string of hex digits as such:
[ { :data => "904060", :timestamp => 904 }, { :data => "804060", :timestamp => 1150 }, { :data => "90447F", :timestamp => 1300 } ]
@return [Array<Hash>]
# File lib/shmidi/ffi-coremidi-patch.rb, line 45 def gets_s messages = gets messages.each do |message| message[:data] = TypeConversion.numeric_bytes_to_hex_string(message[:data]) end messages end
Protected Instance Methods
Base initialization for this endpoint – done whether or not the endpoint is enabled to check whether it is truly available for use
# File lib/shmidi/ffi-coremidi-patch.rb, line 111 def connect enable_client initialize_port @resource = API.MIDIEntityGetSource(@entity.resource, @resource_id) error = API.MIDIPortConnectSource(@handle, @resource, nil ) initialize_buffer # SINM ADDED @queue = Queue.new @sysex_buffer = [] @start_time = Time.now.to_f error.zero? end
Private Instance Methods
Add a single message to the buffer @param [Array<Fixnum>] bytes Message data @param [Float] timestamp The system float timestamp @return [Array<Hash>] The resulting buffer
# File lib/shmidi/ffi-coremidi-patch.rb, line 133 def enqueue_message(bytes, timestamp) if bytes.first.eql?(0xF0) || !@sysex_buffer.empty? @sysex_buffer += bytes if bytes.last.eql?(0xF7) bytes = @sysex_buffer.dup @sysex_buffer.clear end end # SINM ADDED @sysex_buffer.empty? ? get_message_formatted(bytes, timestamp) : nil # SINM COMMENTED OUT # @buffer << get_message_formatted(bytes, timestamp) if @sysex_buffer.empty? # @buffer end
Get the next index for “length” from the blob of MIDI data @param [Array<Fixnum>] data @return [Fixnum]
# File lib/shmidi/ffi-coremidi-patch.rb, line 205 def find_next_length_index(data) last_is_zero = false data.each_with_index do |num, i| if num.zero? if last_is_zero return i + 1 else last_is_zero = true end else last_is_zero = false end end end
The callback fired by coremidi when new MIDI messages are in the buffer
# File lib/shmidi/ffi-coremidi-patch.rb, line 159 def get_event_callback Proc.new do |new_packets, refCon_ptr, connRefCon_ptr| begin # p "packets received: #{new_packets[:numPackets]}" timestamp = Time.now.to_f messages = get_messages(new_packets) # SINM ADDED @queue.push(messages.map do |message| enqueue_message(message, timestamp) end.compact) # SINM COMMENTED OUT # messages.each { |message| enqueue_message(message, timestamp) } rescue Exception => exception Thread.main.raise(exception) end end end
Give a message its timestamp and package it in a Hash @return [Hash]
# File lib/shmidi/ffi-coremidi-patch.rb, line 228 def get_message_formatted(raw, time) { :data => raw, :timestamp => timestamp(time) } end
Get MIDI messages from the given CoreMIDI
packet list @param [API::MIDIPacketList] new_packets The packet list @return [Array<Array<Fixnum>>] A collection of MIDI messages
# File lib/shmidi/ffi-coremidi-patch.rb, line 180 def get_messages(packet_list) count = packet_list[:numPackets] first = packet_list[:packet][0] data = first[:data].to_a messages = [] messages << data.slice!(0, first[:length]) (count - 1).times do |i| length_index = find_next_length_index(data) message_length = data[length_index] unless message_length.nil? packet_start_index = length_index + 2 packet_end_index = packet_start_index + message_length if data.length >= packet_end_index + 1 packet = data.slice!(0..packet_end_index) message = packet.slice(packet_start_index, message_length) messages << message end end end messages end
Initialize the MIDI message buffer @return [Boolean]
# File lib/shmidi/ffi-coremidi-patch.rb, line 247 def initialize_buffer @pointer = 0 @buffer = [] def @buffer.clear super @pointer = 0 end true end
Initialize a coremidi port for this endpoint @return [Boolean]
# File lib/shmidi/ffi-coremidi-patch.rb, line 237 def initialize_port @callback = get_event_callback port = API.create_midi_input_port(@client, @resource_id, @name, @callback) @handle = port[:handle] raise "MIDIInputPortCreate returned error code #{port[:error]}" unless port[:error].zero? true end
New MIDI messages from the queue
# File lib/shmidi/ffi-coremidi-patch.rb, line 149 def queued_messages @buffer.slice(@pointer, @buffer.length - @pointer) end
Are there new MIDI messages in the queue?
# File lib/shmidi/ffi-coremidi-patch.rb, line 154 def queued_messages? @pointer < @buffer.length end
Timestamp for a received MIDI message @return [Fixnum]
# File lib/shmidi/ffi-coremidi-patch.rb, line 222 def timestamp(now) (now - @start_time) * 1000 end