portmidi 0.0.1 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.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.