portmidi 0.0.1 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  pkg/
2
- *.gemspec
2
+ *.gemspec
3
+ rdoc/
@@ -0,0 +1,63 @@
1
+ = Portmidi-Mapper
2
+
3
+ This is a small, incomplete wrapper around the portmidi library.
4
+
5
+ It is the successor of my portmidi-ruby project which used ordinary ruby extensions which are a pain to write.
6
+
7
+ Author: Jan 'half/byte' Krutisch <jan@krutisch.de>
8
+
9
+ Uses Portmidi, written by Roger B. Dannenberg.
10
+
11
+ == Requirements
12
+
13
+
14
+ * portmidi (who woulda thought) built as a dynamic library.
15
+ * ffi (which in turn needs a sane build env)
16
+
17
+ == What's missing?
18
+
19
+ The most important: Tests. I'm still pondering on how to test a library that
20
+ has hardware requirements and dependencies (= MIDI hardware). So I guess it
21
+ means I will mock away the portmidi layer to test my abstractions and add some optional integration test that will require a MIDI loopback configuration.
22
+
23
+ Implementation wise, there's a few things missing:
24
+
25
+ * Setting Channel- and Message filters
26
+ * Porttime support(not sure I can support that, I guess it needs multithreading, plus: I did not need it until now)
27
+ examples/read_test.rb on how to decipher sysex atm.
28
+ * More different exceptions, plus support for Host Errors
29
+ * A lot of safety nets like testing for open devices before reading and writing and such.
30
+
31
+ == What's coming
32
+
33
+ * I'd love to see some explicit support for correctly parsing SysEx, see
34
+
35
+
36
+ == Installing Portmidi on OS X Snow leopard
37
+
38
+ This is a major pita, because currently portmidi doesn't officially build for 64 bit, which
39
+ makes it impossible to use with the default Snow Leopard Ruby.
40
+
41
+ You can try to enable 64 bit support, but I don't have a frekkin clue what this means. It works
42
+ for me and my application, but YMMV and I am not responsible for any damage this does
43
+ to you or your system.
44
+
45
+ To enable 64 bit compilation (you will see quite a few warnings while compiling), open
46
+ CMakeLists.txt in the trunk folder and find the following line (around line 39 in my version)
47
+
48
+ set(CMAKE_OSX_ARCHITECTURES i386 ppc CACHE STRING "do not build for 64-bit" FORCE)
49
+
50
+ and change it to: (yes, the comment is a bit silly)
51
+
52
+ set(CMAKE_OSX_ARCHITECTURES i386 ppc x86_64 CACHE STRING "do build for 64-bit" FORCE)
53
+
54
+ Now you can call
55
+
56
+ make -f pm_mac/Makefile.osx
57
+
58
+ if it builds, you can try to install the library. Currently the process seems to be a bit broken,
59
+ so you actually need to type:
60
+
61
+ sudo env PF=/usr make -f pm_mac/Makefile.osx install
62
+
63
+ yes, this sucks - I'm currently trying to find out with the portmidi authors what it would take to fix all this.
data/Rakefile CHANGED
@@ -1,4 +1,7 @@
1
+ require 'rubygems'
2
+ require 'rake'
1
3
  require File.join(File.dirname(__FILE__), "lib/portmidi/version")
4
+
2
5
  begin
3
6
  require 'jeweler'
4
7
 
@@ -16,4 +19,14 @@ begin
16
19
  Jeweler::GemcutterTasks.new
17
20
  rescue LoadError
18
21
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
22
+ end
23
+
24
+ require 'rake/rdoctask'
25
+ Rake::RDocTask.new do |rdoc|
26
+ version = Portmidi::VERSION
27
+
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = "Portmidi #{version}"
30
+ rdoc.rdoc_files.include('README*')
31
+ rdoc.rdoc_files.include('lib/**/*.rb')
19
32
  end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'portmidi'
4
+
5
+ Portmidi.start
@@ -0,0 +1,47 @@
1
+ require 'helper'
2
+ Portmidi.input_devices.each do |dev|
3
+ puts "%d > %s" % [dev.device_id, dev.name]
4
+ end
5
+ puts "choose device id"
6
+ device_id = gets()
7
+
8
+ input = Portmidi::Input.new(device_id.to_i)
9
+ cnt = 0
10
+ data = []
11
+ running_sysex = []
12
+ sysex_is_on = false
13
+ loop do
14
+ begin
15
+ events = input.read(16)
16
+ if events
17
+ events.each do |event|
18
+
19
+
20
+ if event[:message][0] == 0xF0
21
+ running_sysex = event[:message]
22
+ sysex_is_on = true
23
+ elsif sysex_is_on
24
+ if event[:message][0] < 0x80
25
+ running_sysex += event[:message]
26
+ if event[:message].include?(0xF7)
27
+ data << running_sysex.flatten
28
+ puts running_sysex.inspect
29
+ sysex_is_on = false
30
+ end
31
+ else
32
+ puts event[:message].inspect
33
+ sysex_is_on = false
34
+ puts "cancelled sysex"
35
+ end
36
+ end
37
+ end
38
+ # data << events
39
+ else
40
+ # puts "poll" if cnt % 10000 == 0
41
+ end
42
+ rescue Portmidi::DeviceError => e
43
+ puts e.portmidi_error
44
+ end
45
+ cnt += 1
46
+ #sleep 0.002
47
+ end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+ sysex = [0xF0, 00, 12, 0x12, 0xF7]
3
+
4
+ Portmidi.output_devices.each do |dev|
5
+ puts "%d > %s" % [dev.device_id, dev.name]
6
+ end
7
+ puts "choose device id"
8
+ device_id = gets()
9
+
10
+ output = Portmidi::Output.new(device_id.to_i)
11
+ output.write_sysex(sysex)
@@ -0,0 +1,14 @@
1
+ require 'helper'
2
+ messages = [
3
+ {:message => [0x90, 0x33, 0x7F], :timestamp => 0},
4
+ {:message => [0x90, 0x33, 0x00], :timestamp => 0}
5
+ ]
6
+
7
+ Portmidi.output_devices.each do |dev|
8
+ puts "%d > %s" % [dev.device_id, dev.name]
9
+ end
10
+ puts "choose device id"
11
+ device_id = gets()
12
+
13
+ output = Portmidi::Output.new(device_id.to_i)
14
+ output.write(messages)
@@ -7,13 +7,20 @@ require 'portmidi/exceptions'
7
7
 
8
8
  module Portmidi
9
9
 
10
- def self.devices
11
- devices = []
10
+ def self.devices(rescan = false)
11
+ @@devices = []
12
12
  PM_Map.Pm_CountDevices.times do |i|
13
13
  di = PM_Map::DeviceInfo.new(PM_Map.Pm_GetDeviceInfo(i))
14
- devices << Device.new(i, di[:input], di[:output], di[:name])
14
+ @@devices << Device.new(i, di[:input], di[:output], di[:name])
15
15
  end
16
- devices
16
+ @@devices
17
+ end
18
+
19
+ def self.input_devices
20
+ self.devices.select{|device| device.type == :input }
21
+ end
22
+ def self.output_devices
23
+ self.devices.select{|device| device.type == :output }
17
24
  end
18
25
 
19
26
  # this is not a very good name, but Portmidi::initialize woulda been a worse idea
@@ -8,7 +8,7 @@ module Portmidi
8
8
  @name = name
9
9
  end
10
10
 
11
- def open(buffer_size = 4, latency = 0)
11
+ def open(buffer_size = nil, latency = nil)
12
12
  case(@type)
13
13
  when :input
14
14
  Input.new(@device_id, buffer_size)
@@ -1,7 +1,9 @@
1
1
  module Portmidi
2
2
  class DeviceError < StandardError
3
+ attr_reader :portmidi_error
3
4
  def initialize(errnum)
4
- @full_message
5
+ @portmidi_error = PM_Map.Pm_GetErrorText(errnum)
5
6
  end
6
7
  end
8
+
7
9
  end
@@ -1,6 +1,7 @@
1
1
  module Portmidi
2
2
  class Input
3
- def initialize(device_id = 0, buffer_size = 4)
3
+ def initialize(device_id, buffer_size = 512)
4
+ puts buffer_size
4
5
  in_stream_ptr = FFI::MemoryPointer.new(:pointer)
5
6
  if (errnum = PM_Map.Pm_OpenInput(in_stream_ptr, device_id, nil, buffer_size, nil, nil)) == 0
6
7
  @in_stream = in_stream_ptr.read_pointer
@@ -10,10 +11,38 @@ module Portmidi
10
11
  end
11
12
 
12
13
  def poll
13
-
14
+ PM_Map.Pm_Poll(@in_stream) != 0
15
+ end
16
+
17
+ def read(buffer_size = 1)
18
+ event_pointer = FFI::MemoryPointer.new(PM_Map::Event, buffer_size)
19
+ read = PM_Map::Pm_Read(@in_stream, event_pointer, buffer_size);
20
+ if read > 0
21
+ events = []
22
+ read.times do |i|
23
+ events << parseEvent(PM_Map::Event.new(event_pointer[i]))
24
+ end
25
+ return events
26
+ elsif read < 0
27
+ raise DeviceError, read, "read failed"
28
+ else
29
+ return nil
30
+ end
14
31
  end
15
32
 
16
33
  # TODO: poll methods etc.
34
+ private
35
+ def parseEvent(event)
36
+ {
37
+ :message => [
38
+ ((event[:message]) & 0xFF),
39
+ (((event[:message]) >> 8) & 0xFF),
40
+ (((event[:message]) >> 16) & 0xFF),
41
+ (((event[:message]) >> 24) & 0xFF)
42
+ ],
43
+ :timestamp => event[:timestamp]
44
+ }
45
+ end
17
46
 
18
47
  end
19
48
  end
@@ -1,6 +1,10 @@
1
1
  module Portmidi
2
2
  class Output
3
- def initialize(device_id = 0, buffer_size = 4, latency = 0)
3
+
4
+ #
5
+ # open a device with the given ID
6
+ #
7
+ def initialize(device_id = 0, buffer_size = 0, latency = 0)
4
8
  out_stream_ptr = FFI::MemoryPointer.new(:pointer)
5
9
  if (errnum = PM_Map.Pm_OpenOutput(out_stream_ptr, device_id, nil, buffer_size, nil, nil, latency)) == 0
6
10
  @out_stream = out_stream_ptr.read_pointer
@@ -9,9 +13,42 @@ module Portmidi
9
13
  end
10
14
  end
11
15
 
16
+ #
17
+ # write a single three byte message
18
+ #
19
+
12
20
  def write_short(status, data1, data2)
13
21
  PM_Map.Pm_WriteShort(@out_stream, 0, PM_Map.encode_message(status, data1, data2))
14
22
  end
15
23
 
24
+ def write(messages)
25
+ buffer = FFI::MemoryPointer.new(PM_Map::Event, messages.length)
26
+ messages.length.times do |i|
27
+ event = PM_Map::Event.new(buffer[i])
28
+ event[:timestamp] = messages[i][:timestamp]
29
+ event[:message] = packEvent(messages[i][:message])
30
+ end
31
+ PM_Map.Pm_Write(@out_stream, buffer, messages.length)
32
+ end
33
+
34
+
35
+
36
+ #
37
+ # sends the sysex, the message should be an array of byte values
38
+ #
39
+ def write_sysex(sysex)
40
+ raise "Invalid Sysex Format" if (sysex.first != 0xF0 || sysex.last != 0xF7)
41
+ msg = FFI::Buffer.alloc_in(:uchar, sysex.length)
42
+ sysex.each_with_index do |sysx, i|
43
+ msg.put_uchar(i, sysx & 0xFF)
44
+ end
45
+ PM_Map.Pm_WriteSysEx(@out_stream, 0, msg)
46
+ end
47
+
48
+ private
49
+ def packEvent(message)
50
+ ((((message[3] || 0) << 24) & 0xFF000000) | (((message[2] || 0) << 16) & 0xFF0000) | (((message[1] || 0) << 8) & 0xFF00) | ((message[0]) & 0xFF))
51
+ end
52
+
16
53
  end
17
54
  end
@@ -13,7 +13,10 @@ module Portmidi
13
13
  attach_function :Pm_Poll, [:pointer], :int
14
14
  attach_function :Pm_Read, [:pointer, :pointer, :int], :int
15
15
  attach_function :Pm_WriteShort, [:pointer, :int32, :int32], :int
16
- attach_function :Pm_GetErrorText, [:int], :pointer
16
+ attach_function :Pm_Write, [:pointer, :pointer, :int32], :int
17
+ attach_function :Pm_WriteSysEx, [:pointer, :int32, :buffer_in], :int
18
+
19
+ attach_function :Pm_GetErrorText, [:int], :string
17
20
 
18
21
  #attach_function :Pt_Start, [:int, :pointer, :pointer], :int
19
22
 
@@ -28,7 +31,7 @@ module Portmidi
28
31
 
29
32
  class Event < FFI::Struct
30
33
  layout :message, :int32,
31
- :timestamp, :int32
34
+ :timestamp, :int32
32
35
  end
33
36
 
34
37
  def self.encode_message(status, data1, data2)
@@ -1,3 +1,3 @@
1
1
  module Portmidi
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,40 @@
1
+ /*
2
+ * PortMidi Portable Real-Time MIDI Library
3
+ *
4
+ * license.txt -- a copy of the PortMidi copyright notice and license information
5
+ *
6
+ * Latest version available at: http://sourceforge.net/projects/portmedia
7
+ *
8
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
9
+ * Copyright (c) 2001-2009 Roger B. Dannenberg
10
+ *
11
+ * Permission is hereby granted, free of charge, to any person obtaining
12
+ * a copy of this software and associated documentation files
13
+ * (the "Software"), to deal in the Software without restriction,
14
+ * including without limitation the rights to use, copy, modify, merge,
15
+ * publish, distribute, sublicense, and/or sell copies of the Software,
16
+ * and to permit persons to whom the Software is furnished to do so,
17
+ * subject to the following conditions:
18
+ *
19
+ * The above copyright notice and this permission notice shall be
20
+ * included in all copies or substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
26
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
+ */
30
+
31
+ /*
32
+ * The text above constitutes the entire PortMidi license; however,
33
+ * the PortMusic community also makes the following non-binding requests:
34
+ *
35
+ * Any person wishing to distribute modifications to the Software is
36
+ * requested to send the modifications to the original developer so that
37
+ * they can be incorporated into the canonical version. It is also
38
+ * requested that these non-binding requests be included along with the
39
+ * license above.
40
+ */
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'mocha'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'portmidi'
8
+
9
+ class Test::Unit::TestCase
10
+ def stub_portmidi
11
+ Portmidi::PM_Map.stubs(:CountDevices).returns(2)
12
+ Portmidi::PM_Map.stubs(:Pm_Initialize).returns(false);
13
+ Portmidi::PM_map.stubs(:Pm_GetDeviceInfo).returns({:input => 1, :output => 0, :name => 'TestDevice'})
14
+ Portmidi::PM_map.stubs(:Pm_OpenInput).returns(1)
15
+ Portmidi::PM_map.stubs(:Pm_OpenOutput).returns(2)
16
+ Portmidi::PM_map.stubs(:Pm_Poll).returns(1)
17
+ Portmidi::PM_map.stubs(:Pm_Poll).returns(1)
18
+ Portmidi::PM_map.stubs(:Pm_Read).returns(1)
19
+ Portmidi::PM_Map.stubs(:Pm_WriteShort).returns(1)
20
+ Portmidi::PM_Map.stubs(:Pm_GetErrorText).returns(1)
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ require 'helper'
2
+ class TestPortmidi < Test::Unit::TestCase
3
+
4
+
5
+ def test_buffering
6
+ flunk
7
+ end
8
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: portmidi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Krutisch
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-07 00:00:00 +01:00
12
+ date: 2009-11-08 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -29,11 +29,15 @@ executables: []
29
29
  extensions: []
30
30
 
31
31
  extra_rdoc_files:
32
- - README.markdown
32
+ - README.rdoc
33
33
  files:
34
34
  - .gitignore
35
- - README.markdown
35
+ - README.rdoc
36
36
  - Rakefile
37
+ - examples/helper.rb
38
+ - examples/read_test.rb
39
+ - examples/write_sysex_test.rb
40
+ - examples/write_test.rb
37
41
  - lib/portmidi.rb
38
42
  - lib/portmidi/device.rb
39
43
  - lib/portmidi/exceptions.rb
@@ -41,6 +45,9 @@ files:
41
45
  - lib/portmidi/output.rb
42
46
  - lib/portmidi/pm_map.rb
43
47
  - lib/portmidi/version.rb
48
+ - license.txt
49
+ - test/helper.rb
50
+ - test/test_portmidi.rb
44
51
  has_rdoc: true
45
52
  homepage: http://github.com/halfbyte/portmidi
46
53
  licenses: []
@@ -69,5 +76,10 @@ rubygems_version: 1.3.5
69
76
  signing_key:
70
77
  specification_version: 3
71
78
  summary: An ffi wrapper around the cross platform midi library portmidi
72
- test_files: []
73
-
79
+ test_files:
80
+ - test/helper.rb
81
+ - test/test_portmidi.rb
82
+ - examples/helper.rb
83
+ - examples/read_test.rb
84
+ - examples/write_sysex_test.rb
85
+ - examples/write_test.rb
@@ -1,41 +0,0 @@
1
- Portmidi-Mapper
2
- ===============
3
-
4
- This is a small, incomplete wrapper around the portmidi library.
5
-
6
- Requirements
7
- ============
8
-
9
- portmidi (who woulda thought) built as a dynamic library.
10
- ffi gem.
11
-
12
-
13
- Installing Portmidi on OS X Snow leopard
14
- ========================================
15
-
16
- This is a major pita, because currently portmidi doesn't officially build for 64 bit, which
17
- makes it impossible to use with the default Snow Leopard Ruby.
18
-
19
- You can try to enable 64 bit support, but I don't have a frekkin clue what this means. It works
20
- for me and my application, but YMMV and I am not responsible for any damage this does
21
- to you or your system.
22
-
23
- To enable 64 bit compilation (you will see quite a few warnings while compiling), open
24
- CMakeLists.txt in the trunk folder and find the following line (around line 39 in my version)
25
-
26
- set(CMAKE_OSX_ARCHITECTURES i386 ppc CACHE STRING "do not build for 64-bit" FORCE)
27
-
28
- and change it to: (yes, the comment is a bit silly)
29
-
30
- set(CMAKE_OSX_ARCHITECTURES i386 ppc x86_64 CACHE STRING "do build for 64-bit" FORCE)
31
-
32
- Now you can call
33
-
34
- make -f pm_mac/Makefile.osx
35
-
36
- if it builds, you can try to install the library. Currently the process seems to be a bit broken,
37
- so you actually need to type:
38
-
39
- sudo env PF=/usr make -f pm_mac/Makefile.osx install
40
-
41
- yes, this sucks - I'm currently trying to find out with the portmidi authors what it would take to fix all this.