ruck-midi 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/README +8 -0
- data/Rakefile +24 -0
- data/VERSION +1 -0
- data/bin/ruck_midi +43 -0
- data/examples/ex01.rb +21 -0
- data/examples/ex02.rb +76 -0
- data/lib/ruck/midi.rb +2 -0
- data/lib/ruck/midi/midi.rb +192 -0
- data/ruck-midi.gemspec +63 -0
- metadata +108 -0
data/.document
ADDED
data/.gitignore
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake"
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "jeweler"
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ruck-midi"
|
8
|
+
gem.email = "tom@alltom.com"
|
9
|
+
gem.homepage = "http://github.com/alltom/ruck-midi"
|
10
|
+
gem.authors = ["Tom Lieber"]
|
11
|
+
gem.summary = %Q{real-time and offline ruck shredulers for MIDI (thanks midiator and midilib)}
|
12
|
+
gem.description = <<-EOF
|
13
|
+
Real-time and offline ruck shredulers for MIDI using midiator and midilib.
|
14
|
+
EOF
|
15
|
+
gem.has_rdoc = false
|
16
|
+
gem.add_dependency "ruck", ">= 0"
|
17
|
+
gem.add_dependency "midiator", ">= 0"
|
18
|
+
gem.add_dependency "midilib", ">= 0"
|
19
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
24
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/bin/ruck_midi
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
|
5
|
+
# for testing inside gem dir
|
6
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
7
|
+
|
8
|
+
require "ruck"
|
9
|
+
require "ruck/midi"
|
10
|
+
|
11
|
+
if ARGV.length < 4
|
12
|
+
puts "ruby midilib_runner.rb MIDI_FILENAME NUM_TRACKS LIVE SCRIPT_FILENAME [...]"
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
MIDI_FILENAME = ARGV[0]
|
17
|
+
NUM_TRACKS = ARGV[1].to_i
|
18
|
+
ALSO_LIVE = ["yes", "true", "1", "yeah"].include? ARGV[2].downcase
|
19
|
+
FILENAMES = ARGV[3..-1]
|
20
|
+
|
21
|
+
# state
|
22
|
+
$midi = Ruck::MIDI::MIDIOutput.new(ALSO_LIVE, MIDI_FILENAME, NUM_TRACKS)
|
23
|
+
SHREDULER = Ruck::MIDI::MIDIShreduler.new($midi, ALSO_LIVE)
|
24
|
+
|
25
|
+
at_exit { $midi.save }
|
26
|
+
|
27
|
+
FILENAMES.each do |filename|
|
28
|
+
unless File.readable?(filename)
|
29
|
+
LOG.fatal "Cannot read file #{filename}"
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
FILENAMES.each do |filename|
|
35
|
+
SHREDULER.spork(filename) do
|
36
|
+
include Ruck::MIDI::ShredLocal
|
37
|
+
require filename
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
SHREDULER.run
|
42
|
+
|
43
|
+
$midi.save
|
data/examples/ex01.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
def maybe
|
3
|
+
rand >= 0.5
|
4
|
+
end
|
5
|
+
|
6
|
+
control_change(32, 1, 10)
|
7
|
+
program_change(26, 10)
|
8
|
+
|
9
|
+
def play(note, dur = 1.quarter_note)
|
10
|
+
return if maybe
|
11
|
+
note_on note, 100, 10
|
12
|
+
wait dur
|
13
|
+
note_off note, 10
|
14
|
+
end
|
15
|
+
|
16
|
+
30.times do
|
17
|
+
spork { play(rand(30) + 40, rand * 3.quarter_notes + 3.quarter_notes) }
|
18
|
+
wait 0.5.quarter_note
|
19
|
+
end
|
20
|
+
|
21
|
+
wait 2.quarter_notes; note_off 0 # end padding
|
data/examples/ex02.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
|
2
|
+
def maybe
|
3
|
+
rand >= 0.5
|
4
|
+
end
|
5
|
+
|
6
|
+
@base = 43
|
7
|
+
@scale = 0
|
8
|
+
@chord = 0
|
9
|
+
|
10
|
+
major_scale = [0, 2, 4, 5, 7, 9, 11]
|
11
|
+
minor_scale = [0, 2, 3, 5, 7, 8, 11]
|
12
|
+
@scales = [major_scale, minor_scale]
|
13
|
+
|
14
|
+
def scale(note, scale)
|
15
|
+
len = scale.length
|
16
|
+
oct = note / len
|
17
|
+
off = note % len
|
18
|
+
while off < 0
|
19
|
+
off += len
|
20
|
+
oct -= 1
|
21
|
+
end
|
22
|
+
oct * 12 + scale[off]
|
23
|
+
end
|
24
|
+
|
25
|
+
def play(note, dur = 1.beat)
|
26
|
+
midi_note = @base + scale(note, @scales[@scale])
|
27
|
+
note_on midi_note, 100
|
28
|
+
wait dur
|
29
|
+
note_off midi_note
|
30
|
+
end
|
31
|
+
|
32
|
+
def change_chords
|
33
|
+
loop do
|
34
|
+
wait 4.beats
|
35
|
+
@chord = rand(7)
|
36
|
+
if rand <= 0.1
|
37
|
+
@scale = (@scale + 1) % @scales.length
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def play_chord(dur)
|
43
|
+
spork { play(@chord, dur) }
|
44
|
+
spork { play(@chord + 2, dur) }
|
45
|
+
spork { play(@chord + 4, dur) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def play_chords
|
49
|
+
loop do
|
50
|
+
if maybe && maybe
|
51
|
+
play_chord 2.beats
|
52
|
+
wait 2.beats
|
53
|
+
play_chord 2.beats
|
54
|
+
wait 2.beats
|
55
|
+
else
|
56
|
+
play_chord 4.beats
|
57
|
+
wait 4.beats
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def play_melody
|
63
|
+
loop do
|
64
|
+
len = ((rand(2) + 1) / 2.0).beats
|
65
|
+
play(@chord + 7 + 2 * rand(4), len)
|
66
|
+
wait len
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
shreds = []
|
71
|
+
shreds << spork { change_chords }
|
72
|
+
shreds << spork { play_chords }
|
73
|
+
shreds << spork { play_melody }
|
74
|
+
|
75
|
+
wait 16.beats
|
76
|
+
shreds.each { |s| s.finish }
|
data/lib/ruck/midi.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
|
2
|
+
require "midilib"
|
3
|
+
require "midiator"
|
4
|
+
|
5
|
+
module Ruck
|
6
|
+
module MIDI
|
7
|
+
|
8
|
+
class MIDIShreduler < Ruck::Shreduler
|
9
|
+
attr_reader :midi
|
10
|
+
|
11
|
+
def initialize(midi, real_time)
|
12
|
+
super()
|
13
|
+
@midi = midi
|
14
|
+
@real_time = real_time
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
@start_time = Time.now
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def sim_to(new_now)
|
23
|
+
d = new_now - @now
|
24
|
+
|
25
|
+
@midi.tick(d)
|
26
|
+
|
27
|
+
# sync with wall clock
|
28
|
+
if @real_time
|
29
|
+
actual_now = Time.now
|
30
|
+
simulated_now = @start_time + (new_now.to_f / @midi.ppqn / @midi.bpm * 60.0)
|
31
|
+
if simulated_now > actual_now
|
32
|
+
sleep(simulated_now - actual_now)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@now = new_now
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class MIDIOutput
|
41
|
+
def initialize(real_time, save_filename = nil, num_tracks = 1)
|
42
|
+
if save_filename
|
43
|
+
@filename = save_filename
|
44
|
+
@sequence = ::MIDI::Sequence.new
|
45
|
+
@tracks = (1..num_tracks).map { ::MIDI::Track.new(@sequence) }
|
46
|
+
@track_deltas = @tracks.map { 0 }
|
47
|
+
|
48
|
+
@tracks.each do |track|
|
49
|
+
@sequence.tracks << track
|
50
|
+
#track.events << ::MIDI::Tempo.new(::MIDI::Tempo.bpm_to_mpq(120))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if real_time
|
55
|
+
@player = MIDIator::Interface.new
|
56
|
+
@player.use :dls_synth
|
57
|
+
@player.instruct_user!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def ppqn
|
62
|
+
@sequence.ppqn
|
63
|
+
end
|
64
|
+
|
65
|
+
def bpm
|
66
|
+
@sequence.bpm
|
67
|
+
end
|
68
|
+
|
69
|
+
def tick(delta)
|
70
|
+
if @sequence
|
71
|
+
@track_deltas.each_with_index do |track_delta, i|
|
72
|
+
@track_deltas[i] = track_delta + delta
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def note_on(note, velocity = 127, channel = 0, track = 0)
|
78
|
+
if @sequence
|
79
|
+
@tracks[track].events << ::MIDI::NoteOnEvent.new(channel, note, velocity, @track_deltas[track].to_i)
|
80
|
+
@track_deltas[track] = 0
|
81
|
+
end
|
82
|
+
|
83
|
+
if @player
|
84
|
+
@player.driver.note_on(note, channel, velocity)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def note_off(note, channel = 0, track = 0)
|
89
|
+
if @sequence
|
90
|
+
@tracks[track].events << ::MIDI::NoteOffEvent.new(channel, note, 0, @track_deltas[track].to_i)
|
91
|
+
@track_deltas[track] = 0
|
92
|
+
end
|
93
|
+
|
94
|
+
if @player
|
95
|
+
@player.driver.note_on(note, channel, 0)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def control_change(controller, value, channel = 0, track = 0)
|
100
|
+
if @sequence
|
101
|
+
@tracks[track].events << ::MIDI::Controller.new(channel, controller, value)
|
102
|
+
end
|
103
|
+
|
104
|
+
if @player
|
105
|
+
@player.driver.control_change(controller, channel, value)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def program_change(program, channel = 0, track = 0)
|
110
|
+
if @sequence
|
111
|
+
@tracks[track].events << ::MIDI::ProgramChange.new(channel, program)
|
112
|
+
end
|
113
|
+
|
114
|
+
if @player
|
115
|
+
@player.driver.program_change(channel, program)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def save
|
120
|
+
return if @saved
|
121
|
+
return unless @sequence
|
122
|
+
|
123
|
+
@saved = true
|
124
|
+
|
125
|
+
File.open(@filename, "wb") do |file|
|
126
|
+
@sequence.write(file)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# stuff accessible in a shred
|
132
|
+
module ShredLocal
|
133
|
+
def now
|
134
|
+
SHREDULER.now
|
135
|
+
end
|
136
|
+
|
137
|
+
def spork(name = "unnamed", &shred)
|
138
|
+
SHREDULER.spork(name, &shred)
|
139
|
+
end
|
140
|
+
|
141
|
+
def wait(pulses)
|
142
|
+
SHREDULER.current_shred.yield(pulses)
|
143
|
+
end
|
144
|
+
|
145
|
+
def finish
|
146
|
+
shred = SHREDULER.current_shred
|
147
|
+
SHREDULER.remove_shred shred
|
148
|
+
shred.finish
|
149
|
+
end
|
150
|
+
|
151
|
+
def note_on(note, velocity = 127, channel = 0, track = 0)
|
152
|
+
SHREDULER.midi.note_on(note, velocity, channel, track)
|
153
|
+
end
|
154
|
+
|
155
|
+
def note_off(note, channel = 0, track = 0)
|
156
|
+
SHREDULER.midi.note_off(note, channel, track)
|
157
|
+
end
|
158
|
+
|
159
|
+
def control_change(controller, value, channel = 0, track = 0)
|
160
|
+
SHREDULER.midi.control_change(controller, value, channel, track)
|
161
|
+
end
|
162
|
+
|
163
|
+
def program_change(program, channel = 0, track = 0)
|
164
|
+
SHREDULER.midi.program_change(program, channel, track)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# set up some useful time helpers
|
172
|
+
module MIDITime
|
173
|
+
def pulse
|
174
|
+
self
|
175
|
+
end
|
176
|
+
alias_method :pulses, :pulse
|
177
|
+
|
178
|
+
def quarter_note
|
179
|
+
self * SHREDULER.midi.ppqn
|
180
|
+
end
|
181
|
+
alias_method :quarter_notes, :quarter_note
|
182
|
+
alias_method :beat, :quarter_note
|
183
|
+
alias_method :beats, :quarter_note
|
184
|
+
end
|
185
|
+
|
186
|
+
class Fixnum
|
187
|
+
include MIDITime
|
188
|
+
end
|
189
|
+
|
190
|
+
class Float
|
191
|
+
include MIDITime
|
192
|
+
end
|
data/ruck-midi.gemspec
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{ruck-midi}
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Tom Lieber"]
|
12
|
+
s.date = %q{2010-07-10}
|
13
|
+
s.default_executable = %q{ruck_midi}
|
14
|
+
s.description = %q{ Real-time and offline ruck shredulers for MIDI using midiator and midilib.
|
15
|
+
}
|
16
|
+
s.email = %q{tom@alltom.com}
|
17
|
+
s.executables = ["ruck_midi"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"README"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".gitignore",
|
24
|
+
"README",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"bin/ruck_midi",
|
28
|
+
"examples/ex01.rb",
|
29
|
+
"examples/ex02.rb",
|
30
|
+
"lib/ruck/midi.rb",
|
31
|
+
"lib/ruck/midi/midi.rb",
|
32
|
+
"ruck-midi.gemspec"
|
33
|
+
]
|
34
|
+
s.homepage = %q{http://github.com/alltom/ruck-midi}
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubygems_version = %q{1.3.6}
|
38
|
+
s.summary = %q{real-time and offline ruck shredulers for MIDI (thanks midiator and midilib)}
|
39
|
+
s.test_files = [
|
40
|
+
"examples/ex01.rb",
|
41
|
+
"examples/ex02.rb"
|
42
|
+
]
|
43
|
+
|
44
|
+
if s.respond_to? :specification_version then
|
45
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
46
|
+
s.specification_version = 3
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
49
|
+
s.add_runtime_dependency(%q<ruck>, [">= 0"])
|
50
|
+
s.add_runtime_dependency(%q<midiator>, [">= 0"])
|
51
|
+
s.add_runtime_dependency(%q<midilib>, [">= 0"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<ruck>, [">= 0"])
|
54
|
+
s.add_dependency(%q<midiator>, [">= 0"])
|
55
|
+
s.add_dependency(%q<midilib>, [">= 0"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<ruck>, [">= 0"])
|
59
|
+
s.add_dependency(%q<midiator>, [">= 0"])
|
60
|
+
s.add_dependency(%q<midilib>, [">= 0"])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruck-midi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Tom Lieber
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-07-10 00:00:00 -07:00
|
18
|
+
default_executable: ruck_midi
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: ruck
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: midiator
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :runtime
|
43
|
+
version_requirements: *id002
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: midilib
|
46
|
+
prerelease: false
|
47
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
type: :runtime
|
55
|
+
version_requirements: *id003
|
56
|
+
description: " Real-time and offline ruck shredulers for MIDI using midiator and midilib.\n"
|
57
|
+
email: tom@alltom.com
|
58
|
+
executables:
|
59
|
+
- ruck_midi
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
extra_rdoc_files:
|
63
|
+
- README
|
64
|
+
files:
|
65
|
+
- .document
|
66
|
+
- .gitignore
|
67
|
+
- README
|
68
|
+
- Rakefile
|
69
|
+
- VERSION
|
70
|
+
- bin/ruck_midi
|
71
|
+
- examples/ex01.rb
|
72
|
+
- examples/ex02.rb
|
73
|
+
- lib/ruck/midi.rb
|
74
|
+
- lib/ruck/midi/midi.rb
|
75
|
+
- ruck-midi.gemspec
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://github.com/alltom/ruck-midi
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options:
|
82
|
+
- --charset=UTF-8
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.3.6
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: real-time and offline ruck shredulers for MIDI (thanks midiator and midilib)
|
106
|
+
test_files:
|
107
|
+
- examples/ex01.rb
|
108
|
+
- examples/ex02.rb
|