SH101 style sequencer as ruby UniMIDI script

I came across a youtube video which shows an Axoloti set up to emulate an SH101 style sequencer being trigged by an incoming note.

I had not seen this style of sequencer before, and was interested in the patterns that can be generated particularly when you have say a 7 step sequence that has 8 triggers per bar, so the resulting pattern kind of slides around the main rhythm.

Since I don’t yet have an Axoloti I put toget a UniMIDI script to get the same result. I use this with my Electribe 2 Sampler, which is connected via USB to my Macbook running the script.

On the E2S I have a pattern set up as follows:
Part 1 – this is set so Osc = ‘Audio In Mono’ – which makes it effectively muted. The E2S sequencer has every step to this part off. Keys pressed on this part get sent out on Midi Channel 1.

Part 2 – this is set to one of the built in wave forms (not a sample). Also, the ‘AMP EG’ must be selected with a highish decay/release value. The E2S sequencer has every step to this part off. The decay/release is needed because the incoming notes will have a not off immediately after the note on, so the actual audible duration depends on the decay time. This is also why samples don’t work too well, the envelope generator only works how it needs to for the built in waveforms.

Parts 3-15 – Set up with whatever Osc and pattern you like.

Part 16 – this is set so Osc = ‘Audio In Mono’ – which makes it effectively muted. Any note sent on this channel (e.g. via the E2S sequenver) will act as a trigger for the SH101 style sequencer.

To use:

Set up the E2S as above. Then go to ‘part 1′ and press the ‘IFX on’ button so it lights red. This puts the SH101 script into ‘record mode’. Any notes you play on part 1 in this mode get added to the sequence (and also echoed back on Channel 2). Press ‘IFX On’ again (so light goes off) to put the SH101 sequencer into ‘play’ mode. Now go to ‘part 16′ and turn on/off whatever steps you want to trigger a note. Press play.

Going to ‘part 1′ and pressing another note key while in play mode will transpose the current sequence.

—-

!– HTML generated using hilite.me –>

#!/usr/bin/ruby

dir = File.dirname(File.expand_path(__FILE__))
$LOAD_PATH.unshift dir + "/../lib"

require "unimidi"

IN_CHANNEL=1
TRIGGER_CHANNEL=16
OUT_CHANNEL=2
REC_MODE_CC=0x68	#on my electribe 2, this is triggered by pressing the 'IFX ON' button.

DEBUG_LEVEL=1	#0=silent,1=major events, 2=all events

@input = UniMIDI::Input.gets
@output = UniMIDI::Output.gets

@rec_mode=false
@seq_length=0
@seq=[]
@seq_ptr=0
@transpose_amount=0

def debug_log (s,level)
	puts s unless level>DEBUG_LEVEL
end
def execute_status(s)
	debug_log("#{"%2X" % s[0]},#{s[1]},#{s[2]}",2)
	if ((s[0])==0xb0+IN_CHANNEL-1) && (s[1]==REC_MODE_CC)then
		new_rec_mode=(s[2]>64)
		if (@rec_mode==false) && (new_rec_mode==true) then
			@seq=[]
			@seq_length=0
			@seq_ptr=0
		end

		@rec_mode=new_rec_mode
		debug_log("REC_MODE: #{@rec_mode}",1)
	end

	if (s[0]==0x90+IN_CHANNEL-1) && (s[2]>1)then #it's a note-on event on input channel

		if (@rec_mode) then
			@seq[@seq_length]=s[1]
			@seq_length+=1
			@output.puts(0x8F+OUT_CHANNEL, s[1],  64) #echo so can hear what was just played
			@output.puts(0x8F+OUT_CHANNEL, s[1],  0) #immediate note off 

			debug_log("added note - seq length #{@seq_length}",1)
		end

		if (!@rec_mode) && (@seq_length>0) && (IN_CHANNEL!=TRIGGER_CHANNEL) then # not recording or a trigger so do a transpose
			@transpose_amount=s[1]-@seq[0]
			debug_log("transpose amount now - #{@transpose_amount}",1)
		end

	end
	if (s[0]==0x90+TRIGGER_CHANNEL-1) && (s[2]>1)then #it's a note-on event on trigger channel
		if (!@rec_mode) then
			if @seq_length>1 then
				debug_log( "play note - seq ptr #{@seq_ptr}",1)
				@output.puts(0x8F+OUT_CHANNEL, @seq[@seq_ptr]+@transpose_amount,  64)
				@output.puts(0x8F+OUT_CHANNEL, @seq[@seq_ptr]+@transpose_amount,  0) #immediate note off 
				@seq_ptr+=1
				@seq_ptr=0 if @seq_ptr==@seq_length
			else
				debug_log("skipping - no sequence",1)
			end

		end

	end

end

status=[]

begin	

	loop do 	

				m = @input.gets_data
				m.each do |b|
					next if b==0xf8	#ignore clock beats
					if (b<0x80) then
						status<<b
					else
						status=[b]
					end					

					if status.length==3 then
						execute_status(status)
						cmd_byte=status[0]#keep original first byte for short form commands
						status=[cmd_byte]
					end
				end
	end
ensure
	@output.puts(0xAF+OUT_CHANNEL, 0x7B, 0) #all notes off
end
This entry was posted in Uncategorized. Bookmark the permalink.

Comments are closed.