class CoreMIDI::Source

Type of endpoint used for input

Attributes

buffer[R]

Public Class Methods

all() click to toggle source

All input endpoints @return [Array<Source>]

# File lib/shmidi/ffi-coremidi-patch.rb, line 103
def self.all
  Endpoint.all_by_type[:source]
end
first() click to toggle source

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
last() click to toggle source

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() click to toggle source

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(options = {}) { |self| ... } click to toggle source

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
Also aliased as: open, start
gets() click to toggle source

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
Also aliased as: read
gets_bytestr()
Alias for: gets_s
gets_s() click to toggle source

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
Also aliased as: gets_bytestr
open(options = {}, &block)
Alias for: enable
read()
Alias for: gets
start(options = {}, &block)
Alias for: enable

Protected Instance Methods

connect() click to toggle source

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
Also aliased as: connect?
connect?()
Alias for: connect

Private Instance Methods

enqueue_message(bytes, timestamp) click to toggle source

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
find_next_length_index(data) click to toggle source

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
get_event_callback() click to toggle source

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
get_message_formatted(raw, time) click to toggle source

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_messages(packet_list) click to toggle source

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_buffer() click to toggle source

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_port() click to toggle source

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
queued_messages() click to toggle source

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
queued_messages?() click to toggle source

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(now) click to toggle source

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