patchmaster 0.0.0 → 0.0.1
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/bin/patchmaster +18 -10
- data/lib/patchmaster.rb +2 -5
- data/lib/patchmaster/app/info_window.rb +3 -2
- data/lib/patchmaster/app/info_window_contents.txt +5 -4
- data/lib/patchmaster/app/list_window.rb +7 -4
- data/lib/patchmaster/app/main.rb +54 -39
- data/lib/patchmaster/app/trigger_window.rb +27 -0
- data/lib/patchmaster/connection.rb +15 -13
- data/lib/patchmaster/cursor.rb +174 -0
- data/lib/patchmaster/dsl.rb +65 -32
- data/lib/patchmaster/filter.rb +1 -1
- data/lib/patchmaster/{io.rb → instrument.rb} +24 -11
- data/lib/patchmaster/patch.rb +10 -17
- data/lib/patchmaster/patchmaster.rb +84 -100
- data/lib/patchmaster/song.rb +1 -6
- data/lib/patchmaster/song_list.rb +1 -11
- data/lib/patchmaster/sorted_song_list.rb +1 -1
- data/lib/patchmaster/trigger.rb +32 -0
- data/test/test_helper.rb +21 -13
- metadata +7 -6
- data/lib/patchmaster/list.rb +0 -121
- data/lib/patchmaster/list_container.rb +0 -36
data/lib/patchmaster/song.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'patchmaster/list'
|
2
|
-
require 'patchmaster/list_container'
|
3
|
-
|
4
1
|
module PM
|
5
2
|
|
6
3
|
# A Song is a named list of Patches with a cursor.
|
@@ -8,11 +5,9 @@ class Song
|
|
8
5
|
|
9
6
|
attr_accessor :name, :patches
|
10
7
|
|
11
|
-
include ListContainer
|
12
|
-
|
13
8
|
def initialize(name)
|
14
9
|
@name = name
|
15
|
-
@patches =
|
10
|
+
@patches = []
|
16
11
|
PatchMaster.instance.all_songs << self
|
17
12
|
end
|
18
13
|
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'patchmaster/list'
|
2
|
-
require 'patchmaster/list_container'
|
3
|
-
|
4
1
|
module PM
|
5
2
|
|
6
3
|
# A SongList is a list of Songs with a cursor.
|
@@ -8,11 +5,9 @@ class SongList
|
|
8
5
|
|
9
6
|
attr_accessor :name, :songs
|
10
7
|
|
11
|
-
include ListContainer
|
12
|
-
|
13
8
|
def initialize(name)
|
14
9
|
@name = name
|
15
|
-
@songs =
|
10
|
+
@songs = []
|
16
11
|
end
|
17
12
|
|
18
13
|
def <<(song)
|
@@ -30,10 +25,5 @@ class SongList
|
|
30
25
|
%w(first prev curr next last).each do |dir|
|
31
26
|
instance_eval("def #{dir}_patch; @songs.curr.#{dir}_patch; end")
|
32
27
|
end
|
33
|
-
|
34
|
-
# Called when moving out of this song list to another
|
35
|
-
def stop_current_song
|
36
|
-
@songs.stop_current
|
37
|
-
end
|
38
28
|
end
|
39
29
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PM
|
2
|
+
|
3
|
+
# A Trigger performs an action when it sees a particular array of bytes.
|
4
|
+
# Instruments have zero or more triggers. The action is a symbol that gets
|
5
|
+
# sent to PM::PatchMaster.
|
6
|
+
#
|
7
|
+
# Since we want to save them to files, we store the text representation as
|
8
|
+
# well.
|
9
|
+
class Trigger
|
10
|
+
|
11
|
+
attr_accessor :bytes, :block, :text
|
12
|
+
|
13
|
+
def initialize(bytes, block)
|
14
|
+
@bytes, @block = bytes, block
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(sym, *args)
|
18
|
+
PM::PatchMaster.instance.send(sym, *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
# If +bytes+ matches our +@bytes+ array then run +@block+.
|
22
|
+
def signal(bytes)
|
23
|
+
if bytes == @bytes
|
24
|
+
block.call
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"#{@bytes.inspect} => #{@text ? @text.gsub(/\n\s*/, '; ') : ''}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,23 +1,40 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'patchmaster'
|
3
|
+
require 'midi-eye'
|
3
4
|
|
4
5
|
# For all tests, make sure mock I/O MIDI ports are used.
|
5
6
|
PM::PatchMaster.instance.no_midi!
|
6
7
|
|
7
8
|
module PM
|
8
9
|
|
9
|
-
# To help with testing, we replace MockInputPort#
|
10
|
+
# To help with testing, we replace MockInputPort#gets and
|
10
11
|
# MockOutputPort#puts with versions that send what we want and save what is
|
11
12
|
# received.
|
12
13
|
class MockInputPort
|
13
14
|
|
14
15
|
attr_accessor :data_to_send
|
16
|
+
|
17
|
+
# For MIDIEye::Listener
|
18
|
+
def self.is_compatible?(input)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(_=nil)
|
23
|
+
@t0 = (Time.now.to_f * 1000).to_i
|
24
|
+
end
|
15
25
|
|
16
|
-
def
|
26
|
+
def gets
|
17
27
|
retval = @data_to_send || []
|
18
28
|
@data_to_send = []
|
19
|
-
retval
|
29
|
+
[{:data => retval, :timestamp => (Time.now.to_f * 1000).to_i - @t0}]
|
20
30
|
end
|
31
|
+
|
32
|
+
def poll
|
33
|
+
yield gets
|
34
|
+
end
|
35
|
+
|
36
|
+
# add this class to the Listener class' known input types
|
37
|
+
MIDIEye::Listener.input_types << self
|
21
38
|
end
|
22
39
|
|
23
40
|
class MockOutputPort
|
@@ -43,16 +60,7 @@ class TestConnection < PM::Connection
|
|
43
60
|
def midi_in(bytes)
|
44
61
|
@bytes_received ||= []
|
45
62
|
@bytes_received += bytes
|
46
|
-
midi_out(
|
63
|
+
midi_out(bytes)
|
47
64
|
end
|
48
65
|
|
49
66
|
end
|
50
|
-
|
51
|
-
class PMTest < Test::Unit::TestCase
|
52
|
-
|
53
|
-
# Data comes out of UniMIDI::Input#gets_ata as an array of arrays of MIDI
|
54
|
-
# bytes.
|
55
|
-
def midi_data(*bytes)
|
56
|
-
[bytes]
|
57
|
-
end
|
58
|
-
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: patchmaster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-04-09 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: unimidi
|
16
|
-
requirement: &
|
16
|
+
requirement: &2156042580 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2156042580
|
25
25
|
description: ! 'PatchMaster is realtime MIDI performance software that alloweds a
|
26
26
|
musician
|
27
27
|
|
@@ -44,19 +44,20 @@ files:
|
|
44
44
|
- lib/patchmaster/app/patch_window.rb
|
45
45
|
- lib/patchmaster/app/pm_window.rb
|
46
46
|
- lib/patchmaster/app/prompt_window.rb
|
47
|
+
- lib/patchmaster/app/trigger_window.rb
|
47
48
|
- lib/patchmaster/connection.rb
|
48
49
|
- lib/patchmaster/consts.rb
|
50
|
+
- lib/patchmaster/cursor.rb
|
49
51
|
- lib/patchmaster/dsl.rb
|
50
52
|
- lib/patchmaster/filter.rb
|
51
|
-
- lib/patchmaster/
|
52
|
-
- lib/patchmaster/list.rb
|
53
|
-
- lib/patchmaster/list_container.rb
|
53
|
+
- lib/patchmaster/instrument.rb
|
54
54
|
- lib/patchmaster/patch.rb
|
55
55
|
- lib/patchmaster/patchmaster.rb
|
56
56
|
- lib/patchmaster/predicates.rb
|
57
57
|
- lib/patchmaster/song.rb
|
58
58
|
- lib/patchmaster/song_list.rb
|
59
59
|
- lib/patchmaster/sorted_song_list.rb
|
60
|
+
- lib/patchmaster/trigger.rb
|
60
61
|
- lib/patchmaster.rb
|
61
62
|
- test/test_helper.rb
|
62
63
|
homepage: https://github.com/jimm/patchmaster
|
data/lib/patchmaster/list.rb
DELETED
@@ -1,121 +0,0 @@
|
|
1
|
-
module PM
|
2
|
-
|
3
|
-
# A list (Array) of things with a cursor. +@curr+ is an integer index into
|
4
|
-
# an array of data.
|
5
|
-
class List
|
6
|
-
|
7
|
-
include Enumerable
|
8
|
-
|
9
|
-
# If +enter_sym+ or +exit_sym+ are defined, they will be sent to a data
|
10
|
-
# element when it becomes the current element or stops being the current
|
11
|
-
# element.
|
12
|
-
def initialize
|
13
|
-
@data = []
|
14
|
-
@curr = nil
|
15
|
-
end
|
16
|
-
|
17
|
-
# Adding data does not modify the cursor.
|
18
|
-
def <<(data)
|
19
|
-
@data << data
|
20
|
-
end
|
21
|
-
|
22
|
-
# Inserts +data+ before +before_this+. If +before_this+ is not in our
|
23
|
-
# list, +data+ is inserted at the beginning of the list.
|
24
|
-
def insert_before(before_this, data)
|
25
|
-
idx = @data.index(before_this) || 0
|
26
|
-
@data[idx, 0] = data
|
27
|
-
end
|
28
|
-
|
29
|
-
# Inserts +data+ after +after_this+. If +after_this+ is not in our list,
|
30
|
-
# +data+ is inserted at the end of the list.
|
31
|
-
def insert_after(after_this, data)
|
32
|
-
idx = @data.index(after_this) || @data.length-1
|
33
|
-
@data[idx + 1, 0] = data
|
34
|
-
end
|
35
|
-
|
36
|
-
def size
|
37
|
-
@data.size
|
38
|
-
end
|
39
|
-
alias_method :length, :size
|
40
|
-
|
41
|
-
def first?
|
42
|
-
@curr == 0
|
43
|
-
end
|
44
|
-
|
45
|
-
def first
|
46
|
-
if !@data.empty? && @curr != 0
|
47
|
-
@curr = 0
|
48
|
-
end
|
49
|
-
curr
|
50
|
-
end
|
51
|
-
|
52
|
-
def next
|
53
|
-
if @curr == nil || @curr >= @data.length - 1
|
54
|
-
@curr = nil
|
55
|
-
else
|
56
|
-
@curr += 1
|
57
|
-
end
|
58
|
-
curr
|
59
|
-
end
|
60
|
-
|
61
|
-
def curr
|
62
|
-
@curr ? @data[@curr] : nil
|
63
|
-
end
|
64
|
-
|
65
|
-
# This does not change what is stored at the current location. Rather,
|
66
|
-
# it moves this list's cursor to point to +data+ and returns +data+.
|
67
|
-
def curr=(data)
|
68
|
-
if curr != data
|
69
|
-
@curr = @data.index(data)
|
70
|
-
end
|
71
|
-
data
|
72
|
-
end
|
73
|
-
|
74
|
-
# Returns the +n+th data element. This method is not normally used to
|
75
|
-
# access data because it does not change the cursor. It is used to peek
|
76
|
-
# into the list's data array, for example during testing.
|
77
|
-
def [](n)
|
78
|
-
@data[n]
|
79
|
-
end
|
80
|
-
|
81
|
-
def prev
|
82
|
-
if @curr == nil || @curr == 0
|
83
|
-
@curr = nil
|
84
|
-
else
|
85
|
-
@curr -= 1
|
86
|
-
end
|
87
|
-
curr
|
88
|
-
end
|
89
|
-
|
90
|
-
def last
|
91
|
-
if !@data.empty? && !last?
|
92
|
-
@curr = @data.length - 1
|
93
|
-
end
|
94
|
-
curr
|
95
|
-
end
|
96
|
-
|
97
|
-
def last?
|
98
|
-
@curr == nil || @curr == @data.length - 1
|
99
|
-
end
|
100
|
-
|
101
|
-
def remove(data)
|
102
|
-
return unless @data.include?(data)
|
103
|
-
@data[@data.index(data), 1] = []
|
104
|
-
if @data.empty?
|
105
|
-
@curr = nil
|
106
|
-
elsif @curr >= @data.length
|
107
|
-
@curr = @data.length - 1
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def each
|
112
|
-
@data.each { |data| yield data }
|
113
|
-
end
|
114
|
-
|
115
|
-
# For debugging
|
116
|
-
def to_s
|
117
|
-
"List(#{@data.empty? ? 'empty' : @data[0].class.name}), size #{size}, curr index #{@curr}"
|
118
|
-
end
|
119
|
-
|
120
|
-
end
|
121
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module PM
|
2
|
-
|
3
|
-
# A ListContainer responds to messages that manipulate the cursors for one
|
4
|
-
# or more List objects.
|
5
|
-
module ListContainer
|
6
|
-
|
7
|
-
def method_missing(sym, *args)
|
8
|
-
case sym.to_s
|
9
|
-
when /^curr_(\w+)=$/
|
10
|
-
name = $1
|
11
|
-
ivar_sym = "@#{pluralize(name)}".to_sym
|
12
|
-
raise "no such ivar #{ivar_sym} in #{self.class}" unless instance_variable_defined?(ivar_sym)
|
13
|
-
instance_variable_get(ivar_sym).send(:curr=, args[0])
|
14
|
-
when /^(first|next|prev|curr|last)_(\w+)(\?)?$/
|
15
|
-
method, ivar, qmark = $1, $2, $3
|
16
|
-
ivar_sym = "@#{pluralize(ivar)}".to_sym
|
17
|
-
raise "no such ivar #{ivar_sym} in #{self.class}" unless instance_variable_defined?(ivar_sym)
|
18
|
-
instance_variable_get(ivar_sym).send("#{method}#{qmark}".to_sym)
|
19
|
-
else
|
20
|
-
super
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def pluralize(str)
|
25
|
-
case str
|
26
|
-
when /s$/, /ch$/
|
27
|
-
"#{str}es"
|
28
|
-
when /y$/
|
29
|
-
"#{str[0..-2]}ies}"
|
30
|
-
else
|
31
|
-
"#{str}s"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|