Showing posts with label Live Coding. Show all posts
Showing posts with label Live Coding. 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

Thursday, September 29, 2011

A Note About Live Coding in IRB with OSX

Apparently some versions of readline for OSX cause IRB to block thread-passing while it's waiting for user input at the prompt.

If you're experiencing problems live coding in IRB, such as starting the Diamond arpeggiator and nothing happening, it could be because of this. Unfortunately, as far as I know, the only way to fix it is to disable readline. You can do this by adding this line to ~/.irbrc

IRB.conf[:USE_READLINE] = false

This is annoying, because then you'll no longer have the ability to recall previously used commands and things like that. I've been meaning to get text editor based live coding up and running so maybe this is the time

Saturday, September 10, 2011

Generating Sysex Messages with MicroMIDI

Recently, the idea of converting MIDI Control Change messages to Sysex on the fly has come up a couple of times. One could use this to control a synth such as a Roland MKS or Yamaha DX7 that only accepts Sysex for control with a regular MIDI knob controller.

The following is a simplified example of doing this with MicroMIDI.
@i = UniMIDI::Input.use(:first)
@o = UniMIDI::Output.use(:first)
  
MIDI.using(@i, @o) do
  
  node :roland, :model_id => 0x42, :device_id => 0x10
  
  *@my_map =
    [0x40, 0x7F, 0x00],   
    [0x41, 0x7F, 0x00],
    [0x42, 0x7F, 0x00]
  
  receive :cc do |message|
      
    command @my_map[message.index - 1], message.value
      
  end
  
  join
  
end
Defining a Node

I won't get into too much background on Sysex but there are two concepts that one must understand in order to generate Sysex with MicroMIDI.

The first concept is what I call a Sysex Node: there are up to three bytes of data used in each Sysex message to identify the synth/module/destination/node/etc where the message is intended to be sent. This is not unlike the MIDI channel in a regular short message except that it's three bytes. Two of those bytes pinpoint the make and model of the synth while the third byte identifies the individual synth (device ID) in case you have multiple Yamaha DX7's or whatever the case.

MicroMIDI allows you to define these bytes as a sticky value using the node function.

Since I'm only using one synth for the entire example, I call the node function before setting up the input event to catch Control Change messages. (If you are using multiple synths and multiple events you would call node in each event block). The arguments represent the Manufacturer ID and the optional Model ID and Device ID. The Manufacturer can be referred to by a symbol (as above) or a string if its name is found in the manufacturer constants in midi.yml (by all means, add yours and do a pull request).

Now here's the annoying part: different brands and synth models use this Node data differently. For instance, I believe some devices don't understand messages with a model ID in them. In those cases just leave out whatever needs to be omitted from your messages. As I learn more about this myself, perhaps I can have this function streamline accepting the proper data for major synth types.

Command vs Request

The other concept to understand is that Sysex messages can (in theory) either be a command or request. This is pretty simple, and if you are creating a controller program you'll deal mostly in commands. In the case of the example above, we send a command
command @my_map[message.index - 1], message.value
When using the command function, the first argument is the sysex address and the second is the value to assign. The value can either be a number, as in this case, or an array of bytes. When making a request, the first argument is also the address but the second argument is the size of the response (in bytes) that you expect to receive
request 0x43, 43
If all else fails...

Due to the fact that Sysex hasn't had a truly concrete spec, some devices will use messages that don't really adhere to the command/request format. In those cases, you can just use the generic sysex command like this
sysex 0x1, 0x2, 0x3, 0x4
With no node specified, this will give you a message composed of
F0 01 02 03 04 F7
In other words, sysex will create a message and not perform any validation or add a checksum to it. You can still use node with these message-- it will append those bytes immediately after the F0 start byte as it would with a command or request.

tldr, Sysex is tricky to to objectify

If you'd like to gain a deeper understanding of how sysex messages work, this is a good tutorial (if Roland-centric) and I often referred back to it while creating MicroMIDI and the libraries that support it.

http://github.com/arirusso/micromidi

Next: More MicroMIDI Tricks

Wednesday, August 31, 2011

MicroMIDI: Shorthand

Most MicroMIDI methods and common symbol arguments have shorthand aliases intended to reduce keystrokes while live coding. The following is the example from the last post, re-done using only shorthand.
@i = UniMIDI::Input.use(:first)
@o = UniMIDI::Output.use(:first)

M(@i, @o) do

  tu :n

  rc :n { |m| tp(m, :oct, 1) if %w{C E G}.include?(m.note_name) }

  j

end

See the alias definitions here for the complete list.

http://github.com/arirusso/micromidi

Next: Generating Sysex Messages

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: MIDI Messages and Output

Here's an example where MicroMIDI sends some MIDI messages to an output. (see an example here which explains selecting an output...)

require "micromidi"

@o = UniMIDI::Output.use(:first)

MIDI.using(@o) do

  note "C"
  off 

  cc 5, 120

  play "C3", 0.5

end 

Running this code sends the following to @o:

* note C2 (2 is the default octave)
* note-off for C2, since that was the last note sent
* sets controller number 5 to 120
* note C3, waits half of a second and then note-off for C3

By default, any time you call a method that returns a MIDI message object, it's automatically sent to any outputs that are passed in. You can toggle this feature by calling

output false

or

output true

You can also prevent only a single command from sending output by setting the output option:

note "c", :output => false
Sticky Values

If you work with MIDI often, you may have noticed that there was no mention of MIDI channel or velocity in the last example. Most of the time, sending a note-on or note-off message requires those values. In addition, for the first message I didn't specify what octave the note C should be.

In MicroMIDI, channel, velocity and octave are sticky values. When you open a MicroMIDI block, those values default to 0, 100 and 2 respectively. These sticky values will be used by any commands that need them. You can also pass a channel or velocity value to a command, temporarily overriding the sticky value. Providing the octave to a note ala note "c4" will override the sticky octave.

Here's an example where the sticky values are used, changed and overriden.

MIDI.using(@o) do

channel 1

  note "C4"
  off

  octave 5
  velocity 60

  note "E", :channel => 2
  off

  channel 3

  note "C3"
  off

end 

What winds up being sent to @o is:

* note C4 (channel 1, vel 100)
* note-off C4 (channel 1, vel 100)
* note E5 (channel 2, vel 60)
* note-off E5 (channel 2, vel 60)
* note C3 (channel 3, vel 60)
* note-off C3 (channel 3, vel 60)

http://github.com/arirusso/micromidi

Next: MIDI Thru and Processing 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

Sunday, July 3, 2011

Live coding with Diamond

I've created a screencast video to show some basic live coding functionality with Diamond. In it, I use Diamond to send MIDI to a 2-voice modular synth and kick drum module.



(sound starts around 55 seconds in)

Here is a quick explanation of what was happening in the video.

Some people prefer to do live coding with a text editor, which is great because you can work a lot faster. However, for the sake of keeping this tutorial to the point, I'll assume you're working in IRB.

First, I'll start up IRB and set up an arpeggiator using the similar options as in the last post. That should look something like this:
require "diamond"

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

arp = Diamond::Arpeggiator.new(175, :interval => 7, :midi => @output, :range => 4, :rate => 8)

chord = ["C3", "G3", "Bb3", "A4"]

arp << chord

arp.start
At that point we start hearing arpeggios. Since the arpeggiator is started in a background thread by default, and I can make changes to it while it plays.
arp.rate = 16
arp.gate = 20
arp.range = 3

# etc

In the video, I use the Arpeggiator#rest_every method to insert musical rests in to the arpeggio sequence. If I do
arp.rest_every(5)
notes become muted on every fifth beat.

Syncing multiple Arpeggiators

Another thing I do in the video is sync multiple arpeggiators to each other.

Assuming I still have that setup running, I'm going to add another arpeggiator.
arp2 = Diamond::Arpeggiator.new(138, :rate => 2, :output_channel => 1)

arp2 << ["C3", "G4", "A4"]
Because I used the :output_channel => 1 option, any notes coming from arp2 will be outputted on MIDI channel 1. Now I can sync this new arpeggiator to the old one -- the clock of the first arpeggiator, arp will drive arp2.
arp.sync(arp2)
By default, the sync will wait for the next time the arpeggiator pattern repeats to take hold. You can override this by passing in :now => true which will activate the sync on the next downbeat.
arp.sync(arp2, :now => true)
You can sync as many arpeggiators as you like.

Trouble getting this working in OSX? One solution here.

Thanks for reading


http://github.com/arirusso/diamond

Diamond, MIDI Arpeggiator in Ruby



Diamond is a MIDI arpeggiator in Ruby.

It features all of the classic functions of a MIDI arpeggiator plus the ability to live code, algorithmically generate and modify patterns, chain and sync instances and more


This is a quick screencast video I did while live coding Diamond. (read more about this here...)
(Sounds starts around 55 seconds in)

Getting started

Ruby 1.9.2 or JRuby in 1.9 mode are required.

It installs with a typical gem installation...
gem install diamond
Here is a very basic example to get started:
require "diamond"
First, select a MIDI output using unimidi. (more...)
@output = UniMIDI::Output.gets
The Arpeggiator has a number of optional parameters (more...). For the sake of keeping this demo simple, here is a straightforward setup:
opts = { 
  :gate => 90, 
  :range => 4, 
  :interval => 7,
  :midi => @output,
  :pattern => Diamond::Pattern["UpDown"],    
  :rate => 8
}
(read more about what these options mean)

Now create an Arpeggiator object, passing in a tempo value and the options chosen before. In this case the tempo will be 138 BPM
arp = Diamond::Arpeggiator.new(138, opts)
Of course, an Arpeggiator needs notes to work with. As you might expect, it's easy to use a MIDI input for that (see example). However, again for the sake of simplicity here's a chord in Ruby
chord = ["C3", "G3", "Bb3", "A4"]
Use Arpeggiator#add and Arpeggiator#remove to change the notes that the arpeggiator sees. (Arpeggiator#<< is the same as add)
arp.add(chord)
arp.add("C5")
arp << "A4"
By default, the arpeggiator will run in a background thread so if you are working in IRB this will allow you to live code, sync it other arpeggiators, or just run another foreground process. (To start in the foreground, just pass :focus => true to Arpeggiator#start)
arp.start
At that point, the arpeggiator starts playing. All of its options can be controlled on the on the fly.
arp.rate = 16
arp.gate = 20
arp.remove("C5", "A4")
Here are examples showing how to use some of Diamond's other features
Thanks for reading
http://github.com/arirusso/diamond