rtmidi 0.2.2 → 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 951dc43b29adbc7d76f79f191f1e4a9ad6c034fd
4
- data.tar.gz: 4733255fee9f6e321cc690594afafa8fa9e520af
3
+ metadata.gz: 3c430949e32e04bb62083aae6879c0b20dad223e
4
+ data.tar.gz: 81ab70ee2a3fe78349ed6de2542fea7e42ca3bfc
5
5
  SHA512:
6
- metadata.gz: 9777c38f05c57c90f63c731279cb0c3d4ca3064049758d4c28c21bcd8a458900a0d51c33fff557d449bd4d7fdbfadcd6d1ecd7b4aa324f98ece78af57dc3849b
7
- data.tar.gz: 897d148e298d4b9d76ebf953234bfe35bc5e3c76457f4395e8728cf519dacc77400a8d56375b68afbb30499b07e3c0b5587ddb45a525e1c699d74e4a9059e17d
6
+ metadata.gz: 55647eec74cde698b047ad3abea4b3b2c88d8b3f42ca318e05106bd4ceaab9191da0194873b8c16675bc78e6bc0a4f77dd6a19207204261d39f1259e1bb9b690
7
+ data.tar.gz: 7953645285988a046841e8bf4a75e403d1919b3c95640bbdfaa59db64b7934fc21cacba08bff03e24380569a54e367c5daab7ce346df0d870462c406574f6c4f
data/README.md CHANGED
@@ -1,24 +1,25 @@
1
1
  Ruby-RtMidi
2
2
  ===========
3
3
 
4
+ [![Build Status](https://travis-ci.org/adamjmurray/ruby-rtmidi.png)](http://travis-ci.org/adamjmurray/ruby-rtmidi)
5
+
4
6
  [Ruby](http://www.ruby-lang.org/) wrapper for [RtMidi](http://www.music.mcgill.ca/~gary/rtmidi/index.html),
5
7
  a cross-platform C++ library for realtime MIDI input and output.
6
8
 
7
9
  Features:
8
10
 
9
11
  * List MIDI I/O ports
10
- * Send 3-byte MIDI messages to output ports
11
- * Listen for 3-byte messages on input ports
12
+ * Send MIDI messages to output ports
13
+ * Receive messages on input ports
12
14
 
13
- In other words, it can handle [channel messages](http://www.cs.cf.ac.uk/Dave/Multimedia/node158.html)
14
- (notes, control change, pitch bend, pressure, program),
15
- but there is no support for [SySex](https://en.wikipedia.org/wiki/SysEx#System_Exclusive_messages) messages yet.
15
+ In other words, everything you'd want from a low-level MIDI library.
16
+ It's still your responsibility to interpret the MIDI message byte streams!
16
17
 
17
18
  Supported Platforms:
18
19
 
19
20
  * OS X
20
21
  * Windows
21
- * Linux with [JACK](http://jackaudio.org/) or [ALSA](http://www.alsa-project.org)
22
+ * Linux
22
23
 
23
24
 
24
25
  Requirements
@@ -54,8 +55,14 @@ Windows Setup
54
55
  * MinGW Developer ToolKit
55
56
  * Use the the MinGW Shell (MSYS) to install
56
57
 
57
- Linux Setup (Ubuntu)
58
- --------------------
58
+ Note: when installing under MinGW, this library may not work outside of MinGW. If that is a problem for you, use Visual Studio to install.
59
+
60
+ Linux Setup
61
+ -----------
62
+
63
+ Install [JACK](http://jackaudio.org/) or [ALSA](http://www.alsa-project.org).
64
+
65
+ This should work on Ubuntu:
59
66
 
60
67
  sudo apt-get install g++
61
68
  sudo apt-get install jackd
@@ -75,19 +82,39 @@ Usage
75
82
 
76
83
  See the following examples:
77
84
 
78
- * [List MIDI devices](http://rdoc.info/github/adamjmurray/ruby-rtmidi/file/examples/list_ports.rb)
79
- * [MIDI output](http://rdoc.info/github/adamjmurray/ruby-rtmidi/file/examples/play_notes.rb)
80
- * [MIDI input](http://rdoc.info/github/adamjmurray/ruby-rtmidi/file/examples/monitor_input.rb)
85
+ * [list MIDI devices](http://rdoc.info/github/adamjmurray/ruby-rtmidi/file/examples/list_ports.rb)
86
+ * [MIDI channel output](http://rdoc.info/github/adamjmurray/ruby-rtmidi/file/examples/send_channel_message.rb)
87
+ * [MIDI channel input](http://rdoc.info/github/adamjmurray/ruby-rtmidi/file/examples/receive_channel_input.rb)
88
+ * [arbitrary MIDI output](http://rdoc.info/github/adamjmurray/ruby-rtmidi/file/examples/send_any_message.rb)
89
+ * [arbitrary MIDI input](http://rdoc.info/github/adamjmurray/ruby-rtmidi/file/examples/receive_any_input.rb)
90
+
91
+ Use the arbitrary MIDI IO to handle channel messages, SysEx, timing, and/or active sensing messages.
92
+ If you only need channel messages (notes, modulation/CC, pitch bend, aftertouch), it's recommended you
93
+ follow the channel IO examples.
81
94
 
82
95
 
83
96
  Documentation
84
97
  =============
98
+
85
99
  [http://rdoc.info/github/adamjmurray/ruby-rtmidi/frames](http://rdoc.info/github/adamjmurray/ruby-rtmidi/frames)
86
100
 
87
101
 
102
+ Contributing
103
+ ============
104
+
105
+ Pull requests are welcome. The following must work:
106
+
107
+ * `rake test` shows all unit tests are passing
108
+ * Build and test the gem manually:
109
+ * `gem build rtmidi.gemspec`
110
+ * `gem install rtmidi-#{version}.gem`
111
+ * the examples can be run successfully against this version of the gem (`ruby examples/**`)
112
+
113
+
88
114
  Changelog
89
115
  =========
90
116
 
117
+ * 0.3 - Support for arbitrary MIDI messages including SysEx
91
118
  * 0.2.2 - Compilable with Visual Studio on Windows
92
119
  * 0.2.1 - Linux support (thanks to [@quark-zju](https://github.com/quark-zju))
93
120
  * 0.2 - First stable release
data/Rakefile CHANGED
@@ -1,10 +1,27 @@
1
- load File.expand_path('ext/Rakefile', File.dirname(__FILE__))
1
+ require 'rake/clean'
2
+
3
+ CLEAN.include %w[ **/*.o **/*.lib **/*.obj **/*.log ]
4
+ CLOBBER.include %w[ **/*.so **/*.dll ]
5
+
2
6
 
3
- desc 'remove artifacts produced by make task'
4
- task :clean do
5
- for file in Dir["#{RTMIDI_DIR}/*.o", "#{EXT_DIR}/*.o","#{EXT_DIR}/*.so"]
6
- puts "Deleting #{file}"
7
- File.unlink(file)
7
+ require 'rspec/core/rake_task'
8
+
9
+ desc "Run tests"
10
+ RSpec::Core::RakeTask.new('test') do |spec|
11
+ spec.rspec_opts = ["--color", "--format", "nested"]
12
+ if ARGV[1]
13
+ # only run specs with filenames starting with the command line argument
14
+ spec.pattern = "spec/**/#{ARGV[1]}*"
8
15
  end
9
- puts
10
16
  end
17
+
18
+ task :spec => :test # alias test task as spec task
19
+
20
+
21
+ load File.expand_path('ext/Rakefile', File.dirname(__FILE__))
22
+
23
+
24
+ desc 'Build the gem'
25
+ task :gem do
26
+ system 'gem build rtmidi.gemspec'
27
+ end
@@ -4,12 +4,12 @@ puts
4
4
 
5
5
  midiin = RtMidi::In.new
6
6
  puts "Available MIDI input ports"
7
- midiin.port_names.each_with_index{|name,index| puts " ##{index+1}: #{name}" }
7
+ midiin.port_names.each_with_index{|name,index| puts " #{index+1}: #{name}" }
8
8
 
9
9
  puts
10
10
 
11
11
  midiout = RtMidi::Out.new
12
12
  puts "Available MIDI output ports"
13
- midiout.port_names.each_with_index{|name,index| puts " ##{index+1}: #{name}" }
13
+ midiout.port_names.each_with_index{|name,index| puts " #{index+1}: #{name}" }
14
14
 
15
15
  puts
@@ -0,0 +1,35 @@
1
+ require "rtmidi"
2
+
3
+ midiin = RtMidi::In.new
4
+
5
+
6
+ ##############################################################################
7
+ # Boilerplate code for selecting a MIDI port
8
+
9
+ puts "Available MIDI input ports"
10
+ midiin.port_names.each_with_index{|name,index| printf "%3i: %s\n", index, name }
11
+
12
+ def select_port(midiio)
13
+ print "Select a port number: "
14
+ if (port = gets) =~ /^\d+$/
15
+ return port.to_i if (0...midiio.port_count).include? port.to_i
16
+ end
17
+ puts "Invalid port number"
18
+ end
19
+
20
+ port_index = select_port(midiin) until port_index
21
+
22
+ ##############################################################################
23
+ # Use this approach when you need to receive any message including:
24
+ # System Exclusive (SysEx), timing, active sensing
25
+
26
+ midiin.receive_message do |*bytes|
27
+ puts bytes.inspect
28
+ end
29
+
30
+ puts "Receiving MIDI messages including SysEx..."
31
+ puts "Ctrl+C to exit"
32
+
33
+ midiin.open_port(port_index)
34
+
35
+ sleep # prevent Ruby from exiting immediately
@@ -0,0 +1,35 @@
1
+ require "rtmidi"
2
+
3
+ midiin = RtMidi::In.new
4
+
5
+
6
+ ##############################################################################
7
+ # Boilerplate code for selecting a MIDI port
8
+
9
+ puts "Available MIDI input ports"
10
+ midiin.port_names.each_with_index{|name,index| printf "%3i: %s\n", index, name }
11
+
12
+ def select_port(midiio)
13
+ print "Select a port number: "
14
+ if (port = gets) =~ /^\d+$/
15
+ return port.to_i if (0...midiio.port_count).include? port.to_i
16
+ end
17
+ puts "Invalid port number"
18
+ end
19
+
20
+ port_index = select_port(midiin) until port_index
21
+
22
+ ##############################################################################
23
+ # Use this approach when you only need to receive channel message like:
24
+ # MIDI notes, modulation/CC, pitch bend, aftertouch
25
+
26
+ midiin.receive_channel_message do |byte1, byte2, byte3|
27
+ puts "#{byte1} #{byte2} #{byte3}"
28
+ end
29
+
30
+ puts "Receiving MIDI channel messages..."
31
+ puts "Ctrl+C to exit"
32
+
33
+ midiin.open_port(port_index)
34
+
35
+ sleep # prevent Ruby from exiting immediately
@@ -0,0 +1,31 @@
1
+ require "rtmidi"
2
+
3
+ midiout = RtMidi::Out.new
4
+
5
+
6
+ ##############################################################################
7
+ # Boilerplate code for selecting a MIDI port
8
+
9
+ puts "Available MIDI output ports"
10
+ midiout.port_names.each_with_index{|name,index| printf "%3i: %s\n", index, name }
11
+
12
+ def select_port(midiio)
13
+ print "Select a port number: "
14
+ if (port = gets) =~ /^\d+$/
15
+ return port.to_i if (0...midiio.port_count).include? port.to_i
16
+ end
17
+ puts "Invalid port number"
18
+ end
19
+
20
+ port_index = select_port(midiout) until port_index
21
+
22
+ ##############################################################################
23
+ # Use this approach when you need to send any message including:
24
+ # System Exclusive (SysEx), timing, active sensing
25
+
26
+ midiout.open_port(port_index)
27
+
28
+ # Now send some SysEx messages
29
+ midiout.send_message(240, 67, 16, 0, 16, 34, 247)
30
+ # or if you prefer a single array argument:
31
+ midiout.send_message( [240, 67, 16, 0, 16, 35, 247] )
@@ -0,0 +1,34 @@
1
+ require "rtmidi"
2
+
3
+ midiout = RtMidi::Out.new
4
+
5
+
6
+ ##############################################################################
7
+ # Boilerplate code for selecting a MIDI port
8
+
9
+ puts "Available MIDI output ports"
10
+ midiout.port_names.each_with_index{|name,index| printf "%3i: %s\n", index, name }
11
+
12
+ def select_port(midiio)
13
+ print "Select a port number: "
14
+ if (port = gets) =~ /^\d+$/
15
+ return port.to_i if (0...midiio.port_count).include? port.to_i
16
+ end
17
+ puts "Invalid port number"
18
+ end
19
+
20
+ port_index = select_port(midiout) until port_index
21
+
22
+ ##############################################################################
23
+ # Use this approach when you only need to send channel message like:
24
+ # MIDI notes, modulation/CC, pitch bend, aftertouch
25
+
26
+ midiout.open_port(port_index)
27
+
28
+ for pitch in [60, 62, 64, 65, 67]
29
+ midiout.send_channel_message(0x90, pitch, 127) # note on
30
+ sleep 0.5
31
+ midiout.send_channel_message(0x90, pitch, 0) # note off
32
+ end
33
+
34
+ sleep 0.5 # give the final note off time to release
@@ -1,97 +1,11 @@
1
- require 'rake/clean'
2
-
3
- EXT_DIR = File.expand_path(File.dirname __FILE__)
4
- RTMIDI_DIR = "#{EXT_DIR}/rtmidi-2.0.1"
5
-
6
- HOST_OS = RbConfig::CONFIG['host_os'].downcase
7
- OS_X = (HOST_OS =~ /darwin/)
8
- WINDOWS = ((HOST_OS =~ /win/ and HOST_OS !~ /darwin/) or HOST_OS =~ /mingw/)
9
- LINUX = (HOST_OS =~ /linux/)
10
-
11
- CLEAN.include('**/*.o', '*.log')
12
- CLOBBER.include('*.so')
13
-
14
- def cd(dir)
15
- puts "cd #{dir}"
16
- Dir.chdir(dir)
17
- end
18
-
19
- def run(cmd)
20
- puts cmd
21
- unless system(cmd)
22
- puts "Error: command exited with return value #{$?.exitstatus}"
23
- exit $?.exitstatus
24
- end
25
- end
26
-
27
-
28
- require "mkmf"
29
- COMPILER = if WINDOWS and find_executable "cl.exe"
30
- :cl
31
- else
32
- if find_executable "gcc" and find_executable "g++"
33
- :gcc
34
- else
35
- abort "Cannot find gcc/g++ #{'or cl.exe ' if WINDOWS}compiler"
36
- end
37
- end
38
-
39
- PREDEFINE, SYSTEM_LIBS = *case
40
- when OS_X then ["__MACOSX_CORE__", "-framework CoreMIDI -framework CoreAudio -framework CoreFoundation"]
41
- when WINDOWS then ["__WINDOWS_MM__", "-lwinmm"]
42
- when LINUX then
43
- defines, libs = '', ''
44
- {:alsa => '__LINUX_ALSA__', :jack => '__UNIX_JACK__'}.select do |pkg, _|
45
- system "pkg-config --exists #{pkg}"
46
- end.each do |pkg, macro|
47
- defines << "#{macro} "
48
- libs << `pkg-config --libs #{pkg}`.chomp
49
- end
50
- if defines.empty?
51
- raise 'Neither JACK or ALSA detected using pkg-config. Please install one of them first.'
52
- end
53
- [defines, libs]
54
- else
55
- end
56
-
57
-
58
- def compile_rtmidi
59
- cd RTMIDI_DIR
60
- if COMPILER == :gcc
61
- run "g++ -O3 -Wall -Iinclude -fPIC -D#{PREDEFINE} -o RtMidi.o -c RtMidi.cpp"
62
- else
63
- run "cl /O2 /Iinclude /D#{PREDEFINE} /EHsc /FoRtMidi.obj /c RtMidi.cpp"
64
- end
65
- end
66
-
67
- def compile_ruby_rtmidi_wrapper
68
- cd EXT_DIR
69
- if COMPILER == :gcc
70
- run "g++ -g -Wall -I#{RTMIDI_DIR} -fPIC -o ruby-rtmidi.o -c ruby-rtmidi.cpp"
71
- else
72
- run "cl /I#{RTMIDI_DIR} /D__RUBY_RTMIDI_DLL__ /EHsc /Foruby-rtmidi.obj /c ruby-rtmidi.cpp"
73
- end
74
- end
75
-
76
- def create_shared_library
77
- cd EXT_DIR
78
- if COMPILER == :gcc
79
- run "g++ -g -Wall -I#{RTMIDI_DIR} -I#{RTMIDI_DIR}/include -D#{PREDEFINE} -fPIC -shared -o ruby-rtmidi.so " +
80
- "ruby-rtmidi.o #{RTMIDI_DIR}/RtMidi.o #{SYSTEM_LIBS}"
81
- else
82
- run "cl /I#{RTMIDI_DIR} /I#{RTMIDI_DIR}/include /D#{PREDEFINE} /LD ruby-rtmidi.obj #{RTMIDI_DIR}/RtMidi.obj winmm.lib"
83
- end
84
- end
85
-
86
-
87
- desc 'run the make task'
1
+ desc 'Run the make task'
88
2
  task :default => :make
89
3
 
90
- desc 'build the RtMidi C++ library with Ruby FFI wrapper'
4
+ desc 'Compile the RtMidi C++ library with Ruby FFI wrapper'
91
5
  task :make do
92
- compile_rtmidi
93
- puts
94
- compile_ruby_rtmidi_wrapper
95
- puts
96
- create_shared_library
6
+ require_relative '../lib/rtmidi/build/compiler'
7
+ ext_dir = File.expand_path(File.dirname __FILE__)
8
+ rtmidi_dir = "#{ext_dir}/rtmidi-2.0.1"
9
+ compiler = RtMidi::Build::Compiler.new(ext_dir, rtmidi_dir, verbose: true)
10
+ compiler.compile
97
11
  end
@@ -67,6 +67,21 @@ void midiin_set_callback(rtmidi_ptr p, rtmidi_callback callback) {
67
67
  midiin->setCallback(midiin_callback_proxy, (void *)callback);
68
68
  }
69
69
 
70
+ void midiin_varargs_callback_proxy( double deltatime, std::vector< unsigned char > *message, void *userData )
71
+ {
72
+ unsigned int byte_count = message->size();
73
+ // for ( unsigned int i=0; i<byte_count; i++ )
74
+ // std::cout << "Byte " << i << " = " << (int)message->at(i) << ", ";
75
+ // if ( byte_count > 0 )
76
+ // std::cout << "stamp = " << deltatime << std::endl; # TODO: do we care about the deltatime?
77
+ ((rtmidi_varargs_callback)userData)(&(*message)[0], byte_count);
78
+ }
79
+
80
+ void midiin_set_varargs_callback(rtmidi_ptr p, rtmidi_varargs_callback callback) {
81
+ RtMidiIn *midiin = static_cast<RtMidiIn *>(p);
82
+ midiin->setCallback(midiin_varargs_callback_proxy, (void *)callback);
83
+ }
84
+
70
85
  void midiin_cancel_callback(rtmidi_ptr p) {
71
86
  RtMidiIn *midiin = static_cast<RtMidiIn *>(p);
72
87
  midiin->cancelCallback();
@@ -117,3 +132,12 @@ void midiout_send_message(rtmidi_ptr p, int byte1, int byte2, int byte3) {
117
132
  message[2] = byte3;
118
133
  midiout->sendMessage(&message);
119
134
  }
135
+
136
+ void midiout_send_bytes(rtmidi_ptr p, int* bytes, int byte_count) {
137
+ RtMidiOut *midiout = static_cast<RtMidiOut *>(p);
138
+ std::vector<unsigned char> message;
139
+ for(int i=0; i<byte_count; i++) {
140
+ message.push_back(bytes[i]);
141
+ }
142
+ midiout->sendMessage(&message);
143
+ }
@@ -8,6 +8,7 @@ extern "C"
8
8
  {
9
9
  typedef void* rtmidi_ptr;
10
10
  typedef void (*rtmidi_callback)(int byte1, int byte2, int byte3);
11
+ typedef void (*rtmidi_varargs_callback)(unsigned char* bytes, int byte_count);
11
12
 
12
13
  //================================================
13
14
  // INPUT
@@ -28,6 +29,8 @@ extern "C"
28
29
 
29
30
  DLL_EXPORT void midiin_set_callback(rtmidi_ptr p, rtmidi_callback callback);
30
31
 
32
+ DLL_EXPORT void midiin_set_varargs_callback(rtmidi_ptr p, rtmidi_varargs_callback callback);
33
+
31
34
  DLL_EXPORT void midiin_cancel_callback(rtmidi_ptr p);
32
35
 
33
36
 
@@ -47,4 +50,6 @@ extern "C"
47
50
  DLL_EXPORT void midiout_close_port(rtmidi_ptr p);
48
51
 
49
52
  DLL_EXPORT void midiout_send_message(rtmidi_ptr p, int byte1, int byte2, int byte3);
53
+
54
+ DLL_EXPORT void midiout_send_bytes(rtmidi_ptr p, int* bytes, int byte_count);
50
55
  };