Showing posts with label event-based. Show all posts
Showing posts with label event-based. Show all posts

Wednesday, August 31, 2011

MicroMIDI: Custom Events

While MicroMIDI has built-in functions such as transpose to process MIDI input, these functions may not always suit your purpose musically.

In those situations, you can bind your own input events.

Here is an example similar to the one in the last post except that only the notes C, E, and G are transposed
require "micromidi"

@input = UniMIDI::Input.use(:first)
@output = UniMIDI::Output.use(:first)

MIDI.using(@input, @output) do

  thru_except :note

  receive :note do |message|
    message.octave += 1 if %w{C E G}.include?(message.note_name)
    output message
  end

  join

end

For the sake of expressiveness, there are many permutations of each of these methods. I recommend reading the rdoc for the MicroMIDI Instructions classes to get a handle on what's possible.

http://github.com/arirusso/micromidi

Next: Shorthand with MicroMIDI

MicroMIDI: MIDI Thru and Processing

The simplest way to work with MIDI input in MicroMIDI is to use its built-in shortcuts for MIDI Thru and processor classes.

Here's an example where both kinds of shortcuts are used. An input and output are passed in, all messages that are received by the input are sent to the output (Thru) with the exception of notes which will be transposed up one octave before being sent to the output. The transpose function is an example of a processor.
@i = UniMIDI::Input.use(:first)
@o = UniMIDI::Output.use(:first)

MIDI.using(@i, @o) do

  thru_except :note { |msg| transpose(msg, :octave, 1) }

  join

end

http://github.com/arirusso/micromidi

Next: Custom Events with MicroMIDI

MicroMIDI: a Ruby DSL for MIDI

After a month of moving I'm finally getting back into some music projects. I've got some more flashy things in the works but first here is a Ruby DSL called MicroMIDI that brings all of the MIDI projects mentioned in this post together along with some new tricks in to a package a bit more suited for live coding and one-off scripts.

Being that it's an interface for four libraries, there's a lot of functionality. I break down each concept in the following posts:

http://github.com/arirusso/micromidi

Monday, June 27, 2011

High-level realtime MIDI IO with Ruby

Update (9/8/2011): I've created another library that wraps all of the concepts of this post in a Ruby DSL and adds shorthand notation and some other fun things. Read about MicroMIDI here


Understandably, a few people have asked me for advice on how to input and output MIDI in a human friendly way with unimidi so I've decided to put together a quick tutorial. I'll be focusing on two gem libraries that I wrote: midi-message, which deals soley with MIDI message objects, and midi-eye, a library for reacting to MIDI input. Of course, it should be mentioned that there's no one way to do this with unimidi.  You can use whatever MIDI objects you like or create your own classes-- unimidi just deals in raw low-level bytes.  There are other libraries such as midilib that provide an intriguing alternative and could work pretty easily with unimidi.  Or one could get creative and go off with a totally unconventional approach as well. For the examples that follow, I'm using a MIDI input and output that I specify with unimidi.
require 'unimidi'

@input = UniMIDI::Input.use(:first)
@output = UniMIDI::Output.use(:first)

If you copy and paste these, they will just open the first MIDI devices available on your computer. You should determine which MIDI devices you want to use and edit these statements to suit your setup. (here's a blog post that goes into more detail on this)

Dealing with MIDI input using midi-eye

My preferred way of dealing with MIDI input is to react to arriving messages with an event listener.  Midi-eye makes this easy and its constructor accepts a unimidi input to attach to.  Here is an example that will react to all incoming messages in the same way by printing them to the screen

require 'midi-eye'

listener = MIDIEye::Listener.new(@input)
listener.on_message do |event|
  puts event[:timestamp]
  puts event[:message]
end

listener.start
Chances are if you're working with MIDI input that you will want to cherry-pick certain messages, or at least react in a different way depending on what type of message you've received eg. a note message, control change, etc.  To accomplish this, arguments can be passed to the Listener#listen_for method which will match against properties of the incoming messages
listener.listen_for(:class => [MIDIMessage::NoteOn, MIDIMessage::NoteOff]) do |event|

  # raise the note value by an octave
  event[:message].note += 12

  # send the altered note message to the output you chose earlier
  @output.puts(event[:message])

end

listener.start
In this example, I take all note messages (identified by their class), transpose them up one octave and send them to my MIDI output. You can add as many of these callbacks as you like, just keep calling Listener#listen_for. While that type of matching will be useful in a lot of cases, it is limited by the fact that it only matches positively against the properties and values you pass in. If you need more complex matching, I recommend putting a conditional statement within the callback.
listener.listen do |event|

  # is this a note above C3?
  if event[:message].respond_to?(:note) && event[:message].note > 48

    # if so, lower the note value by a fifth
    event[:message].note -= 7

  end

  # and send the message to the unimidi output
  @output.puts(event[:message])

end

listener.start

(listen and listen_for are actually the same method, I just think it looks cleaner to call listen when there is no matching happening)

Threading

Pass :background => true to listener.start to have the listener work only in a background thread. This will allow you to run other listeners or foreground threads while that particular listener is running in the background.

Output MIDI using midi-message

In those examples, I sent messages to an output-- but I didn't create those messages myself. The midi-message library allows you to create messages like that yourself in a flexible way.

require 'midi-message'
include MIDIMessage
Here are three different MIDI note-on messages created using three different methods.
messages = []

messages << NoteOn.new(0, 48, 64) # C3

messages << NoteOn["E3"].new(0, 100)

with(:channel => 0, :velocity => 100) do
  messages << note_on("G3")
end
With those message objects in hand, I pass each to UniMIDI::Output#puts the same way you saw earlier.
messages.each { |message| @output.puts(message) }
That's it... and it works the same for all types of MIDI messages including sysex. You can find some info on creating sysex messages here.

http://github.com/arirusso/midi-eye
http://github.com/arirusso/midi-message
http://github.com/arirusso/unimidi