Showing posts with label hardware. Show all posts
Showing posts with label hardware. Show all posts

Monday, October 3, 2011

Selecting a MIDI Device With Unimidi

There are a couple of recent changes to how MIDI devices can be selected with Unimidi.

Prompting the User

The first is the addition of a handy console prompt that asks the user to select a device and waits for input.  The code looks like this

require "unimidi"

@input = UniMIDI::Input.gets

on my computer, this results in

Select a MIDI input
1) IAC Device
2) Roland UM-2 (1)
3) Roland UM-2 (2)
> 

Note that when the user makes a selection, the device is returned enabled so you don't need to call @input.open on it.

Hard Coded

There's also been some changes to how hard coded selection can be done. As with the user prompt, now you can select and open a device at one fell swoop. Here's a couple of examples which both open the same output. (Device#use and Device#open are the same)

@output = UniMIDI::Output.open(0)
@output = UniMIDI::Output.use(:first)
And of course, you can select and open the device separately if you wish
@input = UniMIDI::Input.all[0].open
@input = UniMIDI::Input.first.open

Tuesday, June 7, 2011

Unimidi: Platform independent realtime MIDI IO in Ruby

Unimidi is a universal Ruby library for realtime MIDI IO.

It currently works with MRI 1.9.2 on Linux, OSX, Windows/cygwin and under JRuby in 1.9 mode on any platform.

gem install unimidi

No compilation is required, install the gem and start playing.

Unimidi deals in raw bytes rather than high level message objects with the intention of allowing people to use whichever message objects or helpers they choose. There's a few libraries out there for that, including one by me.

Under the hood, unimidi is essentially linkage to a set of platform specific C bindings (that use ruby-ffi).  Or in the case of JRuby, some code that wraps javax.sound.   These platform specific libraries are broken out in to their own gems and the appropriate one should install automatically when the unimidi gem is installed. In unusual cases where your platform isn't recognized, all of those gems will be installed.  You can see which library goes with which platform on the unimidi github page. It is possible to use those gems on their own, but currently none of them contain any features that aren't also made available through unimidi.

Here's a couple of quick examples to get started

Sending notes to a MIDI output

First, load unimidi and then define a set of note values and a duration value for how long the notes will be held out.

require 'unimidi'

notes = [36, 40, 43, 48, 52, 55, 60, 64, 67] # C E G arpeggios
duration = 0.1

Next, select an output.  To list all available outputs, you can do unimidi list from the command line or UniMIDI::Device.list in irb.  In this case, the first output is selected and enabled. (here is another post with more information about selecting a device)

output = UniMIDI::Output.open(:first)

Now open that output.  Passing a block to open is optional but it ensures that the device is closed when the program is finished.  This can alleviate some headaches when multitasking MIDI/audio programs.  Note that it's also possible to select, open and use multiple inputs and outputs concurrently.

output.open do |output|

  notes.each do |note|
    output.puts(0x90, note, 100) # note on message
    sleep(duration)  # wait
    output.puts(0x80, note, 100) # note off message
  end

end

Output#puts can also be used for sysex messages in the same manner as note messages, a la:
output.puts(0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7)
Note that some OS's will silently reject invalid sysex or short messages.

You can also Output#puts a string of hex bytes if you prefer to work that way
output.puts("904040")
output.puts("F04110421240007F41F7")

Working with input

Input can be collected two ways. First, unimidi has a method Input#gets which waits for input before returning. It works exactly the way Ruby's $stdin.gets works when waiting for keyboard input.

Here is a demonstration of Input#gets

Select an input and open it...

input = UniMIDI::Input.first
input.open do |input|

  $stdout.puts "send some MIDI to your input now..."

  loop do
    m = input.gets
    $stdout.puts(m)
  end

end

When MIDI input is received, you'll see inspected Hash objects like this:

{ :data => [144, 60, 100], :timestamp => 1024 }

In this case [144, 60, 100] is a note-on message for channel 0, note C4 (aka 60), with velocity 100.  The timestamp is the number of milliseconds since the input was opened.

The other more advanced way to receive input is by polling Input#buffer manually.  As new messages are received, they are added to that buffer array.  I normally use this with a pointer to keep track of the index of the last message seen.  Polling this way would for example, allow you to create your own background thread to collect input while your program does other things.  The library Topaz, which I wrote about in my last post, collects clock messages from unimidi that way.

Your computer should be able to receive data with 1-2 millisecond accuracy

http://github.com/arirusso/unimidi

Thursday, May 26, 2011

Topaz: MIDI syncable tempo in Ruby

Topaz is a Ruby based music tempo generator / analyzer 

gem install midi-topaz

It gives you the ability to time events to a given rate in beats per minute
("sequencer" is some kind of imagined sequencer)

@tempo = Topaz::Tempo.new(130) { sequencer.step! }

or synchronize them to incoming MIDI clock messages

@input = UniMIDI::Input.first.open # a midi input

@tempo = Topaz::Tempo.new(@input) { sequencer.step! }

Topaz can also act as a MIDI master clock.  If a MIDI output object is passed to Topaz, MIDI start/stop/clock signals will automatically be sent to that output at the appropriate time

@output = UniMIDI::Output.first.open # a midi output

@tempo = Topaz::Tempo.new(120, :midi => @output) { sequencer.step! }

an input can be used along with multiple outputs simultaneously

@tempo = Topaz::Tempo.new(@input, :midi => [@output1, @output2]) { sequencer.step! }

once everything is all set, start the clock

@tempo.start

Note that if you are syncing to external clock, nothing will happen until Topaz receives a start or clock message

Other stuff to note...

Whether or not you're using an internal or external clock source, the event block will be called at quarter note intervals by default. If you wish to change this, set the option :interval.  In this case, 16th notes will occur at 138 beats per minute where one beat equals one quarter note

@tempo = Topaz::Tempo.new(138, :interval => 16) { sequencer.step! }

View the current tempo, which is calculated by Topaz if you're syncing to an external source.
(this feature is in progress. you might receive some unreliable values - 5/26/2011)

@tempo.tempo
    => 132.422000

Run the generator in a background thread by passing :background => true to Tempo#start

@tempo.start(:background => true)

@tempo.join # join later if you want

Pass a block in that will stop the clock when it evaluates to true

@tempo.stop_when { @i.eql?(20) }

http://github.com/arirusso/topaz