├── .gitignore
├── CHANGELOG.txt
├── INSTALL.txt
├── NOTES.txt
├── README.md
├── Rakefile
├── ajm.ruby.properties.example
├── ajm
├── ajm.overview.maxpat
├── help
│ ├── ajm.about.maxpat
│ ├── ajm.busymap.maxhelp
│ ├── ajm.cosy.maxhelp
│ ├── ajm.env~.maxhelp
│ ├── ajm.error.maxhelp
│ ├── ajm.eval.maxhelp
│ ├── ajm.help.template.maxpat
│ ├── ajm.helplinks.maxpat
│ ├── ajm.lfo~.maxhelp
│ ├── ajm.makenote.maxhelp
│ ├── ajm.metro.maxhelp
│ ├── ajm.midi2coll.maxhelp
│ ├── ajm.polyroute.maxhelp
│ ├── ajm.psui.maxhelp
│ ├── ajm.rseq.maxhelp
│ ├── ajm.rseq2seq.maxhelp
│ ├── ajm.ruby.advanced.maxhelp
│ ├── ajm.ruby.maxhelp
│ ├── ajm.seealso.maxpat
│ ├── ajm.seq.maxhelp
│ ├── ajm_cc_pan.mid
│ ├── ajm_env_presets.xml
│ ├── ajm_midi2coll.mid
│ ├── ajm_multitrack.mid
│ ├── ajm_shifted_scale.mid
│ ├── ajm_timesig.mid
│ └── compusition.gif
├── js
│ ├── ajm.error.js
│ ├── ajm.polyroute-helper.js
│ └── ajm.psui.js
├── misc
│ ├── ajm.bring-help-to-front-button.maxpat
│ ├── ajm.bring-to-front-listener.maxpat
│ ├── ajm.bring-to-front.maxpat
│ ├── ajm.env-polysaw.maxpat
│ ├── ajm.envthispoly.maxpat
│ ├── ajm.envui-helper.maxpat
│ ├── ajm.help-transport.maxpat
│ ├── ajm.next-quantized-timepoint.maxpat
│ ├── ajm.polymakenote.maxpat
│ ├── ajm.polyprint.maxpat
│ ├── ajm.translate-duration.maxpat
│ └── ajm.translate-line.maxpat
├── objects
│ ├── ajm.busymap.maxpat
│ ├── ajm.cosy.maxpat
│ ├── ajm.envui.maxpat
│ ├── ajm.env~.maxpat
│ ├── ajm.lfo~.maxpat
│ ├── ajm.makenote.maxpat
│ ├── ajm.metro.maxpat
│ ├── ajm.midi2coll.maxpat
│ ├── ajm.polyroute.maxpat
│ └── ajm.rseq2seq.maxpat
└── ruby
│ ├── ajm_cosy.rb
│ ├── ajm_cosy2coll.rb
│ ├── ajm_midi2coll.rb
│ ├── ajm_ruby_initialize.rb
│ ├── ajmfl_cosy2clip.rb
│ ├── help
│ ├── ajm_argv.rb
│ ├── ajm_autowatch.rb
│ ├── ajm_call_send.rb
│ ├── ajm_file_example.rb
│ ├── ajm_inlet_symbols_to.rb
│ ├── ajm_inlets.rb
│ ├── ajm_load_require.rb
│ ├── ajm_scriptfile.rb
│ ├── ajm_variable_scope.rb
│ └── ajm_version.rb
│ ├── midilib
│ ├── consts.rb
│ ├── event.rb
│ ├── info.rb
│ ├── io
│ │ ├── midifile.rb
│ │ ├── seqreader.rb
│ │ └── seqwriter.rb
│ ├── measure.rb
│ ├── sequence.rb
│ ├── track.rb
│ └── utils.rb
│ └── webserver
│ ├── ajm_servlets.rb
│ ├── ajm_webserver.rb
│ ├── ajm_webserver_example.rb
│ ├── favicon.ico
│ ├── filebrowse
│ ├── file.html
│ └── file.txt
│ ├── index.html
│ └── web
│ ├── erb.rhtml
│ └── to_max.html
├── build.xml
├── lib
├── ant-junit.jar
├── bsf.jar
├── jruby.jar
├── junit-4.5.jar
└── max.jar
├── license
├── LICENSE-ajm-objects.txt
├── LICENSE-bsf.txt
├── LICENSE-midilib.txt
└── jruby
│ ├── COPYING
│ ├── COPYING.CPL
│ ├── COPYING.GPL
│ └── COPYING.LGPL
└── src
└── ajm
├── code.java
├── eval.java
├── maxsupport
├── AbstractMaxObject.java
├── AbstractMaxRubyObject.java
└── Atomizer.java
├── preemptrseq.java
├── print.java
├── rseq.java
├── rseqTest.java
├── ruby.java
├── rubysupport
├── AbstractScriptEvaluator.java
├── BSFRubyEvaluator.java
├── IdInUseException.java
├── JRubyEmbedEvaluator.java
├── MaxRubyAdapter.java
├── RubyException.java
├── RubyProperties.java
├── ScriptEvaluator.java
├── ScriptEvaluatorManager.java
└── SymbolConversionOption.java
├── seq.java
├── seqTest.java
├── seqsupport
├── Item.java
├── Parser.java
├── ParserTest.java
└── Token.java
└── util
├── DummyMaxObject.java
├── FileWatcher.java
├── GlobalVariableStore.java
├── LineBuilder.java
├── Logger.java
├── MappedSet.java
├── TextBlock.java
├── TextViewer.java
└── Utils.java
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | download
3 | dist
4 | ajm.jar
5 |
--------------------------------------------------------------------------------
/INSTALL.txt:
--------------------------------------------------------------------------------
1 |
2 | ajm objects for Max/MSP
3 |
4 | Version @@VERSION
5 |
6 | Built @@BUILD_DATE
7 |
8 | http://compusition.com/web/software/maxmsp/ajm-objects
9 |
10 | Adam Murray (adam@compusition.com)
11 |
12 | ------------------------------------------------------------------------------
13 |
14 | REQUIREMENTS
15 |
16 | * Max/MSP 5
17 |
18 | * Java 5 or higher (most computers today have this)
19 |
20 | -----------------------------------------------------------------------------
21 |
22 | FIRST TIME INSTALLATION
23 |
24 | (1) Put lib/ajm.jar and lib/jruby.jar in:
25 | Max5/Cycling '74/java/lib
26 |
27 | (2) Put the ajm folder somewhere on your Max file search path.
28 | See "Options -> File Preferences" under Max's Options menu.
29 |
30 | (3) Restart Max/MSP and open ajm/ajm.overview.maxpat
31 |
32 | -----------------------------------------------------------------------------
33 |
34 | UPGRADING FROM VERSION 0.9 (or newer)
35 |
36 | (1) Delete the old ajm folder (the old patches & help files)
37 |
38 | (2) Proceed with the first time installation instructions.
39 | The following files will be replaced: ajm.jar, jruby.jar
40 |
41 | -----------------------------------------------------------------------------
42 |
43 | UPGRADING FROM VERSION 0.8.8 (or older)
44 |
45 | (1) Delete the entire ruby directory structure:
46 | Max5/Cycling '74/java/lib/ruby
47 |
48 | (2) Delete the old ajm folder (the old patches & help files)
49 |
50 | (3) Delete the file:
51 | Max5/Cycling '74/java/lib/ajm.ruby.properties
52 |
53 | (4) Delete the file:
54 | Max5/Cycling '74/java/lib/bsf.jar
55 |
56 | (5) Proceed with the first time installation instructions.
57 | The following files will be replaced: ajm.jar, jruby.jar
58 |
--------------------------------------------------------------------------------
/NOTES.txt:
--------------------------------------------------------------------------------
1 |
2 | ajm objects for Max/MSP
3 |
4 | Version @@VERSION
5 |
6 | http://compusition.com/web/software/maxmsp/ajm-objects
7 |
8 | Adam Murray (adam@compusition.com)
9 |
10 |
11 | See INSTALL.txt for installation instructions
12 | ------------------------------------------------------------------------------
13 |
14 | NOTES
15 |
16 | Sequencing with good timing
17 | When using a [metro] or [ajm.metro] to sequence music, you should enable
18 | Overdrive from Max's Options menu. If Overdrive is not enabled, interacting
19 | with your Max patch (clicking a GUI object) can cause timing delays and ruin
20 | the sense of rhythm. This is normal Max scheduler behavior, so this advice
21 | applies whenever you need good timing from [metro].
22 |
23 | ------------------------------------------------------------------------------
24 |
25 | SOURCE CODE
26 |
27 | ajm objects is an open source project. The source code is located at:
28 | http://github.com/adamjmurray/ajm_objects
29 |
30 | Feel free to hack away at it, contribute features, or whatever you want.
31 | Just mind my copyright and give credit where credit is due.
32 |
33 | ------------------------------------------------------------------------------
34 |
35 | DEPENDENCIES
36 |
37 | JRuby (http://jruby.org/)
38 | The 100% Java implementation of Ruby.
39 | JRuby in turn depends on other libraries. I included the license info
40 | for bsf.jar under the license folder. You can find license info for
41 | the Ruby language at:
42 | http://www.ruby-lang.org/en/LICENSE.txt
43 | http://www.ruby-lang.org/en/COPYING.txt
44 |
45 | midilib (http://midilib.rubyforge.org/)
46 | A Ruby MIDI library for reading and writing MIDI files.
47 | Used by ajm.midi2coll
48 |
49 | ------------------------------------------------------------------------------
50 |
51 | Copyright (c) 2008-2011, Adam Murray (adam@compusition.com).
52 |
53 | All rights reserved.
54 |
55 | Redistribution and use of ajm objects in source and binary forms, with or
56 | without modification, are permitted provided that the following conditions
57 | are met:
58 |
59 | 1. Redistributions of source code must retain the above copyright
60 | notice, this list of conditions and the following disclaimer.
61 |
62 | 2. Redistributions in binary form must reproduce the above copyright
63 | notice, this list of conditions and the following disclaimer in
64 | the documentation and/or other materials provided with the
65 | distribution.
66 |
67 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
68 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
69 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
70 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
71 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
72 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
73 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
74 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
75 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
76 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
77 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ajm objects #
2 |
3 | A library of objects for Max 5 ()
4 |
5 |
6 |
7 |
8 | ## Author ##
9 |
10 | Adam Murray (adam@compusition.com)
11 |
12 |
13 | ## Status ##
14 |
15 | Version 0.9.2 is the latest stable version, released on November 26, 2009.
16 |
17 |
18 |
19 | Tested on OS X Leopard & Snow Leopard, Windows XP, and with Max for Live.
20 |
21 |
22 | ## Future ##
23 |
24 | * ajm.ruby is being split off into a separate project at so I can focus on development of that object, and not require installation of all these objects for people who are only interested in scripting Max with Ruby.
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake/clean'
2 | require 'tempfile'
3 | include FileUtils
4 |
5 | VERSION = '0.9.2'
6 | BUILD_DATE = Time.now.utc.strftime '%B %d, %Y (%H:%M GMT)'
7 | MANIFEST =
8 | "Library: ajm objects (MXJ) for MaxMSP
9 | Version: #{VERSION}
10 | Built-Date: #{BUILD_DATE}
11 | Author: Adam Murray
12 | URL: http://compusition.com
13 | "
14 |
15 | SRC = 'src'
16 | LIB = 'lib'
17 | PATCHES = 'ajm'
18 | LICENSE = 'license'
19 | BUILD = 'build'
20 | DIST = 'dist'
21 | PACKAGE = "#{DIST}/ajm-objects-#{VERSION}"
22 |
23 | SOURCES = FileList["#{SRC}/**/*.java"].exclude(/Test\.java$/)
24 | CLASSPATH = FileList["#{LIB}/**/*.jar"].exclude(/^ajm.jar$/)
25 | JAR = "#{LIB}/ajm.jar"
26 |
27 |
28 | ##############################################################################
29 | # TASK DEFINITIONS
30 |
31 | CLEAN.include BUILD, JAR
32 | CLOBBER.include DIST
33 |
34 |
35 | desc 'compile the java source files'
36 | task :compile do
37 | mkdir BUILD
38 | puts "Compiling java sources to #{BUILD}/"
39 | `javac -classpath #{CLASSPATH.join ':'} -d #{BUILD} -g -source 1.5 -target 1.5 #{SOURCES}`
40 | end
41 |
42 |
43 | desc 'construct the jar archive of the compiled java sources'
44 | task :jar => [:clean, :compile] do
45 | manifest = Tempfile.new('manifest')
46 | File.open(manifest, 'w') {|io| io.write MANIFEST }
47 | puts "Constructing #{JAR}"
48 | `jar cvfm #{JAR} #{manifest.path} -C #{BUILD} .`
49 | end
50 |
51 |
52 | desc 'prepare the files for distribution'
53 | task :package => [:jar] do
54 | puts "Preparing distribution package"
55 | package_lib = "#{PACKAGE}/#{LIB}"
56 | mkdir DIST
57 | mkdir PACKAGE
58 | mkdir package_lib
59 |
60 | # Collect the files
61 | FileList['*.txt', '*.example'].each do |filename|
62 | cp filename, PACKAGE
63 | end
64 | FileList["#{LIB}/*.jar"].exclude('max.jar', /^.*junit.*jar$/, 'bsf.jar').each do |filename|
65 | cp filename, package_lib
66 | end
67 | cp_r PATCHES, PACKAGE
68 | cp_r LICENSE, PACKAGE
69 | end
70 |
71 |
72 | desc 'search and replace variable values in text files'
73 | task :replace_vars => [:package] do
74 | puts "Performing search and replace for the VERSION and BUILD_DATE variables"
75 | plaintext_filetypes = ['txt', 'maxpat', 'maxhelp']
76 | files = FileList[ plaintext_filetypes.map{|type| "#{PACKAGE}/**/*.#{type}" } ]
77 | files.replace_all '@@VERSION', VERSION
78 | files.replace_all '@@BUILD_DATE', BUILD_DATE
79 | end
80 |
81 |
82 | desc 'construct the distribution archive'
83 | task :dist => [:replace_vars] do
84 | mkdir DIST
85 | archive = "#{DIST}/#{PACKAGE}.zip"
86 | puts "Constructing distribution archive #{archive}"
87 | `zip -l -r #{archive} #{PACKAGE}`
88 | # The -l option converts newlines to crlf, which should display correctly on both OS X and Windows.
89 | # Otherwise, since I write these txt files on OS X, newlines would disappear when viewed in Notepad on Windows.
90 | end
91 |
92 |
93 | ##############################################################################
94 | # SUPPORT CODE:
95 |
96 | # I find it annoying that I always have to check if a directory exists
97 | # before creating it, so I monkey patch mkdir() to handle it automatically:
98 | alias original_mkdir mkdir
99 | def mkdir(dir)
100 | original_mkdir dir if not File.exist? dir
101 | end
102 |
103 |
104 | class FileList
105 | # Replaces all occurrences of token with value
106 | def replace_all(token, value)
107 | self.each do |filename|
108 | next if File.directory? filename
109 | contents = IO.read filename
110 | if contents.include? token
111 | contents.gsub! token, value
112 | File.open(filename, 'w') do |io|
113 | io.write contents
114 | end
115 | end
116 | end
117 | end
118 | end
119 |
--------------------------------------------------------------------------------
/ajm.ruby.properties.example:
--------------------------------------------------------------------------------
1 | ########################
2 | # Options for ajm.ruby #
3 | ########################
4 |
5 | # To use:
6 | # - Rename this file to ajm.ruby.properties
7 | # - Put it on the Max file search path
8 | # - Restart Max (and restart again each time you change this file)
9 |
10 |
11 | ruby.loadpaths =
12 | # A semicolon-separated list of absolute paths to append to the default $LOAD_PATH.
13 | # You can use this to add gems or other projects installed elsewhere.
14 | # Default value is nothing.
15 |
16 |
17 | # jruby.home = /Users/adam/bin/jruby-1.5.2
18 | # If you want to install a lot of gems: instead of adding a loadpath for each one,
19 | # you can install them into a separate jruby installation and set jruby.home to
20 | # run ajm.ruby against that jruby instead of the built-in one.
21 | # This variable is not set by default.
22 |
23 |
24 | ruby.initializers = ajm_ruby_initialize.rb
25 | # A semicolon-separated list of files.
26 | # These files will be run in the order listed whenever a new ajm.ruby context is initialized.
27 | # Default value is ajm_ruby_initialize.rb
28 |
--------------------------------------------------------------------------------
/ajm/help/ajm.help.template.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 591.0, 187.0, 795.0, 454.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 591.0, 187.0, 795.0, 454.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 0,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "newobj",
22 | "text" : "thispatcher",
23 | "fontname" : "Verdana",
24 | "hidden" : 1,
25 | "numinlets" : 1,
26 | "patching_rect" : [ 548.0, 266.0, 73.0, 20.0 ],
27 | "fontsize" : 11.1,
28 | "numoutlets" : 2,
29 | "id" : "obj-31",
30 | "outlettype" : [ "", "" ],
31 | "save" : [ "#N", "thispatcher", ";", "#Q", "end", ";" ]
32 | }
33 |
34 | }
35 | , {
36 | "box" : {
37 | "maxclass" : "newobj",
38 | "text" : "ajm.bring-to-front-listener ajm.____",
39 | "fontname" : "Verdana",
40 | "hidden" : 1,
41 | "numinlets" : 0,
42 | "patching_rect" : [ 548.0, 240.0, 216.0, 20.0 ],
43 | "fontsize" : 11.1,
44 | "numoutlets" : 1,
45 | "id" : "obj-17",
46 | "outlettype" : [ "front" ]
47 | }
48 |
49 | }
50 | , {
51 | "box" : {
52 | "maxclass" : "newobj",
53 | "text" : "p subpatch",
54 | "fontname" : "Verdana",
55 | "numinlets" : 0,
56 | "patching_rect" : [ 499.0, 113.0, 71.0, 20.0 ],
57 | "fontsize" : 11.0,
58 | "numoutlets" : 0,
59 | "id" : "obj-10",
60 | "color" : [ 0.156863, 0.8, 0.54902, 1.0 ],
61 | "patcher" : {
62 | "fileversion" : 1,
63 | "rect" : [ 289.0, 47.0, 724.0, 428.0 ],
64 | "bglocked" : 0,
65 | "defrect" : [ 289.0, 47.0, 724.0, 428.0 ],
66 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
67 | "openinpresentation" : 0,
68 | "default_fontsize" : 11.1,
69 | "default_fontface" : 0,
70 | "default_fontname" : "Verdana",
71 | "gridonopen" : 0,
72 | "gridsize" : [ 15.0, 15.0 ],
73 | "gridsnaponopen" : 0,
74 | "toolbarvisible" : 1,
75 | "boxanimatetime" : 200,
76 | "imprint" : 0,
77 | "metadata" : [ ],
78 | "boxes" : [ ],
79 | "lines" : [ ]
80 | }
81 | ,
82 | "saved_object_attributes" : {
83 | "default_fontsize" : 11.1,
84 | "fontname" : "Verdana",
85 | "fontface" : 0,
86 | "fontsize" : 11.1,
87 | "default_fontface" : 0,
88 | "default_fontname" : "Verdana",
89 | "globalpatchername" : ""
90 | }
91 |
92 | }
93 |
94 | }
95 | , {
96 | "box" : {
97 | "maxclass" : "bpatcher",
98 | "lockeddragscroll" : 1,
99 | "numinlets" : 1,
100 | "args" : [ ],
101 | "patching_rect" : [ 628.0, 386.0, 145.0, 55.0 ],
102 | "numoutlets" : 0,
103 | "id" : "obj-60",
104 | "name" : "ajm.seealso.maxpat"
105 | }
106 |
107 | }
108 | , {
109 | "box" : {
110 | "maxclass" : "bpatcher",
111 | "lockeddragscroll" : 1,
112 | "numinlets" : 0,
113 | "args" : [ ],
114 | "patching_rect" : [ 639.0, 6.0, 138.0, 55.0 ],
115 | "numoutlets" : 0,
116 | "id" : "obj-48",
117 | "name" : "ajm.helplinks.maxpat"
118 | }
119 |
120 | }
121 | , {
122 | "box" : {
123 | "maxclass" : "comment",
124 | "text" : "description",
125 | "fontname" : "Verdana",
126 | "numinlets" : 1,
127 | "patching_rect" : [ 141.0, 16.0, 336.0, 21.0 ],
128 | "fontsize" : 12.0,
129 | "numoutlets" : 0,
130 | "id" : "obj-56"
131 | }
132 |
133 | }
134 | , {
135 | "box" : {
136 | "maxclass" : "comment",
137 | "text" : "ajm.___",
138 | "fontname" : "Arial",
139 | "numinlets" : 1,
140 | "patching_rect" : [ 29.0, 8.0, 77.0, 27.0 ],
141 | "fontsize" : 18.0,
142 | "numoutlets" : 0,
143 | "id" : "obj-57"
144 | }
145 |
146 | }
147 | , {
148 | "box" : {
149 | "maxclass" : "comment",
150 | "text" : "Type",
151 | "fontname" : "Verdana",
152 | "numinlets" : 1,
153 | "patching_rect" : [ 30.0, 33.0, 100.0, 17.0 ],
154 | "fontsize" : 9.0,
155 | "numoutlets" : 0,
156 | "id" : "obj-58"
157 | }
158 |
159 | }
160 | , {
161 | "box" : {
162 | "maxclass" : "panel",
163 | "grad1" : [ 0.823529, 1.0, 0.956863, 1.0 ],
164 | "grad2" : [ 0.219608, 0.564706, 0.462745, 1.0 ],
165 | "angle" : 270.0,
166 | "numinlets" : 1,
167 | "patching_rect" : [ 27.0, 4.0, 464.0, 58.0 ],
168 | "mode" : 1,
169 | "rounded" : 12,
170 | "numoutlets" : 0,
171 | "id" : "obj-50"
172 | }
173 |
174 | }
175 | , {
176 | "box" : {
177 | "maxclass" : "panel",
178 | "grad1" : [ 0.866667, 0.866667, 0.866667, 1.0 ],
179 | "grad2" : [ 0.0, 0.0, 0.0, 1.0 ],
180 | "angle" : 270.0,
181 | "numinlets" : 1,
182 | "patching_rect" : [ 26.0, 3.0, 467.0, 62.0 ],
183 | "mode" : 1,
184 | "rounded" : 16,
185 | "numoutlets" : 0,
186 | "id" : "obj-54"
187 | }
188 |
189 | }
190 | ],
191 | "lines" : [ {
192 | "patchline" : {
193 | "source" : [ "obj-17", 0 ],
194 | "destination" : [ "obj-31", 0 ],
195 | "hidden" : 1,
196 | "midpoints" : [ ]
197 | }
198 |
199 | }
200 | ]
201 | }
202 |
203 | }
204 |
--------------------------------------------------------------------------------
/ajm/help/ajm.helplinks.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 271.0, 323.0, 466.0, 308.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 271.0, 323.0, 466.0, 308.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 1,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "enablehscroll" : 1,
19 | "enablevscroll" : 1,
20 | "devicewidth" : 0.0,
21 | "boxes" : [ {
22 | "box" : {
23 | "maxclass" : "newobj",
24 | "text" : "ajm.bring-to-front ajm.about",
25 | "numinlets" : 1,
26 | "fontsize" : 11.1,
27 | "numoutlets" : 0,
28 | "patching_rect" : [ 143.0, 83.0, 173.0, 20.0 ],
29 | "id" : "obj-3",
30 | "fontname" : "Verdana"
31 | }
32 |
33 | }
34 | , {
35 | "box" : {
36 | "maxclass" : "newobj",
37 | "text" : "ajm.bring-to-front ajm.overview",
38 | "numinlets" : 1,
39 | "fontsize" : 11.1,
40 | "numoutlets" : 0,
41 | "patching_rect" : [ 13.0, 115.0, 192.0, 20.0 ],
42 | "id" : "obj-2",
43 | "fontname" : "Verdana"
44 | }
45 |
46 | }
47 | , {
48 | "box" : {
49 | "maxclass" : "textbutton",
50 | "bordercolor" : [ 0.0, 0.0, 0.0, 1.0 ],
51 | "border" : 1,
52 | "texton" : "Stop",
53 | "text" : "© 2010 Adam Murray",
54 | "numinlets" : 1,
55 | "fontsize" : 12.0,
56 | "bgovercolor" : [ 0.784314, 0.909804, 0.917647, 1.0 ],
57 | "presentation_rect" : [ 4.0, 26.0, 123.0, 17.0 ],
58 | "bgcolor" : [ 0.827451, 0.827451, 0.827451, 1.0 ],
59 | "numoutlets" : 3,
60 | "outlettype" : [ "", "", "int" ],
61 | "patching_rect" : [ 143.0, 57.0, 124.0, 18.0 ],
62 | "presentation" : 1,
63 | "id" : "obj-12",
64 | "fontname" : "Arial",
65 | "rounded" : 20.0
66 | }
67 |
68 | }
69 | , {
70 | "box" : {
71 | "maxclass" : "textbutton",
72 | "bordercolor" : [ 0.0, 0.0, 0.0, 1.0 ],
73 | "border" : 1,
74 | "texton" : "Stop",
75 | "text" : "ajm overview",
76 | "numinlets" : 1,
77 | "fontsize" : 12.0,
78 | "bgovercolor" : [ 0.784314, 0.909804, 0.917647, 1.0 ],
79 | "presentation_rect" : [ 27.0, 5.0, 79.0, 17.0 ],
80 | "bgcolor" : [ 0.827451, 0.827451, 0.827451, 1.0 ],
81 | "numoutlets" : 3,
82 | "outlettype" : [ "", "", "int" ],
83 | "patching_rect" : [ 13.0, 56.0, 80.0, 18.0 ],
84 | "presentation" : 1,
85 | "id" : "obj-10",
86 | "fontname" : "Arial",
87 | "rounded" : 20.0
88 | }
89 |
90 | }
91 | , {
92 | "box" : {
93 | "maxclass" : "fpic",
94 | "numinlets" : 1,
95 | "presentation_rect" : [ 2.0, 4.0, 19.0, 20.0 ],
96 | "numoutlets" : 0,
97 | "patching_rect" : [ 44.0, 18.0, 19.0, 20.0 ],
98 | "pic" : "compusition.gif",
99 | "presentation" : 1,
100 | "id" : "obj-1"
101 | }
102 |
103 | }
104 | , {
105 | "box" : {
106 | "maxclass" : "panel",
107 | "border" : 1,
108 | "numinlets" : 1,
109 | "shadow" : 3,
110 | "presentation_rect" : [ 0.0, 0.0, 133.0, 49.0 ],
111 | "bgcolor" : [ 1.0, 1.0, 1.0, 1.0 ],
112 | "numoutlets" : 0,
113 | "patching_rect" : [ 91.0, 9.0, 127.0, 16.0 ],
114 | "presentation" : 1,
115 | "id" : "obj-9",
116 | "rounded" : 12
117 | }
118 |
119 | }
120 | , {
121 | "box" : {
122 | "maxclass" : "panel",
123 | "mode" : 1,
124 | "numinlets" : 1,
125 | "grad1" : [ 0.482353, 0.482353, 0.482353, 1.0 ],
126 | "presentation_rect" : [ 0.0, 0.0, 135.0, 51.0 ],
127 | "numoutlets" : 0,
128 | "grad2" : [ 0.0, 0.0, 0.0, 1.0 ],
129 | "angle" : 270.0,
130 | "patching_rect" : [ 90.0, 30.0, 131.0, 15.0 ],
131 | "presentation" : 1,
132 | "id" : "obj-70",
133 | "rounded" : 16
134 | }
135 |
136 | }
137 | ],
138 | "lines" : [ {
139 | "patchline" : {
140 | "source" : [ "obj-10", 0 ],
141 | "destination" : [ "obj-2", 0 ],
142 | "hidden" : 0,
143 | "midpoints" : [ ]
144 | }
145 |
146 | }
147 | , {
148 | "patchline" : {
149 | "source" : [ "obj-12", 0 ],
150 | "destination" : [ "obj-3", 0 ],
151 | "hidden" : 0,
152 | "midpoints" : [ ]
153 | }
154 |
155 | }
156 | ]
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/ajm/help/ajm_cc_pan.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/ajm/help/ajm_cc_pan.mid
--------------------------------------------------------------------------------
/ajm/help/ajm_midi2coll.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/ajm/help/ajm_midi2coll.mid
--------------------------------------------------------------------------------
/ajm/help/ajm_multitrack.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/ajm/help/ajm_multitrack.mid
--------------------------------------------------------------------------------
/ajm/help/ajm_shifted_scale.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/ajm/help/ajm_shifted_scale.mid
--------------------------------------------------------------------------------
/ajm/help/ajm_timesig.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/ajm/help/ajm_timesig.mid
--------------------------------------------------------------------------------
/ajm/help/compusition.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/ajm/help/compusition.gif
--------------------------------------------------------------------------------
/ajm/js/ajm.error.js:
--------------------------------------------------------------------------------
1 | // I'm using this simple object as a workaround for the lack of a feature in Max:
2 | // There is currently no way to print an arbitrary error message to the Max window
3 | // using something like [print] for error messages.
4 |
5 | var prefix = null
6 | if(jsarguments.length > 1) prefix = jsarguments[1] + ": "
7 |
8 | function anything() {
9 | var msg = ""
10 | if(prefix) msg = prefix
11 |
12 | // anything receives a messagename (first atom) and arguments array (remaining atoms):
13 | msg += messagename + " "
14 | for(var i=0; i MAX_VOICES) {
55 | voices = MAX_VOICES;
56 | }
57 |
58 | var args = new Array();
59 | for(var i=1; i<=voices; i++) {
60 | args.push("/"+i);
61 | }
62 | var voiceRoute = patcher.newobject("newex", 71, 170, 617, 196617, "OSC-route", args);
63 |
64 | patcher.connect(zlReg, 0, voiceRoute, 0);
65 | for(var i=1; i<=voices; i++) {
66 | var prepend = patcher.newobject("newex", i*66, 220, 61, 196617, "prepend", i);
67 | patcher.connect(voiceRoute, i-1, prepend, 0);
68 | patcher.connect(prepend, 0, zlSlice, 0);
69 | }
70 | // and pass anything that doesn't match to the outlet:
71 | patcher.connect(voiceRoute, voices, out, 0);
72 | }
73 |
--------------------------------------------------------------------------------
/ajm/js/ajm.psui.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice,
8 | this list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
26 | */
27 |
28 | // based on mymatrix.js from the tutorials
29 |
30 | //autowatch = 1
31 | //post("reloaded " + jsarguments[0] + " at " + new Date())
32 |
33 | inlets = 0
34 | outlets = 4
35 | setoutletassist(0, "recall number")
36 | setoutletassist(1, "store number")
37 | setoutletassist(2, "the queue")
38 | setoutletassist(3, "queue on/off")
39 |
40 | var MAX_ROW_COL = 32;
41 |
42 | var cols=4; // default columns
43 | var rows=4; // default rows
44 | var bgcolor = [0,0,0,1];
45 | var offcolor = [.4,.4,.4,1];
46 | var oncolor = [1,1,1,1];
47 | var textcolor = [0,0,0,1];
48 | var storecolor = [1,.2,.2,1];
49 | var queuecolor = [1,1,0,1];
50 | var activeIdx = 0;
51 | var storingIdx = null;
52 | var storing = false;
53 |
54 | var redraw = new Task(function(){storingIdx = null;draw();}, this);
55 | var idxQueue = new Array();
56 | var enqueue = false;
57 |
58 | declareattribute("cols","getcols","setcols");
59 | declareattribute("rows","getrows","setrows");
60 | if(jsarguments.length > 1) {
61 | setcols(jsarguments[1]);
62 | }
63 | if(jsarguments.length > 2) {
64 | setrows(jsarguments[2]);
65 | }
66 | sketch.default2d();
67 | draw();
68 |
69 | function getrows() {
70 | return rows;
71 | }
72 | function setrows(val) {
73 | if(arguments.length) {
74 | var n = arguments[0];
75 | if(n < 1 || n > MAX_ROW_COL) {
76 | post("rows must be between 1 and " + MAX_ROW_COL + " (inclusive)\n");
77 | } else {
78 | rows=arguments[0];
79 | draw();
80 | }
81 | }
82 | }
83 |
84 | function getcols() {
85 | return cols;
86 | }
87 | function setcols(val) {
88 | if(arguments.length) {
89 | var n = arguments[0];
90 | if(n < 1 || n > MAX_ROW_COL) {
91 | post("cols must be between 1 and " + MAX_ROW_COL + " (inclusive)\n");
92 | } else {
93 | cols=arguments[0];
94 | draw();
95 | }
96 | }
97 | }
98 |
99 | function brgb(r,g,b) {
100 | setcolor(bgcolor, r, g, b);
101 | }
102 | function offrgb(r,g,b) {
103 | setcolor(offcolor, r, g, b);
104 | }
105 | function onrgb(r,g,b) {
106 | setcolor(oncolor, r, g, b);
107 | }
108 | function textrgb(r,g,b) {
109 | setcolor(textcolor, r, g, b);
110 | }
111 | function storergb(r,g,b) {
112 | setcolor(storecolor, r, g, b);
113 | }
114 | function queuergb(r,g,b) {
115 | setcolor(queuecolor, r, g, b);
116 | }
117 | function setcolor(colorArray, r, g, b) {
118 | colorArray[0] = r/255.;
119 | colorArray[1] = g/255.;
120 | colorArray[2] = b/255.;
121 | draw();
122 | }
123 | setcolor.local = 1;
124 |
125 | function draw() {
126 | with (sketch) {
127 | glclearcolor(bgcolor);
128 | glclear();
129 | textalign("center","center");
130 | font("Arial Black");
131 | fontsize(10);
132 | colstep = 2./cols;
133 | rowstep = 2./rows;
134 | for(i=0;i 0) {
197 | activeIdx = idxQueue.shift()
198 | outputQueue()
199 | outlet(0, activeIdx)
200 | draw()
201 | } else {
202 | outputQueue()
203 | }
204 | }
205 |
206 | function queue(isEnabled) {
207 | enqueue = isEnabled
208 | if(!isEnabled) {
209 | // clear the queue when queuing is disabled
210 | idxQueue.splice(0)
211 | outputQueue()
212 | draw()
213 | }
214 | }
215 |
216 | function msg_int(idx) {
217 | recall(idx)
218 | }
219 |
220 | function set(idx) {
221 | activeIdx = idx
222 | draw()
223 | }
224 |
225 | function setq(args) {
226 | idxQueue = arrayfromargs(arguments)
227 | if(!enqueue & idxQueue.length > 0) {
228 | enqueue = true
229 | }
230 | //outputQueue()
231 | draw()
232 | }
233 |
234 | function store(idx) {
235 | activeIdx = idx
236 | storingIdx = idx
237 | outlet(1, activeIdx)
238 | draw()
239 | }
240 |
241 | function recall(idx) {
242 | if(enqueue) {
243 | idxQueue.push(idx)
244 | outputQueue()
245 | } else {
246 | activeIdx = idx;
247 | outlet(0, activeIdx);
248 | }
249 | draw();
250 | }
251 |
252 | function outputQueue() {
253 | if(idxQueue.length > 0) {
254 | outlet(2, idxQueue)
255 | } else {
256 | outlet(2, " ")
257 | }
258 | }
259 | outputQueue.local = 1
260 |
261 | function ondblclick(x,y,button,mod1,shift) {
262 | onclick(x,y,button,mod1,shift)
263 | }
264 | ondblclick.local = 1
--------------------------------------------------------------------------------
/ajm/misc/ajm.bring-help-to-front-button.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 473.0, 217.0, 380.0, 348.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 473.0, 217.0, 380.0, 348.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 1,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "newobj",
22 | "text" : "prepend bgcolor",
23 | "fontname" : "Verdana",
24 | "numinlets" : 1,
25 | "patching_rect" : [ 76.0, 57.0, 101.0, 20.0 ],
26 | "fontsize" : 11.1,
27 | "numoutlets" : 1,
28 | "id" : "obj-10",
29 | "outlettype" : [ "" ]
30 | }
31 |
32 | }
33 | , {
34 | "box" : {
35 | "maxclass" : "newobj",
36 | "text" : "route bgcolor",
37 | "fontname" : "Verdana",
38 | "numinlets" : 1,
39 | "patching_rect" : [ 76.0, 30.0, 85.0, 20.0 ],
40 | "fontsize" : 11.1,
41 | "numoutlets" : 2,
42 | "id" : "obj-9",
43 | "outlettype" : [ "", "" ]
44 | }
45 |
46 | }
47 | , {
48 | "box" : {
49 | "maxclass" : "newobj",
50 | "text" : "patcherargs",
51 | "fontname" : "Verdana",
52 | "numinlets" : 1,
53 | "patching_rect" : [ 18.0, 4.0, 77.0, 20.0 ],
54 | "fontsize" : 11.1,
55 | "numoutlets" : 2,
56 | "id" : "obj-7",
57 | "outlettype" : [ "", "" ]
58 | }
59 |
60 | }
61 | , {
62 | "box" : {
63 | "maxclass" : "message",
64 | "text" : "#1",
65 | "fontname" : "Verdana",
66 | "presentation_rect" : [ 0.0, 0.0, 32.5, 18.0 ],
67 | "numinlets" : 2,
68 | "patching_rect" : [ 18.0, 98.0, 32.5, 18.0 ],
69 | "fontsize" : 11.1,
70 | "presentation" : 1,
71 | "numoutlets" : 1,
72 | "id" : "obj-5",
73 | "outlettype" : [ "" ]
74 | }
75 |
76 | }
77 | , {
78 | "box" : {
79 | "maxclass" : "message",
80 | "text" : "help #1",
81 | "fontname" : "Verdana",
82 | "numinlets" : 2,
83 | "patching_rect" : [ 151.0, 283.0, 54.0, 18.0 ],
84 | "fontsize" : 11.1,
85 | "numoutlets" : 1,
86 | "id" : "obj-6",
87 | "outlettype" : [ "" ]
88 | }
89 |
90 | }
91 | , {
92 | "box" : {
93 | "maxclass" : "newobj",
94 | "text" : "t 0",
95 | "fontname" : "Verdana",
96 | "numinlets" : 1,
97 | "patching_rect" : [ 151.0, 215.0, 26.0, 20.0 ],
98 | "fontsize" : 11.1,
99 | "numoutlets" : 1,
100 | "id" : "obj-20",
101 | "outlettype" : [ "int" ]
102 | }
103 |
104 | }
105 | , {
106 | "box" : {
107 | "maxclass" : "newobj",
108 | "text" : "t b b 1",
109 | "fontname" : "Verdana",
110 | "numinlets" : 1,
111 | "patching_rect" : [ 18.0, 131.0, 329.0, 20.0 ],
112 | "fontsize" : 11.1,
113 | "numoutlets" : 3,
114 | "id" : "obj-19",
115 | "outlettype" : [ "bang", "bang", "int" ]
116 | }
117 |
118 | }
119 | , {
120 | "box" : {
121 | "maxclass" : "newobj",
122 | "text" : "gate",
123 | "fontname" : "Verdana",
124 | "numinlets" : 2,
125 | "patching_rect" : [ 151.0, 258.0, 35.0, 20.0 ],
126 | "fontsize" : 11.1,
127 | "numoutlets" : 1,
128 | "id" : "obj-18",
129 | "outlettype" : [ "" ]
130 | }
131 |
132 | }
133 | , {
134 | "box" : {
135 | "maxclass" : "newobj",
136 | "text" : "s #1.bring_to_front",
137 | "fontname" : "Verdana",
138 | "numinlets" : 1,
139 | "patching_rect" : [ 108.0, 161.0, 121.0, 20.0 ],
140 | "fontsize" : 11.1,
141 | "numoutlets" : 0,
142 | "id" : "obj-15"
143 | }
144 |
145 | }
146 | , {
147 | "box" : {
148 | "maxclass" : "newobj",
149 | "text" : "pcontrol",
150 | "fontname" : "Verdana",
151 | "numinlets" : 1,
152 | "patching_rect" : [ 151.0, 309.0, 56.0, 20.0 ],
153 | "fontsize" : 11.1,
154 | "numoutlets" : 1,
155 | "id" : "obj-14",
156 | "outlettype" : [ "" ]
157 | }
158 |
159 | }
160 | , {
161 | "box" : {
162 | "maxclass" : "newobj",
163 | "text" : "r ajm.bring_to_front_callback",
164 | "fontname" : "Verdana",
165 | "numinlets" : 0,
166 | "patching_rect" : [ 151.0, 187.0, 177.0, 20.0 ],
167 | "fontsize" : 11.1,
168 | "numoutlets" : 1,
169 | "id" : "obj-13",
170 | "outlettype" : [ "" ]
171 | }
172 |
173 | }
174 | ],
175 | "lines" : [ {
176 | "patchline" : {
177 | "source" : [ "obj-10", 0 ],
178 | "destination" : [ "obj-5", 0 ],
179 | "hidden" : 0,
180 | "midpoints" : [ 85.5, 85.0, 27.5, 85.0 ]
181 | }
182 |
183 | }
184 | , {
185 | "patchline" : {
186 | "source" : [ "obj-9", 0 ],
187 | "destination" : [ "obj-10", 0 ],
188 | "hidden" : 0,
189 | "midpoints" : [ ]
190 | }
191 |
192 | }
193 | , {
194 | "patchline" : {
195 | "source" : [ "obj-7", 1 ],
196 | "destination" : [ "obj-9", 0 ],
197 | "hidden" : 0,
198 | "midpoints" : [ ]
199 | }
200 |
201 | }
202 | , {
203 | "patchline" : {
204 | "source" : [ "obj-5", 0 ],
205 | "destination" : [ "obj-19", 0 ],
206 | "hidden" : 0,
207 | "midpoints" : [ ]
208 | }
209 |
210 | }
211 | , {
212 | "patchline" : {
213 | "source" : [ "obj-19", 2 ],
214 | "destination" : [ "obj-18", 0 ],
215 | "hidden" : 0,
216 | "midpoints" : [ 337.5, 240.0, 160.5, 240.0 ]
217 | }
218 |
219 | }
220 | , {
221 | "patchline" : {
222 | "source" : [ "obj-20", 0 ],
223 | "destination" : [ "obj-18", 0 ],
224 | "hidden" : 0,
225 | "midpoints" : [ ]
226 | }
227 |
228 | }
229 | , {
230 | "patchline" : {
231 | "source" : [ "obj-19", 0 ],
232 | "destination" : [ "obj-18", 1 ],
233 | "hidden" : 0,
234 | "midpoints" : [ ]
235 | }
236 |
237 | }
238 | , {
239 | "patchline" : {
240 | "source" : [ "obj-19", 1 ],
241 | "destination" : [ "obj-15", 0 ],
242 | "hidden" : 0,
243 | "midpoints" : [ 182.5, 157.0, 117.5, 157.0 ]
244 | }
245 |
246 | }
247 | , {
248 | "patchline" : {
249 | "source" : [ "obj-13", 0 ],
250 | "destination" : [ "obj-20", 0 ],
251 | "hidden" : 0,
252 | "midpoints" : [ ]
253 | }
254 |
255 | }
256 | , {
257 | "patchline" : {
258 | "source" : [ "obj-18", 0 ],
259 | "destination" : [ "obj-6", 0 ],
260 | "hidden" : 0,
261 | "midpoints" : [ ]
262 | }
263 |
264 | }
265 | , {
266 | "patchline" : {
267 | "source" : [ "obj-6", 0 ],
268 | "destination" : [ "obj-14", 0 ],
269 | "hidden" : 0,
270 | "midpoints" : [ ]
271 | }
272 |
273 | }
274 | ]
275 | }
276 |
277 | }
278 |
--------------------------------------------------------------------------------
/ajm/misc/ajm.bring-to-front-listener.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 50.0, 94.0, 640.0, 506.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 50.0, 94.0, 640.0, 506.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 0,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "outlet",
22 | "hint" : "to thispatcher",
23 | "annotation" : "to thispatcher",
24 | "numinlets" : 1,
25 | "patching_rect" : [ 83.0, 77.0, 25.0, 25.0 ],
26 | "numoutlets" : 0,
27 | "id" : "obj-2",
28 | "comment" : "to thispatcher"
29 | }
30 |
31 | }
32 | , {
33 | "box" : {
34 | "maxclass" : "newobj",
35 | "text" : "t bang front",
36 | "fontname" : "Verdana",
37 | "numinlets" : 1,
38 | "patching_rect" : [ 25.0, 47.0, 77.0, 20.0 ],
39 | "fontsize" : 11.1,
40 | "numoutlets" : 2,
41 | "id" : "obj-7",
42 | "outlettype" : [ "bang", "front" ]
43 | }
44 |
45 | }
46 | , {
47 | "box" : {
48 | "maxclass" : "newobj",
49 | "text" : "s ajm.bring_to_front_callback",
50 | "fontname" : "Verdana",
51 | "numinlets" : 1,
52 | "patching_rect" : [ 25.0, 111.0, 178.0, 20.0 ],
53 | "fontsize" : 11.1,
54 | "numoutlets" : 0,
55 | "id" : "obj-5"
56 | }
57 |
58 | }
59 | , {
60 | "box" : {
61 | "maxclass" : "newobj",
62 | "varname" : "foo",
63 | "text" : "r #1.bring_to_front",
64 | "fontname" : "Verdana",
65 | "numinlets" : 0,
66 | "patching_rect" : [ 25.0, 18.0, 120.0, 20.0 ],
67 | "fontsize" : 11.1,
68 | "numoutlets" : 1,
69 | "id" : "obj-1",
70 | "outlettype" : [ "" ]
71 | }
72 |
73 | }
74 | ],
75 | "lines" : [ {
76 | "patchline" : {
77 | "source" : [ "obj-7", 1 ],
78 | "destination" : [ "obj-2", 0 ],
79 | "hidden" : 0,
80 | "midpoints" : [ ]
81 | }
82 |
83 | }
84 | , {
85 | "patchline" : {
86 | "source" : [ "obj-1", 0 ],
87 | "destination" : [ "obj-7", 0 ],
88 | "hidden" : 0,
89 | "midpoints" : [ ]
90 | }
91 |
92 | }
93 | , {
94 | "patchline" : {
95 | "source" : [ "obj-7", 0 ],
96 | "destination" : [ "obj-5", 0 ],
97 | "hidden" : 0,
98 | "midpoints" : [ ]
99 | }
100 |
101 | }
102 | ]
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/ajm/misc/ajm.bring-to-front.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 396.0, 91.0, 372.0, 284.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 396.0, 91.0, 372.0, 284.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 0,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "message",
22 | "text" : "load #1",
23 | "fontname" : "Verdana",
24 | "numinlets" : 2,
25 | "patching_rect" : [ 152.0, 208.0, 54.0, 18.0 ],
26 | "fontsize" : 11.1,
27 | "numoutlets" : 1,
28 | "id" : "obj-6",
29 | "outlettype" : [ "" ]
30 | }
31 |
32 | }
33 | , {
34 | "box" : {
35 | "maxclass" : "inlet",
36 | "numinlets" : 0,
37 | "patching_rect" : [ 19.0, 20.0, 25.0, 25.0 ],
38 | "numoutlets" : 1,
39 | "id" : "obj-3",
40 | "outlettype" : [ "" ],
41 | "comment" : ""
42 | }
43 |
44 | }
45 | , {
46 | "box" : {
47 | "maxclass" : "newobj",
48 | "text" : "t 0",
49 | "fontname" : "Verdana",
50 | "numinlets" : 1,
51 | "patching_rect" : [ 152.0, 134.0, 26.0, 20.0 ],
52 | "fontsize" : 11.1,
53 | "numoutlets" : 1,
54 | "id" : "obj-20",
55 | "outlettype" : [ "int" ]
56 | }
57 |
58 | }
59 | , {
60 | "box" : {
61 | "maxclass" : "newobj",
62 | "text" : "t b b 1",
63 | "fontname" : "Verdana",
64 | "numinlets" : 1,
65 | "patching_rect" : [ 19.0, 52.0, 329.0, 20.0 ],
66 | "fontsize" : 11.1,
67 | "numoutlets" : 3,
68 | "id" : "obj-19",
69 | "outlettype" : [ "bang", "bang", "int" ]
70 | }
71 |
72 | }
73 | , {
74 | "box" : {
75 | "maxclass" : "newobj",
76 | "text" : "gate",
77 | "fontname" : "Verdana",
78 | "numinlets" : 2,
79 | "patching_rect" : [ 152.0, 177.0, 35.0, 20.0 ],
80 | "fontsize" : 11.1,
81 | "numoutlets" : 1,
82 | "id" : "obj-18",
83 | "outlettype" : [ "" ]
84 | }
85 |
86 | }
87 | , {
88 | "box" : {
89 | "maxclass" : "newobj",
90 | "text" : "s #1.bring_to_front",
91 | "fontname" : "Verdana",
92 | "numinlets" : 1,
93 | "patching_rect" : [ 109.0, 82.0, 121.0, 20.0 ],
94 | "fontsize" : 11.1,
95 | "numoutlets" : 0,
96 | "id" : "obj-15"
97 | }
98 |
99 | }
100 | , {
101 | "box" : {
102 | "maxclass" : "newobj",
103 | "text" : "pcontrol",
104 | "fontname" : "Verdana",
105 | "numinlets" : 1,
106 | "patching_rect" : [ 152.0, 238.0, 56.0, 20.0 ],
107 | "fontsize" : 11.1,
108 | "numoutlets" : 1,
109 | "id" : "obj-14",
110 | "outlettype" : [ "" ]
111 | }
112 |
113 | }
114 | , {
115 | "box" : {
116 | "maxclass" : "newobj",
117 | "text" : "r ajm.bring_to_front_callback",
118 | "fontname" : "Verdana",
119 | "numinlets" : 0,
120 | "patching_rect" : [ 152.0, 106.0, 177.0, 20.0 ],
121 | "fontsize" : 11.1,
122 | "numoutlets" : 1,
123 | "id" : "obj-13",
124 | "outlettype" : [ "" ]
125 | }
126 |
127 | }
128 | ],
129 | "lines" : [ {
130 | "patchline" : {
131 | "source" : [ "obj-18", 0 ],
132 | "destination" : [ "obj-6", 0 ],
133 | "hidden" : 0,
134 | "midpoints" : [ ]
135 | }
136 |
137 | }
138 | , {
139 | "patchline" : {
140 | "source" : [ "obj-19", 2 ],
141 | "destination" : [ "obj-18", 0 ],
142 | "hidden" : 0,
143 | "midpoints" : [ 338.5, 159.0, 161.5, 159.0 ]
144 | }
145 |
146 | }
147 | , {
148 | "patchline" : {
149 | "source" : [ "obj-20", 0 ],
150 | "destination" : [ "obj-18", 0 ],
151 | "hidden" : 0,
152 | "midpoints" : [ ]
153 | }
154 |
155 | }
156 | , {
157 | "patchline" : {
158 | "source" : [ "obj-19", 0 ],
159 | "destination" : [ "obj-18", 1 ],
160 | "hidden" : 0,
161 | "midpoints" : [ ]
162 | }
163 |
164 | }
165 | , {
166 | "patchline" : {
167 | "source" : [ "obj-3", 0 ],
168 | "destination" : [ "obj-19", 0 ],
169 | "hidden" : 0,
170 | "midpoints" : [ ]
171 | }
172 |
173 | }
174 | , {
175 | "patchline" : {
176 | "source" : [ "obj-19", 1 ],
177 | "destination" : [ "obj-15", 0 ],
178 | "hidden" : 0,
179 | "midpoints" : [ 183.5, 78.0, 118.5, 78.0 ]
180 | }
181 |
182 | }
183 | , {
184 | "patchline" : {
185 | "source" : [ "obj-13", 0 ],
186 | "destination" : [ "obj-20", 0 ],
187 | "hidden" : 0,
188 | "midpoints" : [ ]
189 | }
190 |
191 | }
192 | , {
193 | "patchline" : {
194 | "source" : [ "obj-6", 0 ],
195 | "destination" : [ "obj-14", 0 ],
196 | "hidden" : 0,
197 | "midpoints" : [ ]
198 | }
199 |
200 | }
201 | ]
202 | }
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/ajm/misc/ajm.envthispoly.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 10.0, 59.0, 281.0, 244.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 10.0, 59.0, 281.0, 244.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 0,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "newobj",
22 | "text" : "sel 0",
23 | "fontname" : "Verdana",
24 | "numinlets" : 2,
25 | "patching_rect" : [ 144.0, 44.0, 46.0, 20.0 ],
26 | "fontsize" : 11.0,
27 | "numoutlets" : 2,
28 | "id" : "obj-1",
29 | "outlettype" : [ "bang", "" ]
30 | }
31 |
32 | }
33 | , {
34 | "box" : {
35 | "maxclass" : "message",
36 | "text" : "1, mute 0",
37 | "fontname" : "Verdana",
38 | "numinlets" : 2,
39 | "patching_rect" : [ 171.0, 139.0, 65.0, 18.0 ],
40 | "fontsize" : 11.0,
41 | "numoutlets" : 1,
42 | "id" : "obj-2",
43 | "outlettype" : [ "" ]
44 | }
45 |
46 | }
47 | , {
48 | "box" : {
49 | "maxclass" : "message",
50 | "text" : "mute 1, 0",
51 | "fontname" : "Verdana",
52 | "numinlets" : 2,
53 | "patching_rect" : [ 59.0, 128.0, 65.0, 18.0 ],
54 | "fontsize" : 11.0,
55 | "numoutlets" : 1,
56 | "id" : "obj-3",
57 | "outlettype" : [ "" ]
58 | }
59 |
60 | }
61 | , {
62 | "box" : {
63 | "maxclass" : "comment",
64 | "text" : "make busy and unmute",
65 | "linecount" : 2,
66 | "fontname" : "Verdana",
67 | "numinlets" : 1,
68 | "patching_rect" : [ 182.0, 104.0, 75.0, 33.0 ],
69 | "fontsize" : 11.0,
70 | "numoutlets" : 0,
71 | "id" : "obj-4"
72 | }
73 |
74 | }
75 | , {
76 | "box" : {
77 | "maxclass" : "newobj",
78 | "text" : "loadbang",
79 | "fontname" : "Verdana",
80 | "numinlets" : 1,
81 | "patching_rect" : [ 59.0, 70.0, 59.0, 20.0 ],
82 | "fontsize" : 11.0,
83 | "numoutlets" : 1,
84 | "id" : "obj-5",
85 | "outlettype" : [ "bang" ]
86 | }
87 |
88 | }
89 | , {
90 | "box" : {
91 | "maxclass" : "comment",
92 | "text" : "mute and make available",
93 | "linecount" : 2,
94 | "fontname" : "Verdana",
95 | "numinlets" : 1,
96 | "patching_rect" : [ 68.0, 98.0, 92.0, 33.0 ],
97 | "fontsize" : 11.0,
98 | "numoutlets" : 0,
99 | "id" : "obj-6"
100 | }
101 |
102 | }
103 | , {
104 | "box" : {
105 | "maxclass" : "comment",
106 | "text" : "start playing:",
107 | "linecount" : 2,
108 | "fontname" : "Verdana",
109 | "numinlets" : 1,
110 | "patching_rect" : [ 182.0, 67.0, 53.0, 33.0 ],
111 | "fontsize" : 11.0,
112 | "numoutlets" : 0,
113 | "id" : "obj-7"
114 | }
115 |
116 | }
117 | , {
118 | "box" : {
119 | "maxclass" : "comment",
120 | "text" : "stop playing:",
121 | "linecount" : 2,
122 | "fontname" : "Verdana",
123 | "numinlets" : 1,
124 | "patching_rect" : [ 18.0, 96.0, 53.0, 33.0 ],
125 | "fontsize" : 11.0,
126 | "numoutlets" : 0,
127 | "id" : "obj-8"
128 | }
129 |
130 | }
131 | , {
132 | "box" : {
133 | "maxclass" : "inlet",
134 | "hint" : "from ajm.env~ right outlet",
135 | "annotation" : "from ajm.env~ right outlet",
136 | "numinlets" : 0,
137 | "patching_rect" : [ 144.0, 12.0, 22.0, 22.0 ],
138 | "numoutlets" : 1,
139 | "id" : "obj-9",
140 | "outlettype" : [ "" ],
141 | "comment" : "from ajm.env~ right outlet"
142 | }
143 |
144 | }
145 | , {
146 | "box" : {
147 | "maxclass" : "outlet",
148 | "hint" : "to thispoly~",
149 | "annotation" : "to thispoly~",
150 | "numinlets" : 1,
151 | "patching_rect" : [ 136.0, 191.0, 22.0, 22.0 ],
152 | "numoutlets" : 0,
153 | "id" : "obj-10",
154 | "comment" : "to thispoly~"
155 | }
156 |
157 | }
158 | ],
159 | "lines" : [ {
160 | "patchline" : {
161 | "source" : [ "obj-1", 1 ],
162 | "destination" : [ "obj-2", 0 ],
163 | "hidden" : 0,
164 | "midpoints" : [ ]
165 | }
166 |
167 | }
168 | , {
169 | "patchline" : {
170 | "source" : [ "obj-9", 0 ],
171 | "destination" : [ "obj-1", 0 ],
172 | "hidden" : 0,
173 | "midpoints" : [ ]
174 | }
175 |
176 | }
177 | , {
178 | "patchline" : {
179 | "source" : [ "obj-3", 0 ],
180 | "destination" : [ "obj-10", 0 ],
181 | "hidden" : 0,
182 | "midpoints" : [ 68.5, 172.0, 145.5, 172.0 ]
183 | }
184 |
185 | }
186 | , {
187 | "patchline" : {
188 | "source" : [ "obj-2", 0 ],
189 | "destination" : [ "obj-10", 0 ],
190 | "hidden" : 0,
191 | "midpoints" : [ 180.5, 172.0, 145.5, 172.0 ]
192 | }
193 |
194 | }
195 | , {
196 | "patchline" : {
197 | "source" : [ "obj-1", 0 ],
198 | "destination" : [ "obj-3", 0 ],
199 | "hidden" : 0,
200 | "midpoints" : [ 153.5, 95.0, 68.5, 95.0 ]
201 | }
202 |
203 | }
204 | , {
205 | "patchline" : {
206 | "source" : [ "obj-5", 0 ],
207 | "destination" : [ "obj-3", 0 ],
208 | "hidden" : 0,
209 | "midpoints" : [ ]
210 | }
211 |
212 | }
213 | ]
214 | }
215 |
216 | }
217 |
--------------------------------------------------------------------------------
/ajm/misc/ajm.polymakenote.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 61.0, 62.0, 323.0, 254.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 61.0, 62.0, 323.0, 254.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 0,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "newobj",
22 | "text" : "makenote",
23 | "fontname" : "Verdana",
24 | "numinlets" : 3,
25 | "numoutlets" : 2,
26 | "patching_rect" : [ 18.0, 52.0, 63.0, 20.0 ],
27 | "fontsize" : 11.0,
28 | "outlettype" : [ "int", "int" ],
29 | "id" : "obj-1"
30 | }
31 |
32 | }
33 | , {
34 | "box" : {
35 | "maxclass" : "newobj",
36 | "text" : "out 1",
37 | "fontname" : "Verdana",
38 | "numinlets" : 1,
39 | "numoutlets" : 0,
40 | "patching_rect" : [ 21.0, 191.0, 41.0, 20.0 ],
41 | "fontsize" : 11.0,
42 | "id" : "obj-2",
43 | "saved_object_attributes" : {
44 | "attr_comment" : ""
45 | }
46 |
47 | }
48 |
49 | }
50 | , {
51 | "box" : {
52 | "maxclass" : "comment",
53 | "text" : "for mutemap & busymap (required for ajm.busymap)",
54 | "linecount" : 2,
55 | "fontname" : "Verdana",
56 | "numinlets" : 1,
57 | "numoutlets" : 0,
58 | "patching_rect" : [ 71.0, 188.0, 163.0, 33.0 ],
59 | "fontsize" : 11.0,
60 | "id" : "obj-3"
61 | }
62 |
63 | }
64 | , {
65 | "box" : {
66 | "maxclass" : "newobj",
67 | "text" : "!= 0",
68 | "fontname" : "Verdana",
69 | "numinlets" : 2,
70 | "numoutlets" : 1,
71 | "patching_rect" : [ 62.0, 85.0, 33.0, 20.0 ],
72 | "fontsize" : 11.0,
73 | "outlettype" : [ "int" ],
74 | "id" : "obj-4"
75 | }
76 |
77 | }
78 | , {
79 | "box" : {
80 | "maxclass" : "newobj",
81 | "text" : "thispoly~",
82 | "fontname" : "Verdana",
83 | "numinlets" : 1,
84 | "numoutlets" : 2,
85 | "patching_rect" : [ 62.0, 118.0, 64.0, 20.0 ],
86 | "fontsize" : 11.0,
87 | "outlettype" : [ "int", "int" ],
88 | "id" : "obj-5"
89 | }
90 |
91 | }
92 | , {
93 | "box" : {
94 | "maxclass" : "newobj",
95 | "text" : "in 1",
96 | "fontname" : "Verdana",
97 | "numinlets" : 1,
98 | "numoutlets" : 1,
99 | "patching_rect" : [ 18.0, 19.0, 29.0, 20.0 ],
100 | "fontsize" : 11.0,
101 | "outlettype" : [ "" ],
102 | "id" : "obj-6",
103 | "saved_object_attributes" : {
104 | "attr_comment" : ""
105 | }
106 |
107 | }
108 |
109 | }
110 | , {
111 | "box" : {
112 | "maxclass" : "comment",
113 | "text" : "make busy if non-zero velocity, then available after note off",
114 | "linecount" : 2,
115 | "fontname" : "Verdana",
116 | "numinlets" : 1,
117 | "numoutlets" : 0,
118 | "patching_rect" : [ 101.0, 80.0, 201.0, 33.0 ],
119 | "fontsize" : 11.0,
120 | "id" : "obj-7"
121 | }
122 |
123 | }
124 | ],
125 | "lines" : [ {
126 | "patchline" : {
127 | "source" : [ "obj-4", 0 ],
128 | "destination" : [ "obj-5", 0 ],
129 | "hidden" : 0,
130 | "midpoints" : [ ]
131 | }
132 |
133 | }
134 | , {
135 | "patchline" : {
136 | "source" : [ "obj-1", 1 ],
137 | "destination" : [ "obj-4", 0 ],
138 | "hidden" : 0,
139 | "midpoints" : [ ]
140 | }
141 |
142 | }
143 | , {
144 | "patchline" : {
145 | "source" : [ "obj-6", 0 ],
146 | "destination" : [ "obj-1", 0 ],
147 | "hidden" : 0,
148 | "midpoints" : [ ]
149 | }
150 |
151 | }
152 | ]
153 | }
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/ajm/misc/ajm.polyprint.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 118.0, 58.0, 230.0, 310.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 118.0, 58.0, 230.0, 310.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 0,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "newobj",
22 | "text" : "out 1",
23 | "patching_rect" : [ 140.0, 230.0, 33.0, 17.0 ],
24 | "id" : "obj-1",
25 | "fontname" : "Verdana",
26 | "fontsize" : 9.0,
27 | "numinlets" : 1,
28 | "numoutlets" : 0,
29 | "saved_object_attributes" : {
30 | "attr_comment" : ""
31 | }
32 |
33 | }
34 |
35 | }
36 | , {
37 | "box" : {
38 | "maxclass" : "comment",
39 | "text" : "for mutemap & busymap",
40 | "linecount" : 2,
41 | "patching_rect" : [ 142.0, 199.0, 70.0, 28.0 ],
42 | "id" : "obj-2",
43 | "fontname" : "Verdana",
44 | "fontsize" : 9.0,
45 | "numinlets" : 1,
46 | "numoutlets" : 0
47 | }
48 |
49 | }
50 | , {
51 | "box" : {
52 | "maxclass" : "newobj",
53 | "text" : "t 0 1",
54 | "patching_rect" : [ 33.0, 61.0, 32.0, 17.0 ],
55 | "outlettype" : [ "int", "int" ],
56 | "id" : "obj-3",
57 | "fontname" : "Verdana",
58 | "fontsize" : 9.0,
59 | "numinlets" : 1,
60 | "numoutlets" : 2
61 | }
62 |
63 | }
64 | , {
65 | "box" : {
66 | "maxclass" : "newobj",
67 | "text" : "pipe 10",
68 | "patching_rect" : [ 33.0, 88.0, 43.0, 17.0 ],
69 | "outlettype" : [ "" ],
70 | "id" : "obj-4",
71 | "fontname" : "Verdana",
72 | "fontsize" : 9.0,
73 | "numinlets" : 2,
74 | "numoutlets" : 1
75 | }
76 |
77 | }
78 | , {
79 | "box" : {
80 | "maxclass" : "newobj",
81 | "text" : "prepend voice",
82 | "patching_rect" : [ 18.0, 203.0, 72.0, 17.0 ],
83 | "outlettype" : [ "" ],
84 | "id" : "obj-5",
85 | "fontname" : "Verdana",
86 | "fontsize" : 9.0,
87 | "numinlets" : 1,
88 | "numoutlets" : 1
89 | }
90 |
91 | }
92 | , {
93 | "box" : {
94 | "maxclass" : "newobj",
95 | "text" : "loadbang",
96 | "patching_rect" : [ 101.0, 86.0, 50.0, 17.0 ],
97 | "outlettype" : [ "bang" ],
98 | "id" : "obj-6",
99 | "fontname" : "Verdana",
100 | "fontsize" : 9.0,
101 | "numinlets" : 1,
102 | "numoutlets" : 1
103 | }
104 |
105 | }
106 | , {
107 | "box" : {
108 | "maxclass" : "message",
109 | "text" : "set $1:",
110 | "patching_rect" : [ 87.0, 140.0, 43.0, 15.0 ],
111 | "outlettype" : [ "" ],
112 | "id" : "obj-7",
113 | "fontname" : "Verdana",
114 | "fontsize" : 9.0,
115 | "numinlets" : 2,
116 | "numoutlets" : 1
117 | }
118 |
119 | }
120 | , {
121 | "box" : {
122 | "maxclass" : "newobj",
123 | "text" : "prepend unknown:",
124 | "patching_rect" : [ 18.0, 179.0, 94.0, 17.0 ],
125 | "outlettype" : [ "" ],
126 | "id" : "obj-8",
127 | "fontname" : "Verdana",
128 | "fontsize" : 9.0,
129 | "numinlets" : 1,
130 | "numoutlets" : 1
131 | }
132 |
133 | }
134 | , {
135 | "box" : {
136 | "maxclass" : "newobj",
137 | "text" : "thispoly~",
138 | "patching_rect" : [ 87.0, 117.0, 52.0, 17.0 ],
139 | "outlettype" : [ "int", "int" ],
140 | "id" : "obj-9",
141 | "fontname" : "Verdana",
142 | "fontsize" : 9.0,
143 | "numinlets" : 1,
144 | "numoutlets" : 2
145 | }
146 |
147 | }
148 | , {
149 | "box" : {
150 | "maxclass" : "newobj",
151 | "text" : "print polyprint",
152 | "patching_rect" : [ 18.0, 227.0, 76.0, 17.0 ],
153 | "id" : "obj-10",
154 | "fontname" : "Verdana",
155 | "fontsize" : 9.0,
156 | "numinlets" : 1,
157 | "numoutlets" : 0
158 | }
159 |
160 | }
161 | , {
162 | "box" : {
163 | "maxclass" : "newobj",
164 | "text" : "in 1",
165 | "patching_rect" : [ 18.0, 28.0, 26.0, 17.0 ],
166 | "outlettype" : [ "" ],
167 | "id" : "obj-11",
168 | "fontname" : "Verdana",
169 | "fontsize" : 9.0,
170 | "numinlets" : 1,
171 | "numoutlets" : 1,
172 | "saved_object_attributes" : {
173 | "attr_comment" : ""
174 | }
175 |
176 | }
177 |
178 | }
179 | , {
180 | "box" : {
181 | "maxclass" : "comment",
182 | "text" : "make busy briefly",
183 | "patching_rect" : [ 68.0, 59.0, 100.0, 17.0 ],
184 | "id" : "obj-12",
185 | "fontname" : "Verdana",
186 | "fontsize" : 9.0,
187 | "numinlets" : 1,
188 | "numoutlets" : 0
189 | }
190 |
191 | }
192 | ],
193 | "lines" : [ {
194 | "patchline" : {
195 | "source" : [ "obj-9", 0 ],
196 | "destination" : [ "obj-7", 0 ],
197 | "hidden" : 0,
198 | "midpoints" : [ ]
199 | }
200 |
201 | }
202 | , {
203 | "patchline" : {
204 | "source" : [ "obj-4", 0 ],
205 | "destination" : [ "obj-9", 0 ],
206 | "hidden" : 0,
207 | "midpoints" : [ 42.5, 110.0, 96.5, 110.0 ]
208 | }
209 |
210 | }
211 | , {
212 | "patchline" : {
213 | "source" : [ "obj-3", 1 ],
214 | "destination" : [ "obj-9", 0 ],
215 | "hidden" : 0,
216 | "midpoints" : [ 55.5, 81.0, 96.5, 81.0 ]
217 | }
218 |
219 | }
220 | , {
221 | "patchline" : {
222 | "source" : [ "obj-6", 0 ],
223 | "destination" : [ "obj-9", 0 ],
224 | "hidden" : 0,
225 | "midpoints" : [ ]
226 | }
227 |
228 | }
229 | , {
230 | "patchline" : {
231 | "source" : [ "obj-3", 0 ],
232 | "destination" : [ "obj-4", 0 ],
233 | "hidden" : 0,
234 | "midpoints" : [ ]
235 | }
236 |
237 | }
238 | , {
239 | "patchline" : {
240 | "source" : [ "obj-11", 0 ],
241 | "destination" : [ "obj-3", 0 ],
242 | "hidden" : 0,
243 | "midpoints" : [ ]
244 | }
245 |
246 | }
247 | , {
248 | "patchline" : {
249 | "source" : [ "obj-5", 0 ],
250 | "destination" : [ "obj-10", 0 ],
251 | "hidden" : 0,
252 | "midpoints" : [ ]
253 | }
254 |
255 | }
256 | , {
257 | "patchline" : {
258 | "source" : [ "obj-8", 0 ],
259 | "destination" : [ "obj-5", 0 ],
260 | "hidden" : 0,
261 | "midpoints" : [ ]
262 | }
263 |
264 | }
265 | , {
266 | "patchline" : {
267 | "source" : [ "obj-7", 0 ],
268 | "destination" : [ "obj-8", 0 ],
269 | "hidden" : 0,
270 | "midpoints" : [ ]
271 | }
272 |
273 | }
274 | , {
275 | "patchline" : {
276 | "source" : [ "obj-11", 0 ],
277 | "destination" : [ "obj-8", 0 ],
278 | "hidden" : 0,
279 | "midpoints" : [ ]
280 | }
281 |
282 | }
283 | ]
284 | }
285 |
286 | }
287 |
--------------------------------------------------------------------------------
/ajm/misc/ajm.translate-duration.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 50.0, 94.0, 362.0, 256.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 50.0, 94.0, 362.0, 256.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 0,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "outlet",
22 | "numoutlets" : 0,
23 | "patching_rect" : [ 121.0, 192.0, 25.0, 25.0 ],
24 | "id" : "obj-7",
25 | "numinlets" : 1,
26 | "comment" : ""
27 | }
28 |
29 | }
30 | , {
31 | "box" : {
32 | "maxclass" : "inlet",
33 | "numoutlets" : 1,
34 | "outlettype" : [ "" ],
35 | "patching_rect" : [ 171.0, 33.0, 25.0, 25.0 ],
36 | "id" : "obj-5",
37 | "numinlets" : 0,
38 | "presentation_rect" : [ 202.0, 80.0, 0.0, 0.0 ],
39 | "comment" : ""
40 | }
41 |
42 | }
43 | , {
44 | "box" : {
45 | "maxclass" : "newobj",
46 | "text" : "route int float",
47 | "numoutlets" : 3,
48 | "fontname" : "Verdana",
49 | "outlettype" : [ "", "", "" ],
50 | "patching_rect" : [ 87.0, 77.0, 87.0, 20.0 ],
51 | "id" : "obj-4",
52 | "fontsize" : 11.1,
53 | "numinlets" : 1
54 | }
55 |
56 | }
57 | , {
58 | "box" : {
59 | "maxclass" : "newobj",
60 | "text" : "translate @out ticks",
61 | "numoutlets" : 1,
62 | "fontname" : "Verdana",
63 | "outlettype" : [ "" ],
64 | "patching_rect" : [ 171.0, 134.0, 122.0, 20.0 ],
65 | "id" : "obj-1",
66 | "fontsize" : 11.1,
67 | "numinlets" : 1
68 | }
69 |
70 | }
71 | , {
72 | "box" : {
73 | "maxclass" : "inlet",
74 | "numoutlets" : 1,
75 | "outlettype" : [ "" ],
76 | "patching_rect" : [ 87.0, 32.0, 25.0, 25.0 ],
77 | "id" : "obj-2",
78 | "numinlets" : 0,
79 | "comment" : ""
80 | }
81 |
82 | }
83 | ],
84 | "lines" : [ {
85 | "patchline" : {
86 | "source" : [ "obj-1", 0 ],
87 | "destination" : [ "obj-7", 0 ],
88 | "hidden" : 0,
89 | "midpoints" : [ ]
90 | }
91 |
92 | }
93 | , {
94 | "patchline" : {
95 | "source" : [ "obj-4", 2 ],
96 | "destination" : [ "obj-1", 0 ],
97 | "hidden" : 0,
98 | "midpoints" : [ ]
99 | }
100 |
101 | }
102 | , {
103 | "patchline" : {
104 | "source" : [ "obj-4", 1 ],
105 | "destination" : [ "obj-7", 0 ],
106 | "hidden" : 0,
107 | "midpoints" : [ ]
108 | }
109 |
110 | }
111 | , {
112 | "patchline" : {
113 | "source" : [ "obj-4", 0 ],
114 | "destination" : [ "obj-7", 0 ],
115 | "hidden" : 0,
116 | "midpoints" : [ ]
117 | }
118 |
119 | }
120 | , {
121 | "patchline" : {
122 | "source" : [ "obj-5", 0 ],
123 | "destination" : [ "obj-1", 0 ],
124 | "hidden" : 0,
125 | "midpoints" : [ ]
126 | }
127 |
128 | }
129 | , {
130 | "patchline" : {
131 | "source" : [ "obj-2", 0 ],
132 | "destination" : [ "obj-4", 0 ],
133 | "hidden" : 0,
134 | "midpoints" : [ ]
135 | }
136 |
137 | }
138 | ]
139 | }
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/ajm/misc/ajm.translate-line.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "rect" : [ 25.0, 69.0, 354.0, 353.0 ],
5 | "bglocked" : 0,
6 | "defrect" : [ 25.0, 69.0, 354.0, 353.0 ],
7 | "openrect" : [ 0.0, 0.0, 0.0, 0.0 ],
8 | "openinpresentation" : 0,
9 | "default_fontsize" : 11.1,
10 | "default_fontface" : 0,
11 | "default_fontname" : "Verdana",
12 | "gridonopen" : 0,
13 | "gridsize" : [ 15.0, 15.0 ],
14 | "gridsnaponopen" : 0,
15 | "toolbarvisible" : 1,
16 | "boxanimatetime" : 200,
17 | "imprint" : 0,
18 | "metadata" : [ ],
19 | "boxes" : [ {
20 | "box" : {
21 | "maxclass" : "outlet",
22 | "numinlets" : 1,
23 | "patching_rect" : [ 61.0, 297.0, 25.0, 25.0 ],
24 | "numoutlets" : 0,
25 | "id" : "obj-41",
26 | "comment" : ""
27 | }
28 |
29 | }
30 | , {
31 | "box" : {
32 | "maxclass" : "newobj",
33 | "text" : "prepend transport",
34 | "fontname" : "Verdana",
35 | "numinlets" : 1,
36 | "patching_rect" : [ 148.0, 134.0, 111.0, 20.0 ],
37 | "fontsize" : 11.1,
38 | "numoutlets" : 1,
39 | "id" : "obj-39",
40 | "outlettype" : [ "" ]
41 | }
42 |
43 | }
44 | , {
45 | "box" : {
46 | "maxclass" : "inlet",
47 | "numinlets" : 0,
48 | "patching_rect" : [ 43.0, 18.0, 25.0, 25.0 ],
49 | "numoutlets" : 1,
50 | "id" : "obj-36",
51 | "outlettype" : [ "" ],
52 | "comment" : ""
53 | }
54 |
55 | }
56 | , {
57 | "box" : {
58 | "maxclass" : "newobj",
59 | "text" : "route transport",
60 | "fontname" : "Verdana",
61 | "numinlets" : 1,
62 | "patching_rect" : [ 148.0, 77.0, 95.0, 20.0 ],
63 | "fontsize" : 11.1,
64 | "numoutlets" : 2,
65 | "id" : "obj-32",
66 | "outlettype" : [ "", "" ]
67 | }
68 |
69 | }
70 | , {
71 | "box" : {
72 | "maxclass" : "newobj",
73 | "text" : "patcherargs",
74 | "fontname" : "Verdana",
75 | "numinlets" : 1,
76 | "patching_rect" : [ 90.0, 22.0, 77.0, 20.0 ],
77 | "fontsize" : 11.1,
78 | "numoutlets" : 2,
79 | "id" : "obj-31",
80 | "outlettype" : [ "", "" ]
81 | }
82 |
83 | }
84 | , {
85 | "box" : {
86 | "maxclass" : "newobj",
87 | "text" : "route int float",
88 | "fontname" : "Verdana",
89 | "numinlets" : 1,
90 | "patching_rect" : [ 61.0, 202.0, 87.0, 20.0 ],
91 | "fontsize" : 11.1,
92 | "numoutlets" : 3,
93 | "id" : "obj-30",
94 | "outlettype" : [ "", "", "" ]
95 | }
96 |
97 | }
98 | , {
99 | "box" : {
100 | "maxclass" : "newobj",
101 | "text" : "t b l",
102 | "fontname" : "Verdana",
103 | "numinlets" : 1,
104 | "patching_rect" : [ 47.0, 148.0, 33.0, 20.0 ],
105 | "fontsize" : 11.1,
106 | "numoutlets" : 2,
107 | "id" : "obj-13",
108 | "outlettype" : [ "bang", "" ]
109 | }
110 |
111 | }
112 | , {
113 | "box" : {
114 | "maxclass" : "newobj",
115 | "text" : "zl group 1024",
116 | "fontname" : "Verdana",
117 | "presentation_rect" : [ 259.0, 330.0, 0.0, 0.0 ],
118 | "numinlets" : 2,
119 | "patching_rect" : [ 61.0, 266.0, 88.0, 20.0 ],
120 | "fontsize" : 11.1,
121 | "numoutlets" : 2,
122 | "id" : "obj-12",
123 | "outlettype" : [ "", "" ]
124 | }
125 |
126 | }
127 | , {
128 | "box" : {
129 | "maxclass" : "newobj",
130 | "text" : "zl iter 1",
131 | "fontname" : "Verdana",
132 | "numinlets" : 2,
133 | "patching_rect" : [ 61.0, 175.0, 53.0, 20.0 ],
134 | "fontsize" : 11.1,
135 | "numoutlets" : 2,
136 | "id" : "obj-2",
137 | "outlettype" : [ "", "" ]
138 | }
139 |
140 | }
141 | , {
142 | "box" : {
143 | "maxclass" : "newobj",
144 | "text" : "translate @in notevalues @out ms",
145 | "fontname" : "Verdana",
146 | "numinlets" : 1,
147 | "patching_rect" : [ 129.0, 232.0, 203.0, 20.0 ],
148 | "fontsize" : 11.1,
149 | "numoutlets" : 1,
150 | "id" : "obj-1",
151 | "outlettype" : [ "" ]
152 | }
153 |
154 | }
155 | ],
156 | "lines" : [ {
157 | "patchline" : {
158 | "source" : [ "obj-12", 0 ],
159 | "destination" : [ "obj-41", 0 ],
160 | "hidden" : 0,
161 | "midpoints" : [ ]
162 | }
163 |
164 | }
165 | , {
166 | "patchline" : {
167 | "source" : [ "obj-39", 0 ],
168 | "destination" : [ "obj-1", 0 ],
169 | "hidden" : 0,
170 | "midpoints" : [ 157.5, 226.0, 138.5, 226.0 ]
171 | }
172 |
173 | }
174 | , {
175 | "patchline" : {
176 | "source" : [ "obj-32", 0 ],
177 | "destination" : [ "obj-39", 0 ],
178 | "hidden" : 0,
179 | "midpoints" : [ ]
180 | }
181 |
182 | }
183 | , {
184 | "patchline" : {
185 | "source" : [ "obj-32", 1 ],
186 | "destination" : [ "obj-13", 0 ],
187 | "hidden" : 0,
188 | "midpoints" : [ ]
189 | }
190 |
191 | }
192 | , {
193 | "patchline" : {
194 | "source" : [ "obj-36", 0 ],
195 | "destination" : [ "obj-32", 0 ],
196 | "hidden" : 0,
197 | "midpoints" : [ 52.5, 66.0, 157.5, 66.0 ]
198 | }
199 |
200 | }
201 | , {
202 | "patchline" : {
203 | "source" : [ "obj-31", 1 ],
204 | "destination" : [ "obj-32", 0 ],
205 | "hidden" : 0,
206 | "midpoints" : [ ]
207 | }
208 |
209 | }
210 | , {
211 | "patchline" : {
212 | "source" : [ "obj-30", 1 ],
213 | "destination" : [ "obj-12", 0 ],
214 | "hidden" : 0,
215 | "midpoints" : [ 104.5, 259.0, 70.5, 259.0 ]
216 | }
217 |
218 | }
219 | , {
220 | "patchline" : {
221 | "source" : [ "obj-30", 0 ],
222 | "destination" : [ "obj-12", 0 ],
223 | "hidden" : 0,
224 | "midpoints" : [ ]
225 | }
226 |
227 | }
228 | , {
229 | "patchline" : {
230 | "source" : [ "obj-30", 2 ],
231 | "destination" : [ "obj-1", 0 ],
232 | "hidden" : 0,
233 | "midpoints" : [ ]
234 | }
235 |
236 | }
237 | , {
238 | "patchline" : {
239 | "source" : [ "obj-2", 0 ],
240 | "destination" : [ "obj-30", 0 ],
241 | "hidden" : 0,
242 | "midpoints" : [ ]
243 | }
244 |
245 | }
246 | , {
247 | "patchline" : {
248 | "source" : [ "obj-13", 1 ],
249 | "destination" : [ "obj-2", 0 ],
250 | "hidden" : 0,
251 | "midpoints" : [ ]
252 | }
253 |
254 | }
255 | , {
256 | "patchline" : {
257 | "source" : [ "obj-13", 0 ],
258 | "destination" : [ "obj-12", 0 ],
259 | "hidden" : 0,
260 | "midpoints" : [ 56.5, 259.0, 70.5, 259.0 ]
261 | }
262 |
263 | }
264 | , {
265 | "patchline" : {
266 | "source" : [ "obj-1", 0 ],
267 | "destination" : [ "obj-12", 0 ],
268 | "hidden" : 0,
269 | "midpoints" : [ 138.5, 259.0, 70.5, 259.0 ]
270 | }
271 |
272 | }
273 | ]
274 | }
275 |
276 | }
277 |
--------------------------------------------------------------------------------
/ajm/ruby/ajm_cosy.rb:
--------------------------------------------------------------------------------
1 | require 'cosy'
2 | include Cosy
3 |
4 | OBJECT_NAME = 'ajm.cosy'
5 | TICKS_PER_QUARTER_NOTE = 480
6 |
7 | module Cosy
8 |
9 | class MaxRenderer < AbstractRenderer
10 | attr_accessor :seq, :time_to_next, :prev_duration, :end, :ticks_per_bang
11 |
12 | def initialize
13 | super({:input => ''})
14 | restart
15 | @ticks_per_bang = DURATION['sixtyfourth'].to_f
16 | end
17 |
18 | def sequence(cosy_syntax)
19 | begin
20 | parse cosy_syntax
21 | restart
22 | return true
23 | rescue Exception
24 | error "#{OBJECT_NAME}: #{$!}"
25 | return false
26 | end
27 | end
28 |
29 | def define(name, cosy_syntax)
30 | begin
31 | define_sequence(name, cosy_syntax)
32 | return true
33 | rescue Exception
34 | error "#{OBJECT_NAME}: #{$!}"
35 | return false
36 | end
37 | end
38 |
39 | def play(name)
40 | begin
41 | load_sequence(name)
42 | restart
43 | rescue Exception
44 | error "#{OBJECT_NAME}: #{$!}"
45 | end
46 | end
47 |
48 | def restart
49 | init
50 | @sequencer.restart
51 | @time_to_next = 1
52 | @prev_duration = DURATION['sixtyfourth']
53 | @end = false
54 | @suppress_rebang = @rebang = false
55 | end
56 |
57 | # like restart but with inifinite loop prevention
58 | # should be used whenever automatically restarting at the end of a sequence
59 | # TODO: maybe loops are better handled in here
60 | def autorestart
61 | rebang = @rebang
62 | restart
63 | @suppress_rebang = rebang
64 | end
65 |
66 | def ticks_to_bangs(ticks)
67 | ticks / @ticks_per_bang if ticks
68 | end
69 |
70 | def bang
71 | if not @end
72 | @time_to_next -= 1
73 | if @time_to_next <= 0
74 | event = next_event
75 |
76 | if not event
77 | @end = true
78 | out4 'bang'
79 |
80 | elsif event.is_a? NoteEvent
81 | pitches, velocity, duration = event.pitches, event.velocity, ticks_to_bangs(event.duration)
82 | if duration >= 0
83 | # output in standard Max right-to-left order:
84 | out2 duration
85 | out1 velocity
86 | out0 pitches
87 |
88 | if duration == 0
89 | # prevent infinite loops
90 | if not @suppress_rebang
91 | @rebang = true
92 | bang
93 | else
94 | @suppress_rebang = false
95 | end
96 | else
97 | @suppress_rebang = @rebang = false
98 | end
99 |
100 | end
101 | @time_to_next = duration.abs
102 |
103 | else
104 | if event.is_a? Chain
105 | # this is kind of nasty, but I don't want to try to flatten
106 | # things in this way in AbstractRenderer#next_event because
107 | # other renderers may need to interpret Labels or
108 | # do handle chords of non-pitches differently
109 | raw = event
110 | event = []
111 | raw.each do |e|
112 | case e
113 | when Label then event.insert(0, e.value)
114 | when Value then event << e.value
115 | when Chord then event += e
116 | else event << e
117 | end
118 | end
119 | end
120 | out3 event
121 | end
122 |
123 | end
124 | end
125 | end
126 |
127 | end
128 | end
129 |
130 | RENDERER = Cosy::MaxRenderer.new
131 |
132 | ################################################
133 | # The interface for Max (the supported messages)
134 |
135 | def sequence(input)
136 | out5 RENDERER.sequence(input)
137 | end
138 |
139 | def define(name, input)
140 | out5 RENDERER.define(name, input)
141 | end
142 |
143 | def play(name)
144 | RENDERER.play(name)
145 | end
146 |
147 | def restart
148 | RENDERER.restart
149 | end
150 |
151 | def autorestart
152 | RENDERER.autorestart
153 | end
154 |
155 | def bang
156 | RENDERER.bang
157 | end
158 |
159 | inlet_assist 'sequence/define/play'
160 | outlet_assist 'pitch','velocity','duration','other','bang when done','parsed successfully?'
161 |
--------------------------------------------------------------------------------
/ajm/ruby/ajm_cosy2coll.rb:
--------------------------------------------------------------------------------
1 | $DEBUG=true
2 | require 'cosy'
3 | include Cosy
4 |
5 | OBJECT_NAME = 'ajm.cosy'
6 | TICKS_PER_QUARTER_NOTE = 480
7 |
8 | def render(input)
9 | Cosy::CollRenderer.new({:input => input}).render
10 | rescue Exception => e
11 | puts e
12 | end
13 |
14 | inlet_assist 'cosy sequence'
15 | outlet_assist 'to midi coll','other messages','bang when done','parsed successfully?'
16 |
17 | module Cosy
18 |
19 | class CollRenderer < AbstractRenderer
20 |
21 | def initialize(options={})
22 | super
23 | @ticks_per_beat = (4.0/options.fetch(:beat_unit, 4) * TICKS_PER_QUARTER_NOTE).to_i
24 | @beats_per_bar = options.fetch(:beats_per_bar, 4)
25 | @quantize_in_ticks = options[:quantize]
26 | end
27 |
28 | def render()
29 | out0 'clear'
30 | @timeline.times.each do |time|
31 | events = @timeline[time]
32 | start_coll_entry(time)
33 | events.each do |event|
34 | case event
35 | when Event::Note
36 | note(event.pitch, event.velocity, event.duration)
37 | end
38 | end
39 | end_coll_entry
40 | end
41 | out2 'bang'
42 | end
43 |
44 |
45 | #################
46 | protected
47 |
48 | def time
49 | @time
50 | end
51 |
52 |
53 | #################
54 | private
55 |
56 | def start_coll_entry(time)
57 | @coll_entry = ['store', time] # the index for [coll]
58 | # we could optionally do ticks to bbu here
59 | end
60 |
61 | def end_coll_entry
62 | puts @coll_entry.inspect
63 | out0 @coll_entry
64 | @coll_entry = nil
65 | end
66 |
67 | def note(pitch, velocity, duration)
68 | duration_in_beats = duration.to_f/@ticks_per_beat
69 | duration_in_beats = (duration_in_beats*1000).round / 1000.0 # limit digits for readability
70 | @coll_entry << "#{pitch} #{velocity} #{duration_in_beats}"
71 | end
72 |
73 | def ticks_to_bbu(ticks)
74 | ticks = ticks.to_i
75 | if @quantize
76 | diff = ticks % @quantize
77 | ticks -= diff
78 | if diff >= @quantize / 2
79 | ticks += @quantize
80 | end
81 | end
82 | units = ticks % @ticks_per_beat
83 | beats = ticks / @ticks_per_beat # total beat offset
84 | bars = beats / @beats_per_bar + 1
85 | beats = beats % @beats_per_bar + 1 # beats relative to start of measure
86 | return "#{bars}.#{beats}.#{units}"
87 | end
88 |
89 | end
90 |
91 | end
92 |
93 |
--------------------------------------------------------------------------------
/ajm/ruby/ajm_midi2coll.rb:
--------------------------------------------------------------------------------
1 | require 'midilib/sequence'
2 |
3 | VALID_BEAT_UNITS = [1,2,4,8,16]
4 |
5 | inlet_assist 'read/timesig/quantize'
6 | outlet_assist 'note data in coll format', 'track index', 'number of tracks'
7 |
8 | def timesig(beats_per_bar, beat_unit)
9 | beats_per_bar = beats_per_bar.to_i
10 | beat_unit = beat_unit.to_i
11 | valid = true
12 | if beats_per_bar < 1 then
13 | error "Invalid beats per bar '#{beats_per_bar}'. Must be a positive number."
14 | valid = false
15 | end
16 | if not VALID_BEAT_UNITS.include? beat_unit then
17 | error "Invalid beat unit '#{beat_unit}'. " +
18 | "Beat unit must be one of the following: #{VALID_BEAT_UNITS.inspect}"
19 | valid = false
20 | end
21 | if valid
22 | set_local(:beats_per_bar, beats_per_bar)
23 | set_local(:beat_unit, beat_unit)
24 | end
25 | end
26 |
27 | def quantize(ticks)
28 | set_local(:quantize, ticks)
29 | end
30 |
31 | def ccfilter(state=nil)
32 | state = false if not state or state == 0
33 | set_local(:cc_filter, state)
34 | end
35 |
36 | def read(midi_file)
37 | midi_file.strip!
38 | if midi_file =~ /^[^:]{2,}:(.*)/ then midi_file = $1 end # Fixes cross platform issues with drive letters (strip off drive name on OS X)
39 | if File.exists?(midi_file) then
40 | # Settings are stored as local variables to this instance,
41 | # because I am using a shared context for this object to improve performance
42 | # (if not used shared context, these could be globals)
43 | beats_per_bar = get_local(:beats_per_bar) || 4
44 | beat_unit = get_local(:beat_unit) || 4
45 | quantize = get_local(:quantize)
46 | cc_filter = get_local(:cc_filter)
47 | midi2coll = Midi2Coll.new(midi_file, beats_per_bar, beat_unit, quantize, cc_filter)
48 |
49 | num_tracks = midi2coll.number_of_tracks
50 | if num_tracks > 1
51 | has_metadata_track = true
52 | out2 num_tracks-1 # ignore metadata track
53 | else
54 | has_metadata_track = false
55 | out2 1
56 | end
57 |
58 | midi2coll.coll_entries.each_with_index do |entries_for_track, track_index|
59 | next if has_metadata_track and track_index == 0
60 | out1 track_index
61 | out0 'clear'
62 | entries_for_track.each do |note_data|
63 | out0 note_data
64 | end
65 | end
66 | else
67 | error "Cannot find file: #{midi_file}"
68 | end
69 | end
70 |
71 |
72 | class Midi2Coll
73 |
74 | def initialize(midi_file, beats_per_bar=4, beat_unit=4, quantize_in_ticks=nil, cc_filter=false)
75 | @midi_file = midi_file # TODO validate
76 | @track_tick_maps = []
77 | @beats_per_bar = beats_per_bar.to_i
78 | @quantize = quantize_in_ticks
79 | @cc_filter = cc_filter
80 |
81 | # use midilib to parse the input file
82 | @sequence = MIDI::Sequence.new()
83 | File.open(@midi_file, 'rb') do |file|
84 | @sequence.read(file)
85 | end
86 | @ticks_per_beat = @sequence.ppqn.to_i
87 |
88 | @sequence.tracks.each_with_index do |track,index|
89 | @track_tick_maps[index] = @tick_map = {}
90 | @pitch_map = {}
91 | track.each do |event|
92 | case event
93 | when MIDI::NoteOnEvent
94 | start_note(event)
95 | when MIDI::NoteOffEvent
96 | end_note(event)
97 | when MIDI::Controller
98 | simple_event(event) if not @cc_filter
99 | end
100 | # TODO: support other event types? Pitch Bends? Program changes? Time Signature changes?
101 | end
102 | end
103 | end
104 |
105 | def number_of_tracks
106 | @sequence.tracks.length
107 | end
108 |
109 | def coll_entries
110 | coll_entries = []
111 | @track_tick_maps.each do |tick_map|
112 | coll_entries_for_track = []
113 | onset_times = tick_map.keys.sort
114 | onset_times.each do |onset|
115 | events = tick_map[onset]
116 | bbu = ticks_to_bbu(onset) # onset is in ticks
117 | coll_entry = ['store', bbu] # the index for [coll]
118 |
119 | events.each do |event|
120 | case event
121 | when Array
122 | on, off = event
123 | pitch = on.note
124 | velocity = on.velocity
125 | offset = off.time_from_start # in ticks
126 | duration = ticks_to_beats(offset - onset)
127 | coll_entry << "#{pitch} #{velocity} #{duration}"
128 |
129 | when MIDI::Controller
130 | ccval = event.value
131 | ccnum = event.controller
132 | coll_entry << "cc #{ccval} #{ccnum}"
133 |
134 | end
135 | end
136 | coll_entries_for_track << coll_entry
137 | end
138 | coll_entries << coll_entries_for_track
139 | end
140 | return coll_entries
141 | end
142 |
143 | #############################
144 | private
145 |
146 | def start_note(on_event)
147 | pitch = on_event.note
148 | @pitch_map[pitch] = on_event
149 | end
150 |
151 | def end_note(off_event)
152 | pitch = off_event.note
153 | on_event = @pitch_map[pitch]
154 | if on_event then
155 | note = [on_event, off_event]
156 | add_event(note, on_event.time_from_start)
157 | else
158 | # This will happen if multiple note ons occur at the
159 | # same pitch before the first note off.
160 | # TODO: handle this case by making pitch_map smarter
161 | error "Warning: unmatched note off for pitch #{pitch} at #{ticks_to_bbu(off_time)}"
162 | end
163 | end
164 |
165 | def simple_event(event)
166 | add_event(event, event.time_from_start)
167 | end
168 |
169 | def add_event(event, time_in_ticks)
170 | time_in_ticks = quantize(time_in_ticks)
171 | # Map time to list of events occuring at that time:
172 | events = @tick_map[time_in_ticks]
173 | if events then
174 | events << event
175 | else
176 | @tick_map[time_in_ticks] = [event]
177 | end
178 | end
179 |
180 | def ticks_to_bbu(ticks)
181 | ticks = ticks.to_i
182 | units = ticks % @ticks_per_beat
183 | beats = ticks / @ticks_per_beat # total beat offset
184 | bars = beats / @beats_per_bar + 1
185 | beats = beats % @beats_per_bar + 1 # beats relative to start of measure
186 | return "#{bars}.#{beats}.#{units}"
187 | end
188 |
189 | def ticks_to_beats(ticks, sig_digits_multiplier=1000.0)
190 | beats = ticks/480.0
191 | # limit digits for readability:
192 | beats = (beats*sig_digits_multiplier).round / sig_digits_multiplier if sig_digits_multiplier
193 | return beats
194 | end
195 |
196 | def quantize(ticks)
197 | if @quantize
198 | diff = ticks % @quantize
199 | ticks -= diff
200 | if diff >= @quantize / 2
201 | ticks += @quantize
202 | end
203 | end
204 | return ticks
205 | end
206 |
207 | end
208 |
209 |
210 |
--------------------------------------------------------------------------------
/ajm/ruby/ajm_ruby_initialize.rb:
--------------------------------------------------------------------------------
1 | require 'java'
2 |
3 | def inlet_assist(*params)
4 | $max_object.setInletAssist params.to_java(:string)
5 | end
6 |
7 | def outlet_assist(*params)
8 | $max_object.setOutletAssist params.to_java(:string)
9 | end
10 |
11 | def atom(obj=nil)
12 | if obj
13 | $max_ruby_adapter.toAtoms(obj)
14 | else
15 | com.cycling74.max.Atom.emptyArray
16 | end
17 | end
18 |
19 | # Placeholders for Max hooks:
20 | def bang
21 | 'bang'
22 | end
23 |
24 | def list(*array)
25 | array
26 | end
27 |
28 | def yield_atoms(*params,&block)
29 | params.each do |param|
30 | begin
31 | atoms_or_atom = $max_ruby_adapter.toAtoms(param)
32 | rescue # this compensates for http://jira.codehaus.org/browse/JRUBY-4998
33 | atoms_or_atom = param.to_s
34 | end
35 | if atoms_or_atom.respond_to? :each
36 | atoms_or_atom.each{|atom| yield atom}
37 | else
38 | yield atoms_or_atom
39 | end
40 | end
41 | end
42 |
43 | def puts(*params)
44 | yield_atoms(*params) {|atom| java.lang.System.out.println(atom)}
45 | nil
46 | end
47 |
48 | def print(*params)
49 | yield_atoms(*params) {|atom| java.lang.System.out.print(atom)}
50 | nil
51 | end
52 |
53 | def error(*params)
54 | yield_atoms(*params) {|atom| java.lang.System.err.println(atom)}
55 | nil
56 | end
57 |
58 | def flush
59 | java.lang.System.out.println
60 | nil
61 | end
62 |
63 | def outlet(outlet_index, *params)
64 | if (outlet_index >= $max_object.numOutlets)
65 | error("Invalid outlet index #{outletIdx}")
66 | else
67 | begin
68 | if params.length == 1
69 | # avoid unnecessary nested arrays for things like "outlet 0, [1,2]"
70 | atoms = $max_ruby_adapter.toAtoms(params[0])
71 | else
72 | atoms = $max_ruby_adapter.toAtoms(params)
73 | end
74 | rescue # this compensates for http://jira.codehaus.org/browse/JRUBY-4998
75 | if params.length == 1
76 | atoms = $max_ruby_adapter.toAtoms(params[0].to_s)
77 | else
78 | atoms = $max_ruby_adapter.toAtoms( params.map{|p| p.to_s} )
79 | end
80 | end
81 | $max_object.outlet(outlet_index, atoms)
82 | end
83 | nil
84 | end
85 |
86 | def inlet(inlet_index, *params)
87 | params
88 | end
89 |
90 | def inlet_index
91 | $max_object.getInlet
92 | end
93 |
94 | module Kernel
95 | # define_method must be called inside a module or class
96 | (0..9).each do |index|
97 | define_method "out#{index}" do |*params|
98 | outlet(index, *params)
99 | end
100 | define_method "in#{index}" do |*params|
101 | inlet(index, *params)
102 | end
103 | end
104 | end
105 |
106 | def max_object(namespace=nil)
107 | return $max_object if not namespace
108 | names = namespace.split '.'
109 | if(names.length > 1)
110 | context = names[0]
111 | id = names[1]
112 | else
113 | context = $max_object.context
114 | id = names[0]
115 | end
116 | $max_object_map[context][id]
117 | end
118 |
119 | # deprecated, use at_exit
120 | def on_context_destroyed(callback_script)
121 | $max_ruby_adapter.on_context_destroyed(callback_script)
122 | end
123 |
124 | # for use with shared contexts:
125 | LOCAL_STORAGE = {}
126 | def set_local(name,obj)
127 | storage = LOCAL_STORAGE[$max_object]
128 | LOCAL_STORAGE[$max_object] = storage = {} if not storage
129 | storage[name] = obj
130 | end
131 | alias setLocal set_local # for backward compatibility
132 | def get_local(name)
133 | storage = LOCAL_STORAGE[$max_object]
134 | storage[name] if storage
135 | end
136 | alias getLocal get_local # for backward compatibility
137 | def delete_local(name)
138 | storage = LOCAL_STORAGE[$max_object]
139 | storage.delete(name) if storage
140 | end
141 | def has_local?(name)
142 | storage = LOCAL_STORAGE[$max_object]
143 | if storage
144 | storage.has_key?(name)
145 | else
146 | false
147 | end
148 | end
149 |
150 | def set_global(name,obj)
151 | $global_variable_store.set(name.to_s, obj) if name
152 | return obj
153 | end
154 | def get_global(name)
155 | $global_variable_store.get(name.to_s) if name
156 | end
157 | def delete_global(name)
158 | $global_variable_store.delete(name.to_s) if name
159 | end
160 | def has_global?(name)
161 | $global_variable_store.defined(name.to_s) if name
162 | end
163 |
164 |
--------------------------------------------------------------------------------
/ajm/ruby/ajmfl_cosy2clip.rb:
--------------------------------------------------------------------------------
1 | $DEBUG=true
2 | require 'cosy'
3 | include Cosy
4 |
5 | OBJECT_NAME = 'ajm.cosy2clip'
6 | TICKS_PER_QUARTER_NOTE = 480
7 |
8 | def render(input)
9 | Cosy::ClipRenderer.new({:input => input}).render
10 | rescue Exception => e
11 | puts e
12 | end
13 |
14 | inlet_assist 'cosy sequence'
15 | outlet_assist 'to live.object','other messages','bang when done','parsed successfully?'
16 |
17 | module Cosy
18 |
19 | class ClipRenderer < AbstractRenderer
20 |
21 | def initialize(options={})
22 | super
23 |
24 | @clip_duration = 0.0
25 | @notes = []
26 | @timeline.each_event do |time,event|
27 | case event
28 | when Event::Note
29 | start_time = to_beats(time)
30 | duration = to_beats(event.duration)
31 | end_time = start_time + duration
32 | @clip_duration = end_time if end_time > @clip_duration
33 | @notes << [event.pitch, start_time, duration, event.velocity, 0]
34 | end
35 | end
36 | @clip_duration = @clip_duration.ceil.to_f
37 | end
38 |
39 | def render()
40 | call :select_all_notes
41 | call :replace_selected_notes
42 | call :notes, @notes.length
43 | @notes.each{|note| call :note, *note }
44 | call :done
45 | set :loop_end, @clip_duration
46 | end
47 |
48 | def call(command, *args)
49 | out0 :call, command, *args
50 | end
51 |
52 | def set(param, *args)
53 | out0 :set, param, *args
54 | end
55 |
56 | def to_beats(ticks)
57 | ticks / 480.0
58 | end
59 |
60 | end
61 |
62 | end
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_argv.rb:
--------------------------------------------------------------------------------
1 | return if ARGV[0] == :mute
2 | puts ' '
3 | puts 'These variables are set:'
4 | puts '$0 = ' + $0.inspect
5 | puts '__FILE__ = ' + __FILE__.inspect
6 | puts '$* = ' + $*.inspect
7 | puts 'ARGV = ' + ARGV.inspect
8 | puts "$LOAD_PATH << #{$LOAD_PATH.last.inspect} # path to the file"
9 |
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_autowatch.rb:
--------------------------------------------------------------------------------
1 | # out0 "uncomment me and save this file"
2 |
3 |
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_call_send.rb:
--------------------------------------------------------------------------------
1 | def my_method(arg0,arg1,arg2)
2 | "arg0=#{arg0}, arg1=#{arg1}, arg2=#{arg2}"
3 | end
4 |
5 | def another_method
6 | "another method's return value"
7 | end
8 |
9 | class MyClass
10 | def method(arg)
11 | %w{the class received value} << arg.inspect
12 | end
13 | end
14 |
15 | $object = MyClass.new
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_file_example.rb:
--------------------------------------------------------------------------------
1 | def custom_method
2 | out0 "Hello from a custom method defined in a file"
3 | end
4 |
5 | def bang
6 | # out0 is a shortcut for:
7 | outlet(0, 'Hello from a custom handler for bang')
8 | end
9 |
10 | def inlet(inlet_index, *args)
11 | out0 'check the Max window'
12 | puts "received a list from inlet #{inlet_index} with #{args.length} items: ", args
13 | end
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_inlet_symbols_to.rb:
--------------------------------------------------------------------------------
1 | def inlet(inlet_index, *params)
2 | out0 params[0].class.to_s+'s:', *params
3 | end
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_inlets.rb:
--------------------------------------------------------------------------------
1 | def in0(*params)
2 | out0 'sum', params.inject(:+)
3 | end
4 |
5 | # use of the splat (*params) is optionally
6 | # it's recommended because it makes the method more flexible
7 | def in2(param1, param2, param3, param4)
8 | params = [param1, param2, param3, param4]
9 | out0 'average', params.inject(:+).to_f/params.length
10 | end
11 |
12 | # when a convenience method (in1 in this case) is not
13 | # defined, it will call the inlet() method
14 | def inlet(inlet_index, *params)
15 | out0 'product', params.inject(:*)
16 | end
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_load_require.rb:
--------------------------------------------------------------------------------
1 | $LOAD_PATH.unshift File.dirname($0) # put this folder on the LOAD_PATH
2 |
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_scriptfile.rb:
--------------------------------------------------------------------------------
1 | out0 'Hello from ajm_scriptfile.rb'
2 |
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_variable_scope.rb:
--------------------------------------------------------------------------------
1 | #####################################################################
2 | # ajm.ruby has separate local variable scopes for each evalution
3 | # If you need to remember a value across separate Max messages, you
4 | # need to use constants, globals, or attributes.
5 | # Note that a constant should only be defined once, and will result
6 | # in an error if you try to assign a different value later.
7 |
8 | Var = 'constants are remembered'
9 | $var = 'globals are remembered'
10 | @var = 'attributes are remembered'
11 | var = 'forgotten'
12 |
13 | def some_method
14 | 'methods are visible'
15 | end
16 |
17 | out0 "local variables are #{var} after a script's evaluation"
18 |
--------------------------------------------------------------------------------
/ajm/ruby/help/ajm_version.rb:
--------------------------------------------------------------------------------
1 | out0 JRUBY_VERSION, RUBY_VERSION
--------------------------------------------------------------------------------
/ajm/ruby/midilib/info.rb:
--------------------------------------------------------------------------------
1 | module MIDI
2 |
3 | VERSION_MAJOR = 1
4 | VERSION_MINOR = 2
5 | VERSION_TWEAK = 1
6 | Version = "#{VERSION_MAJOR}.#{VERSION_MINOR}.#{VERSION_TWEAK}"
7 | Copyright = 'Copyright (c) 2003-2009 by Jim Menard '
8 |
9 | end
10 |
--------------------------------------------------------------------------------
/ajm/ruby/midilib/io/seqreader.rb:
--------------------------------------------------------------------------------
1 | require 'midilib/io/midifile'
2 | require 'midilib/track'
3 | require 'midilib/event'
4 |
5 | module MIDI
6 |
7 | module IO
8 |
9 | # Reads MIDI files. As a subclass of MIDIFile, this class implements
10 | # the callback methods for each MIDI event and use them to build Track
11 | # and Event objects and give the tracks to a Sequence.
12 | #
13 | # We append new events to the end of a track's event list, bypassing a call
14 | # to Track.#add. This means that we must call Track.recalc_times at the end
15 | # of the track so it can update each event with its time from the track's
16 | # start (see end_track below).
17 | #
18 | # META_TRACK_END events are not added to tracks. This way, we don't have to
19 | # worry about making sure the last event is always a track end event. We
20 | # rely on the SeqWriter to append a META_TRACK_END event to each track when
21 | # it is output.
22 |
23 | class SeqReader < MIDIFile
24 |
25 | # The optional proc block is called once at the start of the file
26 | # and again at the end of each track. There are three arguments
27 | # to the block: the track, the track number (1 through _n_), and
28 | # the total number of tracks.
29 | def initialize(seq, proc = nil) # :yields: track, num_tracks, index
30 | super()
31 | @seq = seq
32 | @track = nil
33 | @chan_mask = 0
34 | @update_block = block_given?() ? Proc.new() : proc
35 | end
36 |
37 | def header(format, ntrks, division)
38 | @seq.format = format
39 | @seq.ppqn = division
40 |
41 | @ntrks = ntrks
42 | @update_block.call(nil, @ntrks, 0) if @update_block
43 | end
44 |
45 | def start_track()
46 | @track = Track.new(@seq)
47 | @seq.tracks << @track
48 |
49 | @pending = []
50 | end
51 |
52 | def end_track()
53 | # Turn off any pending note on messages
54 | @pending.each { | on | make_note_off(on, 64) }
55 | @pending = nil
56 |
57 | # Don't bother adding the META_TRACK_END event to the track.
58 | # This way, we don't have to worry about making sure the
59 | # last event is always a track end event.
60 |
61 | # Let the track calculate event times from start of track. This is
62 | # in lieu of calling Track.add for each event.
63 | @track.recalc_times()
64 |
65 | # Store bitmask of all channels used into track
66 | @track.channels_used = @chan_mask
67 |
68 | # call update block
69 | @update_block.call(@track, @ntrks, @seq.tracks.length) if @update_block
70 | end
71 |
72 | def note_on(chan, note, vel)
73 | if vel == 0
74 | note_off(chan, note, 64)
75 | return
76 | end
77 |
78 | on = NoteOnEvent.new(chan, note, vel, @curr_ticks)
79 | @track.events << on
80 | @pending << on
81 | track_uses_channel(chan)
82 | end
83 |
84 | def note_off(chan, note, vel)
85 | # Find note on, create note off, connect the two, and remove
86 | # note on from pending list.
87 | @pending.each_with_index { | on, i |
88 | if on.note == note && on.channel == chan
89 | make_note_off(on, vel)
90 | @pending.delete_at(i)
91 | return
92 | end
93 | }
94 | $stderr.puts "note off with no earlier note on (ch #{chan}, note" +
95 | " #{note}, vel #{vel})" if $DEBUG
96 | end
97 |
98 | def make_note_off(on, vel)
99 | off = NoteOffEvent.new(on.channel, on.note, vel, @curr_ticks)
100 | @track.events << off
101 | on.off = off
102 | off.on = on
103 | end
104 |
105 | def pressure(chan, note, press)
106 | @track.events << PolyPressure.new(chan, note, press, @curr_ticks)
107 | track_uses_channel(chan)
108 | end
109 |
110 | def controller(chan, control, value)
111 | @track.events << Controller.new(chan, control, value, @curr_ticks)
112 | track_uses_channel(chan)
113 | end
114 |
115 | def pitch_bend(chan, msb, lsb)
116 | @track.events << PitchBend.new(chan, (msb << 7) + lsb, @curr_ticks)
117 | track_uses_channel(chan)
118 | end
119 |
120 | def program(chan, program)
121 | @track.events << ProgramChange.new(chan, program, @curr_ticks)
122 | track_uses_channel(chan)
123 | end
124 |
125 | def chan_pressure(chan, press)
126 | @track.events << ChannelPressure.new(chan, press, @curr_ticks)
127 | track_uses_channel(chan)
128 | end
129 |
130 | def sysex(msg)
131 | @track.events << SystemExclusive.new(msg, @curr_ticks)
132 | end
133 |
134 | def meta_misc(type, msg)
135 | @track.events << MetaEvent.new(type, msg, @curr_ticks)
136 | end
137 |
138 | # --
139 | # def sequencer_specific(type, msg)
140 | # end
141 |
142 | # def sequence_number(num)
143 | # end
144 | # ++
145 |
146 | def text(type, msg)
147 | case type
148 | when META_TEXT, META_LYRIC, META_CUE
149 | @track.events << MetaEvent.new(type, msg, @curr_ticks)
150 | when META_SEQ_NAME, META_COPYRIGHT
151 | @track.events << MetaEvent.new(type, msg, 0)
152 | when META_INSTRUMENT
153 | @track.instrument = msg
154 | when META_MARKER
155 | @track.events << Marker.new(msg, @curr_ticks)
156 | else
157 | $stderr.puts "text = #{msg}, type = #{type}" if $DEBUG
158 | end
159 | end
160 |
161 | # --
162 | # Don't bother adding the META_TRACK_END event to the track. This way,
163 | # we don't have to worry about always making sure the last event is
164 | # always a track end event. We just have to make sure to write one when
165 | # the track is output back to a file.
166 | # def eot()
167 | # @track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks)
168 | # end
169 | # ++
170 |
171 | def time_signature(numer, denom, clocks, qnotes)
172 | @seq.time_signature(numer, denom, clocks, qnotes)
173 | @track.events << TimeSig.new(numer, denom, clocks, qnotes, @curr_ticks)
174 | end
175 |
176 | # --
177 | # def smpte(hour, min, sec, frame, fract)
178 | # end
179 | # ++
180 |
181 | def tempo(microsecs)
182 | @track.events << Tempo.new(microsecs, @curr_ticks)
183 | end
184 |
185 | def key_signature(sharpflat, is_minor)
186 | @track.events << KeySig.new(sharpflat, is_minor, @curr_ticks)
187 | end
188 |
189 | # --
190 | # def arbitrary(msg)
191 | # end
192 | # ++
193 |
194 | # Return true if the current track uses the specified channel.
195 | def track_uses_channel(chan)
196 | @chan_mask = @chan_mask | (1 << chan)
197 | end
198 |
199 | end
200 |
201 | end
202 | end
203 |
--------------------------------------------------------------------------------
/ajm/ruby/midilib/io/seqwriter.rb:
--------------------------------------------------------------------------------
1 | # Writes MIDI files.
2 |
3 | require 'midilib/event'
4 | require 'midilib/utils'
5 |
6 | module MIDI
7 |
8 | module IO
9 |
10 | class SeqWriter
11 |
12 | def initialize(seq, proc = nil) # :yields: num_tracks, index
13 | @seq = seq
14 | @update_block = block_given?() ? Proc.new() : proc
15 | end
16 |
17 | # Writes a MIDI format 1 file.
18 | def write_to(io)
19 | @io = io
20 | @bytes_written = 0
21 | write_header()
22 | @update_block.call(nil, @seq.tracks.length, 0) if @update_block
23 | @seq.tracks.each_with_index { | track, i |
24 | write_track(track)
25 | @update_block.call(track, @seq.tracks.length, i) if @update_block
26 | }
27 | end
28 |
29 | def write_header
30 | @io.print 'MThd'
31 | write32(6)
32 | write16(1) # Ignore sequence format; write as format 1
33 | write16(@seq.tracks.length)
34 | write16(@seq.ppqn)
35 | end
36 |
37 | def write_track(track)
38 | @io.print 'MTrk'
39 | track_size_file_pos = @io.tell()
40 | write32(0) # Dummy byte count; overwritten later
41 | @bytes_written = 0 # Reset after previous write
42 |
43 | write_instrument(track.instrument)
44 |
45 | prev_event = nil
46 | prev_status = 0
47 | track.events.each { | event |
48 | if !event.kind_of?(Realtime)
49 | write_var_len(event.delta_time)
50 | end
51 |
52 | data = event.data_as_bytes()
53 | status = data[0] # status byte plus channel number, if any
54 |
55 | # running status byte
56 | status = possibly_munge_due_to_running_status_byte(data,
57 | prev_status)
58 |
59 | @bytes_written += write_bytes(data)
60 |
61 | prev_event = event
62 | prev_status = status
63 | }
64 |
65 | # Write track end event.
66 | event = MetaEvent.new(META_TRACK_END)
67 | write_var_len(0)
68 | @bytes_written += write_bytes(event.data_as_bytes())
69 |
70 | # Go back to beginning of track data and write number of bytes,
71 | # then come back here to end of file.
72 | @io.seek(track_size_file_pos)
73 | write32(@bytes_written)
74 | @io.seek(0, ::IO::SEEK_END)
75 | end
76 |
77 | # If we can use a running status byte, delete the status byte from
78 | # the given data. Return the status to remember for next time as the
79 | # running status byte for this event.
80 | def possibly_munge_due_to_running_status_byte(data, prev_status)
81 | status = data[0]
82 | return status if status >= 0xf0 || prev_status >= 0xf0
83 |
84 | chan = (status & 0x0f)
85 | return status if chan != (prev_status & 0x0f)
86 |
87 | status = (status & 0xf0)
88 | prev_status = (prev_status & 0xf0)
89 |
90 | # Both events are on the same channel. If the two status bytes are
91 | # exactly the same, the rest is trivial. If it's note on/note off,
92 | # we can combine those further.
93 | if status == prev_status
94 | data[0,1] = nil # delete status byte from data
95 | return status + chan
96 | elsif status == NOTE_OFF && data[2] == 64
97 | # If we see a note off and the velocity is 64, we can store
98 | # a note on with a velocity of 0. If the velocity isn't 64
99 | # then storing a note on would be bad because the would be
100 | # changed to 64 when reading the file back in.
101 | data[2] = 0 # set vel to 0; do before possible shrinking
102 | status = NOTE_ON + chan
103 | if prev_status == NOTE_ON
104 | data[0,1] = [] # delete status byte
105 | else
106 | data[0] = status
107 | end
108 | return status
109 | else
110 | # Can't compress data
111 | return status + chan
112 | end
113 | end
114 |
115 | def write_instrument(instrument)
116 | event = MetaEvent.new(META_INSTRUMENT, instrument)
117 | write_var_len(0)
118 | data = event.data_as_bytes()
119 | @bytes_written += write_bytes(data)
120 | end
121 |
122 | def write_var_len(val)
123 | buffer = Utils.as_var_len(val)
124 | @bytes_written += write_bytes(buffer)
125 | end
126 |
127 | def write16(val)
128 | val = (-val | 0x8000) if val < 0
129 |
130 | buffer = []
131 | @io.putc((val >> 8) & 0xff)
132 | @io.putc(val & 0xff)
133 | @bytes_written += 2
134 | end
135 |
136 | def write32(val)
137 | val = (-val | 0x80000000) if val < 0
138 |
139 | @io.putc((val >> 24) & 0xff)
140 | @io.putc((val >> 16) & 0xff)
141 | @io.putc((val >> 8) & 0xff)
142 | @io.putc(val & 0xff)
143 | @bytes_written += 4
144 | end
145 |
146 | def write_bytes(bytes)
147 | bytes.each { |b| @io.putc(b) }
148 | bytes.length
149 | end
150 | end
151 |
152 | end
153 | end
154 |
--------------------------------------------------------------------------------
/ajm/ruby/midilib/measure.rb:
--------------------------------------------------------------------------------
1 | require 'midilib/consts'
2 |
3 | module MIDI
4 |
5 | # The Measure class contains information about a measure from the sequence.
6 | # The measure data is based on the time signature information from the sequence
7 | # and is not stored in the sequence itself
8 | class Measure
9 | # The numerator (top digit) for the measure's time signature
10 | attr_reader :numerator
11 | # The denominator for the measure's time signature
12 | attr_reader :denominator
13 | # Start clock tick for the measure
14 | attr_reader :start
15 | # End clock tick for the measure (inclusive)
16 | attr_reader :end
17 | # The measure number (1-based)
18 | attr_reader :measure_number
19 | # The metronome tick for the measure
20 | attr_reader :metronome_ticks
21 |
22 | # Constructor
23 | def initialize(meas_no, start_time, duration, numer, denom, met_ticks)
24 | @measure_number = meas_no
25 | @start = start_time
26 | @end = start_time + duration - 1
27 | @numerator = numer
28 | @denominator = denom
29 | @metronome_ticks = met_ticks
30 | end
31 |
32 | # Returns a detailed string with information about the measure
33 | def to_s
34 | t = "#{@numerator}/#{2**@denominator}"
35 | m = @metronome_ticks.to_f / 24
36 | "measure #{@measure_number} #{@start}-#{@end} #{t} #{m} qs metronome"
37 | end
38 |
39 | # Returns +true+ if the event is in the measure
40 | def contains_event?(e)
41 | (e.time_from_start >= @start) && (e.time_from_start <= @end)
42 | end
43 | end
44 |
45 | # A specialized container for MIDI::Measure objects, which can be use to map
46 | # event times to measure numbers. Please note that this object has to be remade
47 | # when events are deleted/added in the sequence.
48 | class Measures < Array
49 | # The highest event time in the sequence (at the time when the
50 | # object was created)
51 | attr_reader :max_time
52 |
53 | # The ppqd from the sequence
54 | attr_reader :ppqd
55 |
56 | # Constructor
57 | def initialize(max_time, ppqd)
58 | super(0)
59 | @max_time = max_time
60 | @ppqd = ppqd
61 | end
62 |
63 | # Returns the MIDI::Measure object where the event is located.
64 | # Returns +nil+ if the event isn't found in the container (should
65 | # never happen if the MIDI::Measures object is up to date).
66 | def measure_for_event(e)
67 | detect { | m | m.contains_event?(e) }
68 | end
69 |
70 | # Returns the event's time as a formatted MBT string (Measure:Beat:Ticks)
71 | # as found in MIDI sequencers.
72 | def to_mbt(e)
73 | m = measure_for_event(e)
74 | b = (e.time_from_start.to_f - m.start.to_f) / @ppqd
75 | b *= 24 / m.metronome_ticks
76 | sprintf("%d:%02d:%03d", m.measure_number, b.to_i + 1, (b - b.to_i) * @ppqd)
77 | end
78 | end
79 |
80 | end
81 |
--------------------------------------------------------------------------------
/ajm/ruby/midilib/sequence.rb:
--------------------------------------------------------------------------------
1 | require 'midilib/io/seqreader'
2 | require 'midilib/io/seqwriter'
3 | require 'midilib/measure.rb'
4 |
5 | module MIDI
6 |
7 | # A MIDI::Sequence contains MIDI::Track objects.
8 | class Sequence
9 |
10 | include Enumerable
11 |
12 | UNNAMED = 'Unnamed Sequence'
13 | DEFAULT_TEMPO = 120
14 |
15 | NOTE_TO_LENGTH = {
16 | 'whole' => 4.0,
17 | 'half' => 2.0,
18 | 'quarter' => 1.0,
19 | 'eighth' => 0.5,
20 | '8th' => 0.5,
21 | 'sixteenth' => 0.25,
22 | '16th' => 0.25,
23 | 'thirty second' => 0.125,
24 | 'thirtysecond' => 0.125,
25 | '32nd' => 0.125,
26 | 'sixty fourth' => 0.0625,
27 | 'sixtyfourth' => 0.0625,
28 | '64th' => 0.0625
29 | }
30 |
31 | # Array with all tracks for the sequence
32 | attr_accessor :tracks
33 | # Pulses (i.e. clocks) Per Quarter Note resolution for the sequence
34 | attr_accessor :ppqn
35 | # The MIDI file format (0, 1, or 2)
36 | attr_accessor :format
37 | attr_accessor :numer, :denom, :clocks, :qnotes
38 | # The class to use for reading MIDI from a stream. The default is
39 | # MIDI::IO::SeqReader. You can change this at any time.
40 | attr_accessor :reader_class
41 | # The class to use for writeing MIDI from a stream. The default is
42 | # MIDI::IO::SeqWriter. You can change this at any time.
43 | attr_accessor :writer_class
44 |
45 | def initialize
46 | @tracks = Array.new()
47 | @ppqn = 480
48 |
49 | # Time signature
50 | @numer = 4 # Numer + denom = 4/4 time default
51 | @denom = 2
52 | @clocks = 24 # Bug fix Nov 11, 2007 - this is not the same as ppqn!
53 | @qnotes = 8
54 |
55 | @reader_class = IO::SeqReader
56 | @writer_class = IO::SeqWriter
57 | end
58 |
59 | # Sets the time signature.
60 | def time_signature(numer, denom, clocks, qnotes)
61 | @numer = numer
62 | @denom = denom
63 | @clocks = clocks
64 | @qnotes = qnotes
65 | end
66 |
67 | # Returns the song tempo in beats per minute.
68 | def beats_per_minute
69 | return DEFAULT_TEMPO if @tracks.nil? || @tracks.empty?
70 | event = @tracks.first.events.detect { | e |
71 | e.kind_of?(MIDI::Tempo)
72 | }
73 | return event ? (Tempo.mpq_to_bpm(event.tempo)) : DEFAULT_TEMPO
74 | end
75 | alias_method :bpm, :beats_per_minute
76 | alias_method :tempo, :beats_per_minute
77 |
78 | # Given a note length name like "whole", "dotted quarter", or "8th
79 | # triplet", return the length of that note in quarter notes as a delta
80 | # time.
81 | def note_to_delta(name)
82 | return length_to_delta(note_to_length(name))
83 | end
84 |
85 | # Given a note length name like "whole", "dotted quarter", or "8th
86 | # triplet", return the length of that note in quarter notes as a
87 | # floating-point number, suitable for use as an argument to
88 | # length_to_delta.
89 | #
90 | # Legal names are any value in NOTE_TO_LENGTH, optionally prefixed by
91 | # "dotted_" and/or suffixed by "_triplet". So, for example,
92 | # "dotted_quarter_triplet" returns the length of a dotted quarter-note
93 | # triplet and "32nd" returns 1/32.
94 | def note_to_length(name)
95 | name.strip!
96 | name =~ /^(dotted)?(.*?)(triplet)?$/
97 | dotted, note_name, triplet = $1, $2, $3
98 | note_name.strip!
99 | mult = 1.0
100 | mult = 1.5 if dotted
101 | mult /= 3.0 if triplet
102 | len = NOTE_TO_LENGTH[note_name]
103 | raise "Sequence.note_to_length: \"#{note_name}\" not understood in \"#{name}\"" unless len
104 | return len * mult
105 | end
106 |
107 | # Translates +length+ (a multiple of a quarter note) into a delta time.
108 | # For example, 1 is a quarter note, 1.0/32.0 is a 32nd note, 1.5 is a
109 | # dotted quarter, etc. Be aware when using division; 1/32 is zero due to
110 | # integer mathematics and rounding. Use floating-point numbers like 1.0
111 | # and 32.0. This method always returns an integer.
112 | #
113 | # See also note_to_delta and note_to_length.
114 | def length_to_delta(length)
115 | return (@ppqn * length).to_i
116 | end
117 |
118 | # Returns the name of the first track (track zero). If there are no
119 | # tracks, returns UNNAMED.
120 | def name
121 | return UNNAMED if @tracks.empty?
122 | return @tracks.first.name()
123 | end
124 |
125 | # Hands the name to the first track. Does nothing if there are no tracks.
126 | def name=(name)
127 | return if @tracks.empty?
128 | @tracks.first.name = name
129 | end
130 |
131 | # Reads a MIDI stream.
132 | def read(io, proc = nil) # :yields: track, num_tracks, index
133 | reader = @reader_class.new(self, block_given?() ? Proc.new() : proc)
134 | reader.read_from(io)
135 | end
136 |
137 | # Writes to a MIDI stream.
138 | def write(io, proc = nil) # :yields: track, num_tracks, index
139 | writer = @writer_class.new(self, block_given?() ? Proc.new() : proc)
140 | writer.write_to(io)
141 | end
142 |
143 | # Iterates over the tracks.
144 | def each # :yields: track
145 | @tracks.each { | track | yield track }
146 | end
147 |
148 | # Returns a Measures object, which is an array container for all measures
149 | # in the sequence
150 | def get_measures
151 | # Collect time sig events and scan for last event time
152 | time_sigs = []
153 | max_pos = 0
154 | @tracks.each { |t|
155 | t.each { |e|
156 | time_sigs << e if e.timesig?
157 | max_pos = e.time_from_start if e.time_from_start > max_pos
158 | }
159 | }
160 | time_sigs.sort { |x,y| x.time_from_start <=> y.time_from_start }
161 |
162 | # Add a "fake" time sig event at the very last position of the sequence,
163 | # just to make sure the whole sequence is calculated.
164 | t = MIDI::TimeSig.new(4, 2, 24, 8, 0)
165 | t.time_from_start = max_pos
166 | time_sigs << t
167 |
168 | # Default to 4/4
169 | measure_length = @ppqn * 4
170 | oldnumer, olddenom, oldbeats = 4, 2, 24
171 |
172 | measures = MIDI::Measures.new(max_pos, @ppqn)
173 | curr_pos = 0
174 | curr_meas_no = 1
175 | time_sigs.each { |te|
176 | meas_count = (te.time_from_start - curr_pos) / measure_length
177 | meas_count += 1 if (te.time_from_start - curr_pos) % measure_length > 0
178 | 1.upto(meas_count) { |i|
179 | measures << MIDI::Measure.new(curr_meas_no,
180 | curr_pos, measure_length, oldnumer, olddenom, oldbeats)
181 | curr_meas_no += 1
182 | curr_pos += measure_length
183 | }
184 | oldnumer, olddenom, oldbeats = te.numerator, te.denominator, te.metronome_ticks
185 | measure_length = te.measure_duration(@ppqn)
186 | }
187 | measures
188 | end
189 |
190 | end
191 | end
192 |
--------------------------------------------------------------------------------
/ajm/ruby/midilib/track.rb:
--------------------------------------------------------------------------------
1 | require 'midilib/event'
2 |
3 | module MIDI
4 |
5 | # A Track is a list of events.
6 | #
7 | # When you modify the +events+ array, make sure to call recalc_times so
8 | # each Event gets its +time_from_start+ recalculated.
9 | #
10 | # A Track also holds a bitmask that specifies the channels used by the track.
11 | # This bitmask is set when the track is read from the MIDI file by an
12 | # IO::SeqReader but is _not_ kept up to date by any other methods.
13 |
14 | class Track
15 |
16 | include Enumerable
17 |
18 | UNNAMED = 'Unnamed'
19 |
20 | attr_accessor :events, :channels_used
21 | attr_reader :sequence
22 |
23 | def initialize(sequence)
24 | @sequence = sequence
25 | @events = Array.new()
26 |
27 | # Bitmask of all channels used. Set when track is read in from
28 | # a MIDI file.
29 | @channels_used = 0
30 | end
31 |
32 | # Return track name. If there is no name, return UNNAMED.
33 | def name
34 | event = @events.detect { | e |
35 | e.meta? && e.meta_type == META_SEQ_NAME
36 | }
37 | return event ? event.data_as_str : UNNAMED
38 | end
39 |
40 | # Set track name. Replaces or creates a name meta-event.
41 | def name=(name)
42 | event = @events.detect { | e |
43 | e.meta? && e.meta_type == META_SEQ_NAME
44 | }
45 | if event
46 | event.data = name
47 | else
48 | event = MetaEvent.new(META_SEQ_NAME, name, 0)
49 | @events[0, 0] = event
50 | end
51 | end
52 |
53 | def instrument
54 | MetaEvent.bytes_as_str(@instrument)
55 | end
56 |
57 | def instrument=(str_or_bytes)
58 | @instrument = case str_or_bytes
59 | when String
60 | MetaEvent.str_as_bytes(str_or_bytes)
61 | else
62 | str_or_bytes
63 | end
64 | end
65 |
66 | # Merges an array of events into our event list. After merging, the
67 | # events' time_from_start values are correct so you don't need to worry
68 | # about calling recalc_times.
69 | def merge(event_list)
70 | @events = merge_event_lists(@events, event_list)
71 | end
72 |
73 | # Merges two event arrays together. Does not modify this track.
74 | def merge_event_lists(list1, list2)
75 | recalc_times(0, list1)
76 | recalc_times(0, list2)
77 | list = (list1 + list2).sort_by { | e | e.time_from_start }
78 | recalc_delta_from_times(0, list)
79 | return list
80 | end
81 |
82 | # Quantize every event. length_or_note is either a length (1 = quarter,
83 | # 0.25 = sixteenth, 4 = whole note) or a note name ("sixteenth", "32nd",
84 | # "8th triplet", "dotted quarter").
85 | #
86 | # Since each event's time_from_start is modified, we call
87 | # recalc_delta_from_times after each event quantizes itself.
88 | def quantize(length_or_note)
89 | delta = case length_or_note
90 | when String
91 | @sequence.note_to_delta(length_or_note)
92 | else
93 | @sequence.length_to_delta(length_or_note.to_i)
94 | end
95 | @events.each { | event | event.quantize_to(delta) }
96 | recalc_delta_from_times
97 | end
98 |
99 | # Recalculate start times for all events in +list+ from starting_at to
100 | # end.
101 | def recalc_times(starting_at=0, list=@events)
102 | t = (starting_at == 0) ? 0 : list[starting_at - 1].time_from_start
103 | list[starting_at .. -1].each { | e |
104 | t += e.delta_time
105 | e.time_from_start = t
106 | }
107 | end
108 |
109 | # The opposite of recalc_times: recalculates delta_time for each event
110 | # from each event's time_from_start. This is useful, for example, when
111 | # merging two event lists. As a side-effect, elements from starting_at
112 | # are sorted by time_from_start.
113 | def recalc_delta_from_times(starting_at=0, list=@events)
114 | prev_time_from_start = 0
115 | # We need to sort the sublist. sublist.sort! does not do what we want.
116 | list[starting_at .. -1] = list[starting_at .. -1].sort { | e1, e2 |
117 | e1.time_from_start <=> e2.time_from_start
118 | }
119 | list[starting_at .. -1].each { | e |
120 | e.delta_time = e.time_from_start - prev_time_from_start
121 | prev_time_from_start = e.time_from_start
122 | }
123 | end
124 |
125 | # Iterate over events.
126 | def each # :yields: event
127 | @events.each { | event | yield event }
128 | end
129 |
130 | # Sort events by their time_from_start. After sorting,
131 | # recalc_delta_from_times is called to make sure that the delta times
132 | # reflect the possibly new event order.
133 | def sort
134 | @events = @events.sort_by { | e | e.time_from_start }
135 | recalc_delta_from_times()
136 | end
137 | end
138 |
139 | end
140 |
--------------------------------------------------------------------------------
/ajm/ruby/midilib/utils.rb:
--------------------------------------------------------------------------------
1 | module MIDI
2 |
3 | # Utility methods.
4 | class Utils
5 |
6 | # MIDI note names. NOTE_NAMES[0] is 'C', NOTE_NAMES[1] is 'C#', etc.
7 | NOTE_NAMES = [
8 | 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
9 | ]
10 |
11 | # Given a MIDI note number, return the name and octave as a string.
12 | def Utils.note_to_s(num)
13 | note = num % 12
14 | octave = num / 12
15 | return "#{NOTE_NAMES[note]}#{octave - 1}"
16 | end
17 |
18 | # Given an integer, returns it as a variable length array of bytes (the
19 | # format used by MIDI files).
20 | #
21 | # The converse operation--converting a var len into a number--requires
22 | # input from a stream of bytes. Therefore we don't supply it here. That is
23 | # a part of the MIDIFile class.
24 | def Utils.as_var_len(val)
25 | buffer = []
26 | buffer << (val & 0x7f)
27 | val = (val >> 7)
28 | while val > 0
29 | buffer << (0x80 + (val & 0x7f))
30 | val = (val >> 7)
31 | end
32 | return buffer.reverse!
33 | end
34 |
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/ajm/ruby/webserver/ajm_servlets.rb:
--------------------------------------------------------------------------------
1 | class OutputToMax < WEBrick::HTTPServlet::AbstractServlet
2 | def initialize(server, root)
3 | super
4 | @root = root
5 | end
6 | def do_GET(request, response)
7 | request.query.to_a.each do |pair|
8 | out0 pair
9 | end
10 | response['Content-Type'] = 'text/html'
11 | response.body = open "#{@root}/web/to_max.html" do |file|
12 | file.read
13 | end
14 | end
15 | alias do_POST do_GET
16 | end
17 |
18 | class SourceViewer < WEBrick::HTTPServlet::AbstractServlet
19 | def initialize(server, root)
20 | super
21 | @root = root
22 | end
23 | def do_GET(request, response)
24 | response['Content-Type'] = 'text/plain'
25 | response.body = open "#{@root}#{request.path_info}" do |file|
26 | file.read
27 | end
28 | end
29 | alias do_POST do_GET
30 | end
--------------------------------------------------------------------------------
/ajm/ruby/webserver/ajm_webserver.rb:
--------------------------------------------------------------------------------
1 | require 'java'
2 | require 'webrick'
3 | require 'erb'
4 |
5 | class AjmWebServer
6 |
7 | @@instances = []
8 |
9 | def initialize(config = {})
10 | @server = WEBrick::HTTPServer.new(config)
11 | @server.config[:MimeTypes]['rhtml'] = 'text/html'
12 | @@instances << self
13 | end
14 |
15 | def port
16 | @server.config[:Port]
17 | end
18 |
19 | def root
20 | @server.config[:DocumentRoot]
21 | end
22 |
23 | def servlet(*args)
24 | @server.mount(*args)
25 | end
26 |
27 | def start
28 | Thread.new {
29 | begin
30 | puts "Starting server on port #{port}"
31 | puts "Serving files from #{root}"
32 | @server.start
33 | rescue
34 | error $!
35 | end
36 | }
37 | end
38 |
39 | def stop
40 | puts "Shutting down server on port #{port} ..."
41 | @server.shutdown
42 | while @server.status != :Stop do
43 | puts '...'
44 | java.lang.Thread.sleep(500)
45 | end
46 | puts "Server on port #{port} stopped\n "
47 | end
48 |
49 | def self.stop_all
50 | @@instances.each do |instance|
51 | instance.stop
52 | end
53 | end
54 |
55 | end
56 |
57 | at_exit { AjmWebServer.stop_all }
58 |
--------------------------------------------------------------------------------
/ajm/ruby/webserver/ajm_webserver_example.rb:
--------------------------------------------------------------------------------
1 | $LOAD_PATH.unshift File.dirname($0) # put this folder on the LOAD_PATH
2 | require 'ajm_webserver'
3 | require 'ajm_servlets'
4 | require 'pathname'
5 |
6 | ###########################################################
7 | # set root to the folder containing this script file
8 | this_file = Pathname.new($0)
9 | WEB_ROOT = this_file.parent
10 | # You might normally want to use a subdirectory:
11 | # WEB_ROOT = this_file.parent + Pathname.new('webroot')
12 | # or set absolute path:
13 | # WEB_ROOT = '/Users/user/some/path'
14 |
15 | PORT = if ARGV[0] then ARGV[0] else 9000 end
16 |
17 | ###########################################################
18 | # All you need to set is a port and directory.
19 | # See WEBrick documentation for more config options.
20 | server = AjmWebServer.new({
21 | :Port => PORT,
22 | :DocumentRoot => WEB_ROOT,
23 | :Logger => WEBrick::Log.new($stdout, WEBrick::Log::INFO),
24 | :AccessLog => []
25 | })
26 |
27 |
28 | ###########################################################
29 | # Some example servlets showing custom request handling
30 | server.servlet '/to_max', OutputToMax, WEB_ROOT
31 | server.servlet '/source', SourceViewer, WEB_ROOT
32 |
33 | ###########################################################
34 | # The most important part!
35 | # Needs to happen after the servlets are registered
36 | server.start
37 |
38 |
39 | ###########################################################
40 | # Expose methods to Max to set attributes that can be used
41 | # in .rhtml files:
42 |
43 | $ATTR = {}
44 | def setattr(name,val=nil)
45 | if(val)
46 | $ATTR[name] = val
47 | else
48 | $ATTR.delete(name)
49 | end
50 | end
51 |
52 | def getattr(name)
53 | $ATTR[name]
54 | end
55 |
56 | # convenience method to set attrs with minimal syntax:
57 | def list(args)
58 | if args.length > 2
59 | $ATTR[args[0]] = args[1..-1]
60 | elsif args.length == 2
61 | $ATTR[args[0]] = args[1]
62 | elsif args.length == 1
63 | $ATTR.delete(args[0])
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/ajm/ruby/webserver/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/ajm/ruby/webserver/favicon.ico
--------------------------------------------------------------------------------
/ajm/ruby/webserver/filebrowse/file.html:
--------------------------------------------------------------------------------
1 | Some HTML
2 |
--------------------------------------------------------------------------------
/ajm/ruby/webserver/filebrowse/file.txt:
--------------------------------------------------------------------------------
1 | P
2 | L
3 | A
4 | I
5 | N
6 | T
7 | E
8 | X
9 | T
10 |
--------------------------------------------------------------------------------
/ajm/ruby/webserver/index.html:
--------------------------------------------------------------------------------
1 |
2 | ajm.ruby
3 |
4 |
5 | ajm.ruby web server example
6 |
7 |
8 | This page is being served from ajm.ruby via
9 | WEBrick.
10 |
11 |
12 | Webserver Examples:
13 |
18 |
19 |
20 | View the source for:
21 |
22 | - The code that runs this webserver
23 | - The Max webserver class
24 | - Custom servlets for source code viewing and sending messages to Max
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/ajm/ruby/webserver/web/erb.rhtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | This table shows attributes set from inside Max.
5 | Reload this page to see changes made in Max.
6 |
7 |
8 | Name |
9 | Value |
10 |
11 | <% $ATTR.to_a.each do |pair| %>
12 |
13 | <%= pair[0] %> |
14 | <%= pair[1].inspect %> |
15 |
16 | <% end %>
17 |
18 |
19 |
20 |
24 | view source for this page
25 |
26 | Home
27 |
28 |
--------------------------------------------------------------------------------
/ajm/ruby/webserver/web/to_max.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 | view source for this page
11 |
12 |
13 | Home
14 |
15 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
80 |
81 |
82 |
83 |
84 | Test]]>
85 |
86 | Copyright © 2007-2010 Adam Murray. All Rights Reserved.]]>
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/lib/ant-junit.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/lib/ant-junit.jar
--------------------------------------------------------------------------------
/lib/bsf.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/lib/bsf.jar
--------------------------------------------------------------------------------
/lib/jruby.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/lib/jruby.jar
--------------------------------------------------------------------------------
/lib/junit-4.5.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/lib/junit-4.5.jar
--------------------------------------------------------------------------------
/lib/max.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamjmurray/ajm_objects/2a002a6a88f589ec89713b7a0f18bebbb08a05dd/lib/max.jar
--------------------------------------------------------------------------------
/license/LICENSE-ajm-objects.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2011, Adam Murray (adam@compusition.com).
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use of ajm objects in source and binary forms, with or
6 | without modification, are permitted provided that the following conditions
7 | are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in
14 | the documentation and/or other materials provided with the
15 | distribution.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/license/LICENSE-bsf.txt:
--------------------------------------------------------------------------------
1 | The Apache Software License, Version 1.1
2 |
3 | Copyright (c) 2002 The Apache Software Foundation. All rights
4 | reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions
8 | are met:
9 |
10 | 1. Redistributions of source code must retain the above copyright
11 | notice, this list of conditions and the following disclaimer.
12 |
13 | 2. Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in
15 | the documentation and/or other materials provided with the
16 | distribution.
17 |
18 | 3. The end-user documentation included with the redistribution,
19 | if any, must include the following acknowledgment:
20 | "This product includes software developed by the
21 | Apache Software Foundation (http://www.apache.org/)."
22 | Alternately, this acknowledgment may appear in the software itself,
23 | if and wherever such third-party acknowledgments normally appear.
24 |
25 | 4. The names "BSF", "Apache", and "Apache Software Foundation" must
26 | not be used to endorse or promote products derived from this
27 | software without prior written permission. For written
28 | permission, please contact apache@apache.org.
29 |
30 | 5. Products derived from this software may not be called "Apache",
31 | nor may "Apache" appear in their name, without prior written
32 | permission of the Apache Software Foundation.
33 |
34 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37 | DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
38 | ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
39 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
40 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
41 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
42 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
43 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
44 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 | SUCH DAMAGE.
46 |
47 | This software consists of voluntary contributions made by many individuals
48 | on behalf of the Apache Software Foundation and was originally created by
49 | Sanjiva Weerawarana and others at International Business Machines
50 | Corporation. For more information on the Apache Software Foundation,
51 | please see .
52 |
53 |
54 |
--------------------------------------------------------------------------------
/license/LICENSE-midilib.txt:
--------------------------------------------------------------------------------
1 | Author: Jim Menard (jimm@io.com)
2 | Copyright: Copyright (c) 2003-2005 Jim Menard
3 | License: Distributed under the same license as Ruby.
4 |
5 | midilib is copyrighted free software by Jim Menard and is released under the same license as Ruby.
6 | See the Ruby license at www.ruby-lang.org/en/LICENSE.txt.
7 |
--------------------------------------------------------------------------------
/license/jruby/COPYING:
--------------------------------------------------------------------------------
1 | JRuby is released under a tri CPL/GPL/LGPL license. You can use it,
2 | redistribute it and/or modify it under the terms of the:
3 |
4 | CPL - see COPYING.CPL file
5 | GPL - see COPYING.GPL file
6 | LGPL - see COPYING.LGPL file
7 |
8 | Some additional libraries distributed with JRuby are not covered by
9 | JRuby's licence. See the licence files for the respective libraries in
10 | the 'lib' directory for more information and also LICENSE.RUBY for most
11 | files found in src/lib/ruby/1.8.
12 |
--------------------------------------------------------------------------------
/src/ajm/code.java:
--------------------------------------------------------------------------------
1 | package ajm;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.awt.BorderLayout;
31 | import java.awt.Dimension;
32 | import java.awt.Font;
33 | import java.awt.event.ActionEvent;
34 | import java.util.ArrayList;
35 | import java.util.List;
36 |
37 | import javax.swing.AbstractAction;
38 | import javax.swing.JButton;
39 | import javax.swing.JComponent;
40 | import javax.swing.JFrame;
41 | import javax.swing.JPanel;
42 | import javax.swing.JScrollPane;
43 | import javax.swing.JTabbedPane;
44 | import javax.swing.JTextArea;
45 | import javax.swing.SwingUtilities;
46 |
47 | import ajm.maxsupport.AbstractMaxObject;
48 | import ajm.util.TextBlock;
49 | import ajm.util.Utils;
50 |
51 | import com.cycling74.max.Atom;
52 |
53 | /**
54 | * Multi-tab plaintext editor.
55 | *
56 | * @author Adam Murray (adam@compusition.com)
57 | */
58 | public class code extends AbstractMaxObject {
59 |
60 | // TODO: remember the current index in embedMessage
61 |
62 | // TODO: after clicking a tab, put focus on the text area.
63 | // tried it doesn't work, maybe TextArea needs to be made focusable?
64 | // try focusing on something else and see if it's just broken in max
65 |
66 | private static int NUM_TABS = 6;
67 |
68 | private JFrame frame;
69 | private JTabbedPane tabs;
70 | private List textAreas = new ArrayList();
71 | // private String initText = "";
72 | private RunAction run = new RunAction(this);
73 |
74 | public code(Atom[] args) {
75 |
76 | declareIO(1, 2);
77 |
78 | if (args.length > 0 && args[0].isInt()) {
79 | NUM_TABS = args[0].getInt();
80 | }
81 |
82 | frame = new JFrame("Editor");
83 | frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
84 |
85 | tabs = new JTabbedPane();
86 | /* Doesn't seem to work. Is it a Max thing?
87 | tabs.addChangeListener(new ChangeListener() {
88 | public void stateChanged(ChangeEvent e) {
89 | System.out.println(tabs.getSelectedIndex());
90 | activeTextArea().requestFocus();
91 | }
92 | });
93 | */
94 | for (int i = 0; i < NUM_TABS; i++) {
95 | tabs.add(i + "", createEditor());
96 | }
97 | frame.add(tabs, BorderLayout.CENTER);
98 |
99 | JPanel buttonPanel = new JPanel();
100 | buttonPanel.add(new JButton(run));
101 | frame.add(buttonPanel, BorderLayout.PAGE_END);
102 |
103 | SwingUtilities.invokeLater(new Runnable() {
104 | public void run() {
105 | frame.pack();
106 | // frame.setVisible(true);
107 | }
108 | });
109 | }
110 |
111 | @Override
112 | protected void notifyDeleted() {
113 | /*
114 | SwingUtilities.invokeLater(new Runnable() {
115 | public void run() {
116 | frame.dispose();
117 | }
118 | });
119 | */
120 | super.notifyDeleted();
121 | };
122 |
123 | private final String OBJ_ID = hashCode() + "";
124 |
125 | private String getTextBlockName(int tabIndex) {
126 | return OBJ_ID + "::tab." + tabIndex;
127 | }
128 |
129 | public void dblclick() {
130 | open();
131 | }
132 |
133 | public void open() {
134 | // if (frame != null) {
135 | frame.setVisible(true);
136 | // }
137 | }
138 |
139 | public void set(Atom[] args) {
140 | if (args.length > 1 && args[0].isInt()) {
141 | int index = args[0].getInt();
142 | if (index > 0 && index < textAreas.size()) {
143 | textAreas.get(index).setText(Utils.detokenize(Atom.removeFirst(args)));
144 | return;
145 | }
146 | }
147 | activeTextArea().setText(Utils.detokenize(args));
148 |
149 | }
150 |
151 | public void run(Atom[] args) {
152 | int index = tabs.getSelectedIndex();
153 | if (args.length > 0 && args[0].isInt()) {
154 | int i = args[0].getInt();
155 | if (i > 0 && i < textAreas.size()) {
156 | index = i;
157 | }
158 | }
159 | output(index);
160 | }
161 |
162 | public void bang() {
163 | output(tabs.getSelectedIndex());
164 | }
165 |
166 | private void output(int tabIndex) {
167 | String textblock = getTextBlockName(tabIndex);
168 | TextBlock.set(textblock, textAreas.get(tabIndex).getText());
169 | outlet(1, tabIndex);
170 | outlet(0, "textblock", getTextBlockName(tabIndex));
171 | }
172 |
173 | private JTextArea activeTextArea() {
174 | return textAreas.get(tabs.getSelectedIndex());
175 | }
176 |
177 | private String getText(int tabIndex) {
178 | return textAreas.get(tabIndex).getText();
179 | }
180 |
181 | public void save() {
182 | Atom[] contents = new Atom[NUM_TABS];
183 | for (int i = 0; i < NUM_TABS; i++) {
184 | String text = getText(i);
185 | // This is completely broken. Max pretty much makes it impossible for me to embed
186 | // arbitrary strings. Maybe it will work in Max 5...
187 | System.out.println("TEXT = " + text);
188 | // text = text.replaceAll("\"", "\\\\\"");
189 | // text = text.replaceAll(" ", "\\\\ ");
190 | System.out.println("Escaped text = " + text);
191 | contents[i] = Atom.newAtom(text);
192 | System.out.println(contents[i].toString());
193 | }
194 | embedMessage("settabs", contents);
195 | }
196 |
197 | public void settabs(Atom[] args) {
198 | for (int i = 0; i < args.length && i < NUM_TABS; i++) {
199 | System.out.println("Setting text: " + args[i].toString());
200 | textAreas.get(i).setText(args[i].toString());
201 | }
202 | }
203 |
204 | private JComponent createEditor() {
205 | JTextArea textArea = new JTextArea();
206 | textArea.setTabSize(2); // this probably isn't appropriate for other languages
207 | textArea.setFont(new Font("Monospaced", Font.PLAIN, 11));
208 | // textArea.setText(initText);
209 | textAreas.add(textArea);
210 |
211 | JScrollPane scroller = new JScrollPane(textArea);
212 | scroller.setPreferredSize(new Dimension(300, 400));
213 | return scroller;
214 | }
215 |
216 | @SuppressWarnings("serial")
217 | private static class RunAction extends AbstractAction {
218 | private code thisObj;
219 |
220 | public RunAction(code obj) {
221 | super("Run");
222 | putValue(SHORT_DESCRIPTION, "Send the code out this object's outlet");
223 | thisObj = obj;
224 | }
225 |
226 | public void actionPerformed(ActionEvent e) {
227 | thisObj.bang();
228 | }
229 | }
230 |
231 | }
232 |
--------------------------------------------------------------------------------
/src/ajm/eval.java:
--------------------------------------------------------------------------------
1 | package ajm;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.util.List;
31 |
32 | import ajm.maxsupport.AbstractMaxRubyObject;
33 | import ajm.seqsupport.Item;
34 | import ajm.seqsupport.Parser;
35 | import ajm.util.Utils;
36 |
37 | import com.cycling74.max.Atom;
38 | import com.cycling74.max.Executable;
39 |
40 | /**
41 | * The ajm.eval MaxObject
42 | *
43 | * @author Adam Murray (adam@compusition.com)
44 | */
45 | public class eval extends AbstractMaxRubyObject {
46 |
47 | Parser parser = new Parser();
48 |
49 | public eval(Atom[] args) {
50 | declareIO(1, 1);
51 | setInletAssist(new String[] { "message" });
52 | setOutletAssist(new String[] { "evaluated message" });
53 | // TODO parsing options
54 | }
55 |
56 | @Override
57 | protected Executable getInitializer() {
58 | return new EvalInitializer();
59 | }
60 |
61 | protected class EvalInitializer extends DefaultRubyInitializer {
62 | @Override
63 | public void execute() {
64 | super.execute();
65 | parser.setRubyEvaluator(ruby);
66 | }
67 | }
68 |
69 | public void list(Atom[] list) {
70 | anything(null, list);
71 | }
72 |
73 | public void anything(String msg, Atom[] args) {
74 | try {
75 | List- items = parser.parse(msg, args);
76 | Atom[] atoms = new Atom[items.size()];
77 | for (int i = 0; i < atoms.length; i++) {
78 | Item item = items.get(i);
79 | item.getValue(); // evaluate ruby, if needed
80 | atoms[i] = item.toAtom();
81 | }
82 | outlet(0, atoms);
83 | }
84 | catch (Exception e) {
85 | err("Could not evaluate: " + Utils.detokenize(msg, args) + "\n" + e.getMessage());
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/ajm/maxsupport/AbstractMaxObject.java:
--------------------------------------------------------------------------------
1 | package ajm.maxsupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.io.PrintStream;
31 |
32 | import ajm.util.Logger;
33 |
34 | import com.cycling74.max.Executable;
35 | import com.cycling74.max.MaxObject;
36 | import com.cycling74.max.MaxQelem;
37 |
38 | /**
39 | * Common behavior for ajm objects.
40 | *
41 | * @author Adam Murray (adam@compusition.com)
42 | */
43 | public abstract class AbstractMaxObject extends MaxObject implements Logger {
44 |
45 | protected boolean verbose = false;
46 |
47 | protected boolean initialized = false;
48 | protected MaxQelem initializer = getInitializerQelem();
49 |
50 | // The initializer should never be null inside Max, but needs to be null in unit tests
51 |
52 | public AbstractMaxObject() {
53 | declareAttribute("verbose");
54 | if (initializer == null) {
55 | initialized = true;
56 | }
57 | else {
58 | initializer.set();
59 | }
60 | }
61 |
62 | private final MaxQelem getInitializerQelem() {
63 | Executable initializer = getInitializer();
64 | return initializer == null ? null : new MaxQelem(initializer);
65 | }
66 |
67 | /**
68 | * A subclass can override this method to return a MaxQelem that can do operations not normally available in the
69 | * constructor, such as outputting messages. See discussion at
70 | * http://www.cycling74.com/forums/index.php?t=rview&goto=114649 Note that if this method is overriden, it is the
71 | * responsibility of the initializer code to set initialized = true.
72 | *
73 | * @return an Executable that executes initialization code. Must not be null.
74 | */
75 | protected Executable getInitializer() {
76 | return new DefaultInitializer();
77 | }
78 |
79 | protected class DefaultInitializer implements Executable {
80 | public void execute() {
81 | initialized = true;
82 | }
83 | }
84 |
85 | @Override
86 | protected void notifyDeleted() {
87 | if (initializer != null) {
88 | initializer.release();
89 | }
90 | }
91 |
92 | // for use with debugging unit tests, must be set from the test after instantiation
93 | private PrintStream debugOut;
94 |
95 | /**
96 | * Unit tests can set this output stream back to system.out (or a log file) for testing purposes outside of the Max
97 | * environment. Normally any MaxObject will "steal" System.out and set it to the Max output stream (which is not
98 | * actually available outside of Max).
99 | *
100 | * @param debugOut
101 | */
102 | protected void setDebugOut(PrintStream debugOut) {
103 | this.debugOut = debugOut;
104 | }
105 |
106 | public void debug(String message) {
107 | if (debugOut != null) {
108 | debugOut.println(message);
109 | }
110 | }
111 |
112 | /**
113 | * Automatically prepends the object name to the beginning of error messages.
114 | *
115 | * @param message
116 | * the error message
117 | */
118 | public void info(String message) {
119 | info(message, false);
120 | }
121 | public void info(String message, boolean force) {
122 | if (verbose || force) {
123 | post(this.getClass().getName() + ": " + message);
124 | }
125 | }
126 |
127 | /**
128 | * Automatically prepends the object name to the beginning of error messages.
129 | *
130 | * @param message
131 | * the error message
132 | */
133 | public void err(String message) {
134 | error(this.getClass().getName() + ": " + message);
135 | }
136 |
137 | public String toString() {
138 | return getClass().getName() + "#<" + Integer.toHexString(hashCode()) + ">";
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/ajm/maxsupport/AbstractMaxRubyObject.java:
--------------------------------------------------------------------------------
1 | package ajm.maxsupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import org.jruby.CompatVersion;
31 |
32 | import com.cycling74.max.*;
33 |
34 | import ajm.rubysupport.*;
35 | import ajm.util.Utils;
36 |
37 | /**
38 | * Superclass for objects that support Ruby scripting.
39 | *
40 | * @author Adam Murray (adam@compusition.com)
41 | */
42 | public abstract class AbstractMaxRubyObject extends AbstractMaxObject {
43 |
44 | protected String context = null;
45 | protected String id = defaultId();
46 | private boolean autoinit = false;
47 |
48 | protected Atom[] rubyVersionValue;
49 | protected CompatVersion rubyVersion;
50 |
51 | protected MaxRubyAdapter ruby;
52 |
53 | protected AbstractMaxRubyObject self = this;
54 |
55 | public AbstractMaxRubyObject() {
56 | super();
57 | declareAttribute("context", "getcontext", "context");
58 | declareAttribute("id", "getid", "id");
59 | declareAttribute("autoinit");
60 | declareAttribute("ruby_version", "getruby_version", "ruby_version");
61 | }
62 |
63 | @Override
64 | protected Executable getInitializer() {
65 | return new DefaultRubyInitializer();
66 | }
67 |
68 | protected class DefaultRubyInitializer extends DefaultInitializer {
69 | @Override
70 | public void execute() {
71 | super.execute();
72 | try {
73 | ruby = new MaxRubyAdapter(self, context, id, rubyVersion);
74 | }
75 | catch (IdInUseException e) {
76 | String availableId = e.getMessage();
77 | error("id " + id + " not available. Using: " + availableId);
78 | id = availableId;
79 | ruby = new MaxRubyAdapter(self, context, id, rubyVersion);
80 | }
81 | if (autoinit) {
82 | ruby.init();
83 | /* Doing this at construction time causes Max to hang for a while if there are many instances of this object.
84 | Thus autoinit is false by default.
85 | The downside to not init'ing here is there will be a slight delay the first time a script tries to evaluate
86 | The hang delay got much shorter with JRuby 1.1. */
87 | }
88 | }
89 | }
90 |
91 | public Atom[] getcontext() {
92 | return Atom.newAtom(new String[]{context});
93 | }
94 |
95 | public String context() {
96 | return context;
97 | }
98 |
99 | public void context(Atom[] params) {
100 | String context = Utils.toString(params);
101 | this.context = context;
102 | if (ruby != null) {
103 | ruby.setContext(context);
104 | } // else we're still initializing and the initalizer should handle this
105 | }
106 |
107 | public Atom[] getid() {
108 | return Atom.newAtom(new String[]{id});
109 | }
110 |
111 | public String id() {
112 | return id;
113 | }
114 |
115 | public void id(Atom[] params) {
116 | String id = Utils.toString(params);
117 | if (id == null || "".equals(id)) {
118 | id = defaultId();
119 | }
120 | this.id = id;
121 | if (ruby != null) {
122 | try {
123 | ruby.setId(id);
124 | }
125 | catch (IdInUseException e) {
126 | String availableId = e.getMessage();
127 | error("id " + id + " not available. Using: " + availableId);
128 | id = availableId;
129 | ruby.setId(id);
130 | }
131 | } // else we're still initializing and the initalizer should handle this
132 | }
133 |
134 | private String defaultId() {
135 | return Integer.toHexString(hashCode());
136 | }
137 |
138 | public Atom[] getruby_version() {
139 | return rubyVersionValue;
140 | }
141 |
142 | // Using Atom[] is annoying but it avoids an annoying warning in the max menu "coerced float to String" when doing @ruby_version 1.9
143 | public void ruby_version(Atom[] rubyVersionValue) {
144 | if(initialized) {
145 | err("ruby_version cannot be changed. Use @ruby_version when creating the object.");
146 | return;
147 | }
148 | if(rubyVersionValue == null || rubyVersionValue.length < 1) return;
149 |
150 | String rubyVersionString = rubyVersionValue[0].toString();
151 | while(rubyVersionString.endsWith("0")) {
152 | // @ruby_version 1.9 comes through as "1.900000" so we have to chop off the trailing zeros
153 | rubyVersionString = rubyVersionString.substring(0, rubyVersionString.length()-1);
154 | }
155 |
156 | this.rubyVersion = RubyProperties.getRubyVersion(rubyVersionString);
157 | if(this.rubyVersion == null) {
158 | err("Invalid ruby_version '" + rubyVersionString + "'. Only 1_8 and 1_9 supported. " +
159 | "Defaulting to version " + RubyProperties.DEFAULT_RUBY_VERSION_STRING + ".");
160 | this.rubyVersionValue = Atom.newAtom(new String[]{RubyProperties.DEFAULT_RUBY_VERSION_STRING});
161 | rubyVersion = RubyProperties.DEFAULT_RUBY_VERSION;
162 | } else {
163 | this.rubyVersionValue = Atom.newAtom(new String[]{rubyVersionString});
164 | }
165 | }
166 |
167 | @Override
168 | public void notifyDeleted() {
169 | if (ruby != null) {
170 | ruby.notifyDeleted();
171 | }
172 | super.notifyDeleted();
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/src/ajm/maxsupport/Atomizer.java:
--------------------------------------------------------------------------------
1 | package ajm.maxsupport;
2 |
3 | import com.cycling74.max.Atom;
4 |
5 | /**
6 | * Interface for objects that can convert themselves to a Max Atom.
7 | *
8 | * @author Adam Murray (adam@compusition.com)
9 | */
10 | public interface Atomizer {
11 |
12 | Atom toAtom();
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/ajm/preemptrseq.java:
--------------------------------------------------------------------------------
1 | package ajm;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.util.ArrayList;
31 | import java.util.List;
32 |
33 | import ajm.seqsupport.Item;
34 | import ajm.util.Utils;
35 |
36 | import com.cycling74.max.Atom;
37 |
38 | /**
39 | * Preemptive rseq, sends positive values out one bang early.
40 | *
41 | * @author Adam Murray (adam@compusition.com)
42 | */
43 | public class preemptrseq extends rseq {
44 |
45 | List
- actualSeq = new ArrayList
- ();
46 |
47 | public preemptrseq(Atom[] args) {
48 | super(args);
49 | }
50 |
51 | @Override
52 | public void set(Atom[] list) {
53 | try {
54 | List
- newSeq = parser.parse(list);
55 |
56 | actualSeq.clear();
57 | actualSeq.addAll(newSeq);
58 | setPreemptiveSeq();
59 |
60 | onSeqChange();
61 | }
62 | catch (IllegalStateException e) {
63 | err("Could not evaluate: " + Utils.detokenize(list) + "\n" + e.getMessage());
64 | }
65 | }
66 |
67 | // This shouldn't be necessary
68 | @Override
69 | public void resetseq() {
70 | actualSeq.clear();
71 | actualSeq.addAll(defaultSeq);
72 | setPreemptiveSeq();
73 | onSeqChange();
74 | outputseq();
75 | }
76 |
77 | // The seq needs to look the same externally
78 | // but internally transform positive numbers n into -(n-1), -1
79 | // e.g. 16 4 3 -> -15 1 -3 1 -2 1
80 | private void setPreemptiveSeq() {
81 | seq.clear();
82 | for (Item item : actualSeq) {
83 | Atom atom = item.getAtom();
84 | if (Utils.isNumber(atom)) {
85 | int val = atom.toInt();
86 | if (val > 1) {
87 | seq.add(new Item(-(val - 1)));
88 | seq.add(new Item(1));
89 | }
90 | else {
91 | seq.add(item);
92 | }
93 | }
94 | else {
95 | seq.add(item);
96 | }
97 | }
98 | }
99 |
100 | @Override
101 | public Atom[] getseq() {
102 | Atom[] atoms = new Atom[actualSeq.size()];
103 | for (int i = 0; i < actualSeq.size(); i++) {
104 | atoms[i] = actualSeq.get(i).getAtom();
105 | }
106 | return atoms;
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/src/ajm/print.java:
--------------------------------------------------------------------------------
1 | package ajm;
2 |
3 | import com.cycling74.max.Atom;
4 |
5 | import ajm.maxsupport.AbstractMaxObject;
6 | import ajm.util.Utils;
7 |
8 | public class print extends AbstractMaxObject {
9 |
10 | private boolean onlyanything = false;
11 | private boolean detokenize = false;
12 |
13 | public print() {
14 | declareAttribute("onlyanything");
15 | declareAttribute("detokenize");
16 | declareIO(1, 0);
17 | createInfoOutlet(false);
18 | }
19 |
20 | public void inlet(float value) {
21 | if (onlyanything) {
22 | super.inlet(value);
23 | }
24 | else {
25 | System.out.println("Recieved a float: " + value);
26 | }
27 | }
28 |
29 | public void inlet(int value) {
30 | if (onlyanything) {
31 | super.inlet(value);
32 | }
33 | else {
34 | System.out.println("Recieved an int: " + value);
35 | }
36 | }
37 |
38 | public void list(Atom[] list) {
39 | if (onlyanything) {
40 | super.list(list);
41 | }
42 | else {
43 | System.out.println("Received a list of length " + list.length + (list.length > 0 ? ":" : "."));
44 | dumpList(list);
45 | }
46 | }
47 |
48 | public void anything(String msg, Atom[] args) {
49 | System.out.println("Received message '" + (detokenize ? Utils.detokenize(msg) : msg) + "' with " + args.length
50 | + " arguments" + (args.length > 0 ? ":" : "."));
51 | dumpList(args);
52 | }
53 |
54 | private void dumpList(Atom[] list) {
55 | for (Atom atom : list) {
56 | if (atom == null) {
57 | System.out.println(" null (might have been a semicolon?)");
58 | }
59 | else if (atom.isInt()) {
60 | System.out.println(" int: " + atom.getInt());
61 | }
62 | else if (atom.isFloat()) {
63 | System.out.println(" float: " + atom.getFloat());
64 | }
65 | else {
66 | System.out.println(" string: '" + (detokenize ? Utils.detokenize(atom.getString()) : atom.getString())
67 | + "'");
68 | }
69 | }
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/ajm/rseq.java:
--------------------------------------------------------------------------------
1 | package ajm;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.util.Arrays;
31 |
32 | import ajm.seqsupport.Item;
33 |
34 | import com.cycling74.max.Atom;
35 | import com.cycling74.max.Executable;
36 |
37 | /**
38 | * The ajm.rseq MaxObject
39 | *
40 | * @author Adam Murray (adam@compusition.com)
41 | */
42 | public class rseq extends seq {
43 |
44 | public rseq(Atom[] args) {
45 | super(args); // declares seq attributes
46 | parser.setEvalNotes(false);
47 | }
48 |
49 | @Override
50 | protected Executable getInitializer() {
51 | return new RseqInitializer();
52 | }
53 |
54 | protected class RseqInitializer extends SeqInitializer {
55 | @Override
56 | public void execute() {
57 | super.execute();
58 | }
59 | }
60 |
61 | protected static int INFO_OUTLET = 5;
62 |
63 | protected int count = 0;
64 | protected int duration = INDEX_SET_BEFORE_SEQ;
65 | protected static int INFINITY = -1;
66 | protected static int INDEX_SET_BEFORE_SEQ = -2;
67 |
68 | /*
69 | @Override
70 | protected void onSeqChange() {
71 | super.onSeqChange();
72 | if (seq.size() > 0) {
73 | boolean allZeros = true;
74 | for (Item item : seq) {
75 | Object val = item.getValue();
76 | if (val instanceof Atom) {
77 | Atom atom = (Atom) val;
78 | if (Utils.isNumber(atom) && atom.toInt() != 0) {
79 | allZeros = false;
80 | break;
81 | }
82 | }
83 | }
84 | if (allZeros) {
85 | seq.add(new Item(1)); // prevents infinite loops
86 | }
87 | }
88 | }
89 | */
90 |
91 | @Override
92 | public void bang() {
93 | if (!seq.isEmpty()) {
94 | fixIndexBounds();
95 |
96 | // debug("!! count=" + count + ", duration=" + duration);
97 |
98 | if (count >= duration) {
99 | if (duration == INDEX_SET_BEFORE_SEQ) {
100 | // handle case where @index is set before @seq
101 | // by reseting the index without advancing, to get the current value
102 | index(index);
103 | }
104 | else if (duration > 0) {
105 | if (!arpeggiating()) {
106 | index(index + step);
107 | }
108 | else {
109 | setDuration();
110 | }
111 | }
112 | }
113 |
114 | // debug("count=" + count + ", duration=" + duration);
115 |
116 | if (count == 0) {
117 | output();
118 | int startIndex = index;
119 | while (duration == 0 && duration != INFINITY) {
120 | if (!arpeggiating()) {
121 | index(index + step);
122 | if (index == startIndex) {
123 | // prevent infinite loop
124 | // we already advanced to the next value we want to output,
125 | // so prevent advancing on the next bang:
126 | duration = INDEX_SET_BEFORE_SEQ;
127 | break;
128 | }
129 | }
130 | else {
131 | setDuration();
132 | }
133 | output();
134 | }
135 | }
136 | }
137 | count++;
138 | }
139 |
140 | @Override
141 | public void index(int idx) {
142 | super.index(idx);
143 | setDuration();
144 | }
145 |
146 | private void setDuration() {
147 | count = 0;
148 | if (seq.isEmpty()) {
149 | duration = INDEX_SET_BEFORE_SEQ;
150 | }
151 | else {
152 | fixIndexBounds();
153 | Item item = seq.get(index);
154 | if (item.isInfinite()) {
155 | duration = INFINITY;
156 | return;
157 | }
158 |
159 | Object val = item.getValue();
160 | if (val instanceof Atom) {
161 | Atom atom = (Atom) val;
162 | // Strings will be coerced to 0, which is exactly what we want (output immediately and advance)
163 | duration = Math.abs(atom.toInt());
164 | }
165 | else if (chordmode == CHORDMODE.ARPEGGIATE) {
166 | duration = Math.abs(((Atom[]) val)[chordIndex].toInt());
167 | }
168 | else {
169 | duration = 0;
170 | }
171 | }
172 | }
173 |
174 | @Override
175 | public void reset() {
176 | super.reset();
177 | count = 0;
178 | duration = 0;
179 | }
180 |
181 | public boolean equals(Object obj) {
182 | if (obj instanceof rseq) {
183 | return Arrays.equals(getseq(), ((rseq) obj).getseq());
184 | }
185 | else {
186 | return false;
187 | }
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/ajm/rubysupport/AbstractScriptEvaluator.java:
--------------------------------------------------------------------------------
1 | package ajm.rubysupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.util.HashMap;
31 | import java.util.Map;
32 |
33 | /**
34 | * Superclass for script evaluator implementations.
35 | *
36 | * @author Adam Murray (adam@compusition.com)
37 | */
38 | public abstract class AbstractScriptEvaluator implements ScriptEvaluator {
39 |
40 | private boolean initialized = false;
41 | private Map persitentGlobals = new HashMap();
42 |
43 | protected abstract void resetEngineContext();
44 |
45 | protected abstract void declareGlobalInternal(String variableName, Object obj) throws Exception;
46 |
47 | protected abstract void undeclareGlobalInternal(String variableName) throws Exception;
48 |
49 | public void resetContext() {
50 | resetEngineContext();
51 | try {
52 | for (Map.Entry global : persitentGlobals.entrySet()) {
53 | String name = global.getKey();
54 | undeclareGlobalInternal(name);
55 | declareGlobalInternal(name, global.getValue());
56 | }
57 | }
58 | catch (Exception e) {
59 | throw new RubyException(e);
60 | }
61 | }
62 |
63 | public boolean isInitialized() {
64 | return initialized;
65 | }
66 |
67 | public void setInitialized(boolean initialized) {
68 | this.initialized = initialized;
69 | }
70 |
71 | public void declareGlobal(String variableName, Object obj) {
72 | try {
73 | declareGlobalInternal(variableName, obj);
74 | }
75 | catch (Exception e) {
76 | throw new RubyException(e);
77 | }
78 | persitentGlobals.put(variableName, obj);
79 | }
80 |
81 | public void undeclareGlobal(String variableName) {
82 | try {
83 | undeclareGlobalInternal(variableName);
84 | }
85 | catch (Exception e) {
86 | throw new RubyException(e);
87 | }
88 | persitentGlobals.remove(variableName);
89 | }
90 |
91 | public void setScriptFilename(String scriptFilename) {
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/ajm/rubysupport/BSFRubyEvaluator.java:
--------------------------------------------------------------------------------
1 | package ajm.rubysupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import org.apache.bsf.BSFException;
31 | import org.apache.bsf.BSFManager;
32 | import org.jruby.CompatVersion;
33 |
34 | /**
35 | * Bridge to Apache BSF project's Ruby evaluator engine.
36 | *
37 | * @author Adam Murray (adam@compusition.com)
38 | */
39 | public class BSFRubyEvaluator extends AbstractScriptEvaluator {
40 |
41 | private BSFManager manager;
42 |
43 | public BSFRubyEvaluator(CompatVersion version) {
44 | BSFManager.registerScriptingEngine("ruby", "org.jruby.javasupport.bsf.JRubyEngine", new String[] { "rb" });
45 | resetEngineContext();
46 | }
47 |
48 | protected void resetEngineContext() {
49 | manager = new BSFManager();
50 | }
51 |
52 | protected void declareGlobalInternal(String variableName, Object obj) throws BSFException {
53 | manager.declareBean(variableName, obj, obj.getClass());
54 | }
55 |
56 | protected void undeclareGlobalInternal(String variableName) throws BSFException {
57 | // BUG
58 | // It should be a best practice to undeclare any global varaibles before redeclaring them,
59 | // but when I try to do this, somehow $max_ruby_adapter and $global_variable_store can end up
60 | // null inside my scripts even though I can verify that it is being redeclared as a non-null value,
61 | // or not even being undeclared at all!
62 |
63 | // Commenting this out for now until I can figure out what's going on (probably a bug in JRuby?)
64 | // manager.undeclareBean(variableName);
65 | }
66 |
67 | public Object eval(CharSequence rubyCode) {
68 | try {
69 | return manager.eval("ruby", getClass().getName(), 1, 1, rubyCode);
70 | }
71 | catch (BSFException e) {
72 | throw new RubyException(e);
73 | }
74 | }
75 |
76 | public void exit() {
77 | manager.terminate();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/ajm/rubysupport/IdInUseException.java:
--------------------------------------------------------------------------------
1 | package ajm.rubysupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | /**
31 | * A failure to assign an ID because it was already in use.
32 | *
33 | * The exception message will suggest an available id.
34 | *
35 | * @author Adam Murray (adam@compusition.com)
36 | */
37 | @SuppressWarnings("serial")
38 | public class IdInUseException extends RuntimeException {
39 |
40 | public IdInUseException(String availableId) {
41 | super(availableId);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/ajm/rubysupport/JRubyEmbedEvaluator.java:
--------------------------------------------------------------------------------
1 | package ajm.rubysupport;
2 |
3 | import org.jruby.CompatVersion;
4 | import org.jruby.embed.ScriptingContainer;
5 | import org.jruby.embed.LocalContextScope;
6 | import org.jruby.embed.LocalVariableBehavior;
7 |
8 | public class JRubyEmbedEvaluator extends AbstractScriptEvaluator {
9 |
10 | private ScriptingContainer container;
11 |
12 | private CompatVersion compatVersion;
13 |
14 | public JRubyEmbedEvaluator(CompatVersion rubyVersion) {
15 | // if compatVersion isn't set right away, it won't work (maybe because of the global var setup?)
16 | compatVersion = rubyVersion;
17 | resetEngineContext();
18 | }
19 |
20 | protected void resetEngineContext() {
21 | container = new ScriptingContainer(LocalContextScope.SINGLETHREAD, LocalVariableBehavior.TRANSIENT);
22 | container.setCompatVersion(compatVersion);
23 | }
24 |
25 | protected void declareGlobalInternal(String variableName, Object obj) {
26 | container.put("$" + variableName, obj);
27 | }
28 |
29 | protected void undeclareGlobalInternal(String variableName) {
30 | container.removeAttribute("$" + variableName);
31 | }
32 |
33 | public void setScriptFilename(String scriptFilename) {
34 | if(scriptFilename == null) {
35 | scriptFilename = "";
36 | }
37 | container.setScriptFilename(scriptFilename);
38 | }
39 |
40 | public Object eval(CharSequence rubyCode) {
41 | return container.runScriptlet(rubyCode.toString());
42 | }
43 |
44 | public void exit() {
45 | container.terminate();
46 | container = null;
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/ajm/rubysupport/RubyException.java:
--------------------------------------------------------------------------------
1 | package ajm.rubysupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | /**
31 | * Wrapper for any Ruby exceptions. Helps encapsulate the underlying Ruby implementation.
32 | *
33 | * @author Adam Murray (adam@compusition.com)
34 | */
35 | @SuppressWarnings("serial")
36 | public class RubyException extends RuntimeException {
37 |
38 | public RubyException() {
39 | super();
40 | }
41 |
42 | public RubyException(String message, Throwable cause) {
43 | super(message, cause);
44 | }
45 |
46 | public RubyException(String message) {
47 | super(message);
48 | }
49 |
50 | public RubyException(Throwable cause) {
51 | super(cause);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/ajm/rubysupport/RubyProperties.java:
--------------------------------------------------------------------------------
1 | package ajm.rubysupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.io.File;
31 | import java.io.FileInputStream;
32 | import java.io.IOException;
33 | import java.util.Properties;
34 |
35 | import org.jruby.CompatVersion;
36 |
37 | import com.cycling74.max.MaxSystem;
38 |
39 | /**
40 | * Manages all global settings for Ruby evaluation.
41 | *
42 | * @author Adam Murray (adam@compusition.com)
43 | */
44 | public class RubyProperties {
45 |
46 | public static Properties properties;
47 |
48 | public static final String DEFAULT_RUBY_ENGINE = "ajm.rubysupport.JRubyEmbedEvaluator";
49 |
50 | public static final String DEFAULT_INITIALIZER_FILES = "ajm_ruby_initialize.rb";
51 |
52 | private static String rubyEngine;
53 |
54 | private static String[] initializers;
55 |
56 | private static String[] loadpaths;
57 |
58 | public static String getRubyEngine() {
59 | if (rubyEngine == null) {
60 | rubyEngine = properties.getProperty("ruby.engine", DEFAULT_RUBY_ENGINE).trim();
61 | }
62 | return rubyEngine;
63 | }
64 |
65 | public static String[] getInitializerFiles() {
66 | if (initializers == null) {
67 | initializers = properties.getProperty("ruby.initializers", DEFAULT_INITIALIZER_FILES).split(";");
68 | for (int i = 0; i < initializers.length; i++) {
69 | initializers[i] = initializers[i].trim();
70 | }
71 | }
72 | return initializers;
73 | }
74 |
75 | public static String[] getLoadPaths() {
76 | if (loadpaths == null) {
77 | String loadpathsProp = properties.getProperty("ruby.loadpaths");
78 | if (loadpathsProp == null || loadpathsProp.trim().equals("")) {
79 | loadpaths = new String[] {};
80 | }
81 | else {
82 | loadpaths = loadpathsProp.split(";");
83 | for (int i = 0; i < loadpaths.length; i++) {
84 | loadpaths[i] = loadpaths[i].trim();
85 | }
86 | }
87 | }
88 | return loadpaths;
89 | }
90 |
91 | static {
92 | // Initialize JRuby system properties and load the ajm.ruby.properties file
93 | try {
94 | String propsPath = MaxSystem.locateFile("ajm.ruby.properties");
95 | if (propsPath == null) {
96 | //MaxSystem.error("ajm.ruby.properties not found! Maybe ajm objects was not installed correctly?");
97 | properties = new Properties();
98 | }
99 | else {
100 | File propFile = new File(propsPath);
101 | properties = new Properties();
102 | try {
103 | properties.load(new FileInputStream(propFile));
104 | }
105 | catch (IOException e) {
106 | throw new RuntimeException(e);
107 | }
108 |
109 | String jrubyHome = properties.getProperty("jruby.home");
110 | if(jrubyHome != null) {
111 | System.setProperty("jruby.home", jrubyHome);
112 | }
113 | }
114 | }
115 | catch (UnsatisfiedLinkError e) {
116 | // we're running outside of Max, probably for unit testing
117 | // can't call System.out here, Max stole it and it will just cause another UnsatisfiedLinkError
118 | // System.out.println("Using hard-coded defaults for RubyProperties.");
119 | properties = new Properties();
120 | }
121 | }
122 |
123 | public static CompatVersion getRubyVersion(String version) {
124 | if("1.9".equals(version)) {
125 | return CompatVersion.RUBY1_9;
126 | }
127 | else if("1.8".equals(version)) {
128 | return CompatVersion.RUBY1_8;
129 | }
130 | else return null;
131 | }
132 |
133 | public static String DEFAULT_RUBY_VERSION_STRING = "1.8";
134 | public static CompatVersion DEFAULT_RUBY_VERSION = CompatVersion.RUBY1_8;
135 | }
136 |
--------------------------------------------------------------------------------
/src/ajm/rubysupport/ScriptEvaluator.java:
--------------------------------------------------------------------------------
1 | package ajm.rubysupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | /**
31 | * Interface for all script evaluator engines.
32 | *
33 | * @author Adam Murray (adam@compusition.com)
34 | */
35 | public interface ScriptEvaluator {
36 |
37 | void resetContext();
38 |
39 | boolean isInitialized();
40 |
41 | void setInitialized(boolean initialized);
42 |
43 | void declareGlobal(String variableName, Object obj);
44 |
45 | void undeclareGlobal(String variableName);
46 |
47 | void setScriptFilename(String scriptFilename);
48 |
49 | Object eval(CharSequence rubyCode);
50 |
51 | void exit();
52 | }
53 |
--------------------------------------------------------------------------------
/src/ajm/rubysupport/SymbolConversionOption.java:
--------------------------------------------------------------------------------
1 | package ajm.rubysupport;
2 |
3 | /*
4 | Copyright (c) 2010, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | public enum SymbolConversionOption {
31 |
32 | /**
33 | * Convert incoming Max symbols to Ruby strings
34 | */
35 | STRING ("string"),
36 |
37 | /**
38 | * Convert incoming Max symbols to Ruby symbols
39 | */
40 | SYMBOL ("symbol"),
41 |
42 | /**
43 | * Don't modify incoming Max symbols (so they can be interpreted as literal numbers, reference method names, etc)
44 | */
45 | LITERAL ("literal"),
46 |
47 | /**
48 | * Apply the previous setting to the remaining inlets
49 | */
50 | REMAINING_INLETS ("*");
51 |
52 | public static SymbolConversionOption DEFAULT = LITERAL;
53 |
54 | SymbolConversionOption(String stringValue) {
55 | this.stringValue = stringValue;
56 | }
57 |
58 | /**
59 | * The string value of this enumerated value.
60 | */
61 | public String toString() {
62 | return stringValue;
63 | }
64 | private final String stringValue;
65 |
66 | public static SymbolConversionOption fromString(String stringValue) {
67 | if(STRING.stringValue.equals(stringValue)) {
68 | return STRING;
69 | }
70 | else if(SYMBOL.stringValue.equals(stringValue)) {
71 | return SYMBOL;
72 | }
73 | else if(LITERAL.stringValue.equals(stringValue)) {
74 | return LITERAL;
75 | }
76 | else if(REMAINING_INLETS.stringValue.equals(stringValue)) {
77 | return REMAINING_INLETS;
78 | }
79 | else {
80 | return null;
81 | }
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/ajm/seqsupport/Token.java:
--------------------------------------------------------------------------------
1 | package ajm.seqsupport;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import com.cycling74.max.Atom;
31 |
32 | /**
33 | * A token in the ajm objects sequencing syntax.
34 | *
35 | * @author Adam Murray (adam@compusition.com)
36 | */
37 | public class Token {
38 |
39 | public enum TYPE {
40 | REPEAT_BEGIN, REPEAT_END, REPEAT_STAR, CHORD_BEGIN, CHORD_END, NEXT, PREV, RUBY_BEGIN, RUBY_END, TEXT;
41 | }
42 |
43 | private TYPE type;
44 | private String text;
45 |
46 | public Token(TYPE type) {
47 | this(type, null);
48 | }
49 |
50 | public Token(TYPE type, String text) {
51 | this.type = type;
52 | this.text = text;
53 | }
54 |
55 | public TYPE getType() {
56 | return type;
57 | }
58 |
59 | public int getInt() {
60 | return Integer.parseInt(text);
61 | }
62 |
63 | public String getText() {
64 | return text;
65 | }
66 |
67 | public Item getItem() {
68 | try {
69 | if (text.contains(".")) {
70 | return new Item(Float.parseFloat(text));
71 | }
72 | else {
73 | return new Item(Integer.parseInt(text));
74 | }
75 | }
76 | catch (NumberFormatException e) {
77 | return new Item(text);
78 | }
79 | }
80 |
81 | public Atom getAtom() {
82 | try {
83 | if (text.contains(".")) {
84 | return Atom.newAtom(Float.parseFloat(text));
85 | }
86 | else {
87 | return Atom.newAtom(Integer.parseInt(text));
88 | }
89 | }
90 | catch (NumberFormatException e) {
91 | return Atom.newAtom(text);
92 | }
93 | }
94 |
95 | public Atom getValue() {
96 | int val;
97 | switch (Character.toUpperCase(text.charAt(0))) {
98 | case 'C':
99 | val = 0;
100 | break;
101 |
102 | case 'D':
103 | val = 2;
104 | break;
105 |
106 | case 'E':
107 | val = 4;
108 | break;
109 |
110 | case 'F':
111 | val = 5;
112 | break;
113 |
114 | case 'G':
115 | val = 7;
116 | break;
117 |
118 | case 'A':
119 | val = 9;
120 | break;
121 |
122 | case 'B':
123 | val = 11;
124 | break;
125 |
126 | default:
127 | return getAtom();
128 |
129 | }
130 |
131 | int i = 1;
132 | int quarterSteps = 0;
133 | loop: for (; i < text.length(); i++) {
134 | switch (text.charAt(i)) {
135 | case '#':
136 | val++;
137 | break;
138 |
139 | case 'b':
140 | val--;
141 | break;
142 |
143 | case '+':
144 | quarterSteps++;
145 | break;
146 |
147 | case '_':
148 | quarterSteps--;
149 | break;
150 |
151 | default:
152 | break loop;
153 | }
154 | }
155 |
156 | try {
157 | int octave = Integer.parseInt(text.substring(i));
158 | val += (octave + 1) * 12;
159 | // only convert to float if absolutely necessary (floats introduce round off error)
160 | if (quarterSteps % 2 == 0) {
161 | return Atom.newAtom(val + quarterSteps / 2);
162 | }
163 | else {
164 | return Atom.newAtom(val + quarterSteps / 2.0f);
165 | }
166 | }
167 | catch (NumberFormatException e) {
168 | return Atom.newAtom(text);
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/ajm/util/DummyMaxObject.java:
--------------------------------------------------------------------------------
1 | package ajm.util;
2 |
3 | import com.cycling74.max.MaxObject;
4 |
5 | /**
6 | * For Unit Testing
7 | */
8 | public class DummyMaxObject extends MaxObject {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/ajm/util/FileWatcher.java:
--------------------------------------------------------------------------------
1 | package ajm.util;
2 |
3 | /*
4 | Copyright (c) 2008-2010, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.io.File;
31 |
32 | import com.cycling74.max.Executable;
33 |
34 | /**
35 | * File watch mechanism for Max objects. Executes a callback when the file modified date changes.
36 | *
37 | * @author Adam Murray (adam@compusition.com)
38 | */
39 |
40 | public class FileWatcher extends Thread {
41 |
42 | public final static long DEFAULT_WATCH_PERIOD = 2500; // 2.5 seconds, for no particular reason
43 |
44 | private File file;
45 |
46 | private long prevLastModified;
47 |
48 | private long watchPeriod;
49 |
50 | private Executable callback;
51 |
52 | private volatile boolean threadPaused = false;
53 |
54 | private Thread thisThread;
55 |
56 | public FileWatcher(File file, Executable callback) {
57 | this(file, callback, DEFAULT_WATCH_PERIOD);
58 | }
59 |
60 | public FileWatcher(File file, Executable callback, long watchPeriod) {
61 | setFile(file);
62 | setCallback(callback);
63 | setWatchPeriod(watchPeriod);
64 | }
65 |
66 | public void setFile(File file) {
67 | this.file = file;
68 | if (file == null) {
69 | prevLastModified = -1;
70 | }
71 | else {
72 | prevLastModified = file.lastModified();
73 | }
74 | }
75 |
76 | public File getFile() {
77 | return file;
78 | }
79 |
80 | public long getWatchPeriod() {
81 | return watchPeriod;
82 | }
83 |
84 | public void setWatchPeriod(long watchPeriod) {
85 | this.watchPeriod = watchPeriod;
86 | }
87 |
88 | public Executable getCallback() {
89 | return callback;
90 | }
91 |
92 | public void setCallback(Executable callback) {
93 | this.callback = callback;
94 | }
95 |
96 | public void run() {
97 | thisThread = Thread.currentThread();
98 | while (true) {
99 | if (file != null) {
100 | long currLastMod = file.lastModified();
101 | // System.out.println("Last modified = " + currLastMod);
102 | if (currLastMod > prevLastModified) {
103 | try {
104 | callback.execute();
105 | } catch (Exception e) {
106 | if (e.getMessage() != null) {
107 | // not sure why the message would be null, but I've seen it happen occasionally
108 | System.err.println(e.getMessage());
109 | }
110 | }
111 | prevLastModified = currLastMod;
112 | }
113 | try {
114 | Thread.sleep(watchPeriod);
115 | if (threadPaused) {
116 | synchronized (this) {
117 | while (threadPaused)
118 | wait(); // until notify();
119 | }
120 | }
121 | } catch (InterruptedException e) {
122 | // System.err.println("FileWatcher interrupted (harmless, but probably shouldn't have happened)");
123 | // e.printStackTrace();
124 | }
125 | }
126 | }
127 | }
128 |
129 | public synchronized void stopWatching() {
130 | pauseWatching();
131 | }
132 |
133 | public synchronized void pauseWatching() {
134 | threadPaused = true;
135 | }
136 |
137 | public synchronized void resumeWatching() {
138 | threadPaused = false;
139 | thisThread.notify(); // resume from wait();
140 | }
141 |
142 | public synchronized boolean isPaused() {
143 | return threadPaused;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/ajm/util/GlobalVariableStore.java:
--------------------------------------------------------------------------------
1 | package ajm.util;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.util.HashMap;
31 | import java.util.Map;
32 | import java.util.Set;
33 |
34 | /**
35 | * A singleton map for sharing global variables across any object in the JVM.
36 | *
37 | * @author Adam Murray (adam@compusition.com)
38 | */
39 | public class GlobalVariableStore {
40 |
41 | private static GlobalVariableStore instance;
42 |
43 | private GlobalVariableStore() {
44 | }
45 |
46 | public synchronized static GlobalVariableStore getInstance() {
47 | if (instance == null) {
48 | instance = new GlobalVariableStore();
49 | }
50 | return instance;
51 | }
52 |
53 | private Map variableStore = new HashMap();
54 |
55 | public synchronized Object get(String name) {
56 | return variableStore.get(name);
57 | }
58 |
59 | public synchronized Object set(String name, Object value) {
60 | return variableStore.put(name, value);
61 | }
62 |
63 | public synchronized boolean delete(String name) {
64 | Object value = variableStore.remove(name);
65 | return value != null;
66 | }
67 |
68 | public synchronized boolean defined(String name) {
69 | return variableStore.containsKey(name);
70 | }
71 |
72 | public synchronized Set names() {
73 | return variableStore.keySet();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/ajm/util/LineBuilder.java:
--------------------------------------------------------------------------------
1 | package ajm.util;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | /**
31 | * Builds text one line at a time.
32 | *
33 | * @author Adam Murray (adam@compusition.com)
34 | */
35 | public class LineBuilder implements CharSequence {
36 |
37 | private StringBuilder lines = new StringBuilder();
38 |
39 | public void line(String s) {
40 | lines.append(s).append("\n");
41 | }
42 |
43 | public void append(String s) {
44 | lines.append(s);
45 | }
46 |
47 | public String toString() {
48 | return lines.toString();
49 | }
50 |
51 | public boolean isEmpty() {
52 | return lines.length() == 0;
53 | }
54 |
55 | public int length() {
56 | return lines.length();
57 | }
58 |
59 | public void clear() {
60 | lines.setLength(0);
61 | }
62 |
63 | public char charAt(int index) {
64 | return lines.charAt(index);
65 | }
66 |
67 | public CharSequence subSequence(int start, int end) {
68 | return lines.subSequence(start, end);
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/ajm/util/Logger.java:
--------------------------------------------------------------------------------
1 | package ajm.util;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | /**
31 | * Simple logging interface.
32 | *
33 | * @author Adam Murray (adam@compusition.com)
34 | */
35 | public interface Logger {
36 |
37 | void debug(String message);
38 |
39 | void info(String message);
40 |
41 | void err(String message);
42 | }
43 |
--------------------------------------------------------------------------------
/src/ajm/util/MappedSet.java:
--------------------------------------------------------------------------------
1 | package ajm.util;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.util.LinkedHashSet;
31 | import java.util.Set;
32 | import java.util.TreeMap;
33 |
34 | /**
35 | * A Map where all the values are a Set. The Set maintains insertion order of its entires.
36 | *
37 | * @author Adam Murray (adam@compusition.com)
38 | */
39 | @SuppressWarnings("serial")
40 | public class MappedSet extends TreeMap> {
41 |
42 | public Set addValue(K key, T value) {
43 | Set values = get(key);
44 | if (values == null) {
45 | // LinkedHashSet maintains insertion order
46 | values = new LinkedHashSet();
47 | put(key, values);
48 | }
49 | values.add(value);
50 | return values;
51 | }
52 |
53 | @SuppressWarnings("unchecked")
54 | public MappedSet clone() {
55 | return (MappedSet) super.clone();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/ajm/util/TextBlock.java:
--------------------------------------------------------------------------------
1 | package ajm.util;
2 |
3 | /*
4 | Copyright (c) 2008, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.util.HashMap;
31 | import java.util.Map;
32 |
33 | /**
34 | * A singleton Map of Strings. Used to indirectly pass long messages in Max.
35 | *
36 | * @author Adam Murray (adam@compusition.com)
37 | */
38 | public class TextBlock {
39 |
40 | private static Map textMap = new HashMap();
41 |
42 | private TextBlock() {
43 | }
44 |
45 | public synchronized static void set(String name, String text) {
46 | textMap.put(name, text);
47 | }
48 |
49 | public synchronized static String get(String name) {
50 | return textMap.get(name);
51 | }
52 |
53 | public synchronized static void remove(String name) {
54 | textMap.remove(name);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/ajm/util/TextViewer.java:
--------------------------------------------------------------------------------
1 | package ajm.util;
2 |
3 | /*
4 | Copyright (c) 2008-2009, Adam Murray (adam@compusition.com). All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 | */
29 |
30 | import java.awt.BorderLayout;
31 | import java.awt.Dimension;
32 | import java.awt.Font;
33 | import java.awt.Toolkit;
34 |
35 | import javax.swing.JFrame;
36 | import javax.swing.JScrollPane;
37 | import javax.swing.JTextArea;
38 | import javax.swing.SwingUtilities;
39 |
40 | /**
41 | * A Swing-based popup window for viewing text.
42 | *
43 | * @author Adam Murray (adam@compusition.com)
44 | */
45 | public class TextViewer {
46 |
47 | private JFrame frame;
48 | private JTextArea textArea;
49 | private boolean packed = false;
50 | private int width;
51 | private int height;
52 | Dimension dim;
53 |
54 | public TextViewer(String name) {
55 | frame = new JFrame(name);
56 | frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
57 | width = 600;
58 | height = 450;
59 |
60 | textArea = new JTextArea();
61 | textArea.setEditable(false);
62 | textArea.setFont(new Font("Monospaced", Font.PLAIN, 11));
63 |
64 | JScrollPane scroller = new JScrollPane(textArea);
65 | scroller.setPreferredSize(new Dimension(width, height));
66 |
67 | frame.add(scroller, BorderLayout.CENTER);
68 | }
69 |
70 | public void show() {
71 | SwingUtilities.invokeLater(new Runnable() {
72 | public void run() {
73 | if(!packed) {
74 | frame.pack();
75 | packed = true;
76 | }
77 | frame.setVisible(true);
78 | }
79 | });
80 | }
81 |
82 | public void hide() {
83 | frame.setVisible(false);
84 | }
85 |
86 | public void setText(String text) {
87 | textArea.setText(text);
88 | }
89 |
90 | private int WINDOW_PADDING = 10;
91 | public void setCenter(int x, int y) {
92 | x -= width/2;
93 | y -= height/2;
94 |
95 | if(x < WINDOW_PADDING) x = WINDOW_PADDING;
96 | if(y < WINDOW_PADDING) y = WINDOW_PADDING;
97 |
98 | Toolkit toolkit = Toolkit.getDefaultToolkit();
99 | Dimension resolution = toolkit.getScreenSize();
100 | int maxX = resolution.width - width - WINDOW_PADDING;
101 | int maxY = resolution.height - height - WINDOW_PADDING;
102 | if(x > maxX) x = maxX;
103 | if(y > maxY) y = maxY;
104 |
105 | frame.setLocation(x,y);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------