niki 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+ *.rbc
data/Gemfile CHANGED
@@ -2,6 +2,4 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in niki.gemspec
4
4
  gemspec
5
-
6
- gem 'unimidi'
7
- # gem 'midiator', :path => '../midiator'
5
+ gem 'rake'
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- niki (0.0.1)
5
- midilib
4
+ niki (0.1.0)
5
+ unimidi
6
6
 
7
7
  GEM
8
8
  remote: http://rubygems.org/
@@ -10,7 +10,7 @@ GEM
10
10
  ffi (1.0.9)
11
11
  ffi-coremidi (0.0.8)
12
12
  ffi (>= 1.0)
13
- midilib (2.0.0)
13
+ rake (0.9.2)
14
14
  unimidi (0.2.1)
15
15
  ffi-coremidi
16
16
 
@@ -19,4 +19,4 @@ PLATFORMS
19
19
 
20
20
  DEPENDENCIES
21
21
  niki!
22
- unimidi
22
+ rake
data/Readme.md CHANGED
@@ -2,21 +2,22 @@
2
2
 
3
3
  Niki is a Ruby DSL to describe and play musical pieces.
4
4
 
5
- It leverages Giles Bowkett's [Archaeopteryx](
6
- https://github.com/gilesbowkett/archaeopteryx) to send MIDI output to other
5
+ Its name comes from the [world-famous band Niki](http://niki.fm) which I happen
6
+ to be part of :D
7
+
8
+ It leverages Ari Russo's [Unimidi](
9
+ https://github.com/arirusso/unimidi) to send MIDI output to other
7
10
  programs accepting MIDI inputs (Ableton Live, Reason, Garage Band...), which
8
11
  will actually play the song.
9
12
 
10
- To see what the DSL looks like, take a look at the [example song](https://github.com/txus/niki/blob/master/examples/my_song.rb)
11
-
12
- https://github.com/gilesbowkett/archaeopteryx/blob/master/db_drum_definition.rb
13
+ To see what the DSL looks like, take a look at the [example song](https://github.com/txus/niki/blob/master/examples/my_song.rb).
13
14
 
14
- ## Caveats
15
+ Thanks to Unimidi, it works on all major platforms (Linux, OSX and Windows). To
16
+ ensure Niki will run on your platform (I've personally tested it with OSX only),
17
+ please look at the [Unimidi repo at Github](https://github.com/arirusso/unimidi)
18
+ for instructions about your specific platform.
15
19
 
16
- * It runs only in Ruby 1.8.7 (due to the dependency on Archaeopteryx) :(
17
- * I've tested this only in Snow Leopard, but it should run in more platforms.
18
- If your platform is not supported it's because Archaeopteryx
19
- doesn't support it yet.
20
+ Currently niki runs (at least) on Ruby MRI versions 1.8.7 and 1.9.2.
20
21
 
21
22
  ## Run the example song
22
23
 
@@ -36,10 +37,8 @@ difficult in other operating systems):
36
37
 
37
38
  * Make sure the IAC driver is activated in Mac OSX's Audio/MIDI Setup (inside the
38
39
  MIDI section) (activate the "Device is online" checkbox if it is not)
39
- * In this section, make sure at least one port is created (under the Ports
40
- section). If not, add one clicking the + button.
41
40
  * Go to the Reason options panel, go to Advanced, and set your first
42
- MIDI bus (Bus A) to "IAC Driver IAC Bus 1" (the port you just created).
41
+ MIDI bus (Bus A) to "IAC Driver IAC Bus 1" or something similar.
43
42
 
44
43
  ### Install the gem and run!
45
44
 
@@ -1,4 +1,3 @@
1
- require File.expand_path(File.dirname(__FILE__)) + "/../vendor/archaeopteryx/archaeopteryx"
2
1
  require 'core_ext/fixnum'
3
2
  require 'core_ext/array'
4
3
  require 'niki/chords'
@@ -40,5 +40,6 @@ module Niki
40
40
  end
41
41
 
42
42
  end
43
+
43
44
  end
44
45
  end
@@ -1,14 +1,16 @@
1
+ require 'unimidi'
2
+
1
3
  module Niki
2
4
  class Song
3
5
  attr_reader :tempo, :drum_notes
4
6
 
5
7
  include Niki::Chords
6
8
 
7
- def initialize(options, &block)
8
- @clock = Archaeopteryx::Midi::Clock.new(options[:tempo] || 127)
9
-
10
- @midi = Archaeopteryx::Midi::PracticalRubyProjects::LiveMIDI.new(:clock => @clock )
9
+ NOTE_ON = 0x90
10
+ NOTE_OFF = 0x80
11
11
 
12
+ def initialize(options, &block)
13
+ @midi = UniMIDI::Output.first
12
14
  @parts = []
13
15
  @channel = {}
14
16
  @drum_notes = {}
@@ -45,24 +47,27 @@ module Niki
45
47
  play_part(part, instrument_name)
46
48
  end
47
49
  end.map { |thread| thread.join }
50
+
51
+ [:basses, :chords, :melodies, :drums].each do |instrument_name|
52
+ reset(instrument_name)
53
+ end
48
54
  end
49
55
  end
50
56
 
51
57
  def play_part(part, instrument_name)
52
- part.send(instrument_name).each do |instrument|
53
- notes = [instrument.first].flatten.compact.map do |note|
54
- Archaeopteryx::Midi::Note.create(
55
- :channel => @channel[instrument_name],
56
- :number => note,
57
- :duration => instrument.last,
58
- :velocity => 127)
59
- end
60
- notes.each do |note|
61
- @midi.note_on(note)
62
- end
63
- sleep(instrument.last)
64
- notes.each do |note|
65
- @midi.note_off(note)
58
+ return if part.send(instrument_name).length.zero?
59
+ channel = @channel[instrument_name]
60
+
61
+ @midi.open do |out|
62
+ part.send(instrument_name).each do |instrument|
63
+ notes = [instrument.first].flatten.compact
64
+ notes.each do |note|
65
+ out.puts NOTE_ON + channel, note, 100
66
+ end
67
+ sleep(instrument.last)
68
+ notes.each do |note|
69
+ out.puts NOTE_OFF + channel, note, 100
70
+ end
66
71
  end
67
72
  end
68
73
  end
@@ -77,5 +82,15 @@ module Niki
77
82
  !!@parts.map(&:name).detect {|part_name| part_name == name }
78
83
  end
79
84
 
85
+ def reset(instrument_name)
86
+ channel = @channel[instrument_name]
87
+ @midi.open do |out|
88
+ # Reset all notes
89
+ 95.times do |i|
90
+ out.puts NOTE_OFF + channel, i, 100
91
+ end
92
+ end
93
+ end
94
+
80
95
  end
81
96
  end
@@ -1,3 +1,3 @@
1
1
  module Niki
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -14,10 +14,10 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.rubyforge_project = "niki"
16
16
 
17
- s.add_runtime_dependency 'midilib'
17
+ s.add_runtime_dependency 'unimidi'
18
18
 
19
19
  s.files = `git ls-files`.split("\n")
20
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
- s.require_paths = ["lib", "vendor"]
22
+ s.require_paths = ["lib"]
23
23
  end
metadata CHANGED
@@ -1,46 +1,35 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: niki
3
- version: !ruby/object:Gem::Version
4
- hash: 29
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 0
9
- - 1
10
- version: 0.0.1
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Josep M. Bach
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-08-15 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: midilib
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
12
+ date: 2011-08-16 00:00:00.000000000 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: unimidi
17
+ requirement: &2154214760 !ruby/object:Gem::Requirement
24
18
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 3
29
- segments:
30
- - 0
31
- version: "0"
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
32
23
  type: :runtime
33
- version_requirements: *id001
24
+ prerelease: false
25
+ version_requirements: *2154214760
34
26
  description: A DSL to describe and play structured musical pieces, i.e. songs
35
- email:
27
+ email:
36
28
  - josep.m.bach@gmail.com
37
29
  executables: []
38
-
39
30
  extensions: []
40
-
41
31
  extra_rdoc_files: []
42
-
43
- files:
32
+ files:
44
33
  - .gitignore
45
34
  - Gemfile
46
35
  - Gemfile.lock
@@ -56,58 +45,35 @@ files:
56
45
  - lib/niki/song.rb
57
46
  - lib/niki/version.rb
58
47
  - niki.gemspec
59
- - vendor/archaeopteryx/archaeopteryx.rb
60
- - vendor/archaeopteryx/clip.rb
61
- - vendor/archaeopteryx/core_ext/array.rb
62
- - vendor/archaeopteryx/core_ext/struct.rb
63
- - vendor/archaeopteryx/drum.rb
64
- - vendor/archaeopteryx/live_hacks.rb
65
- - vendor/archaeopteryx/loop.rb
66
- - vendor/archaeopteryx/midi/clock.rb
67
- - vendor/archaeopteryx/midi/file_output/file_midi.rb
68
- - vendor/archaeopteryx/midi/note.rb
69
- - vendor/archaeopteryx/midi/practical_ruby_projects/core_foundation.rb
70
- - vendor/archaeopteryx/midi/practical_ruby_projects/core_midi.rb
71
- - vendor/archaeopteryx/midi/practical_ruby_projects/live_midi.rb
72
- - vendor/archaeopteryx/midi/practical_ruby_projects/no_midi_destinations.rb
73
- - vendor/archaeopteryx/midi/practical_ruby_projects/practical_ruby_projects.rb
74
- - vendor/archaeopteryx/midi/practical_ruby_projects/timer.rb
75
- - vendor/archaeopteryx/mix.rb
76
- - vendor/archaeopteryx/rhythm.rb
77
- - vendor/archaeopteryx/track.rb
48
+ has_rdoc: true
78
49
  homepage: http://github.com/txus/niki
79
50
  licenses: []
80
-
81
51
  post_install_message:
82
52
  rdoc_options: []
83
-
84
- require_paths:
53
+ require_paths:
85
54
  - lib
86
- - vendor
87
- required_ruby_version: !ruby/object:Gem::Requirement
55
+ required_ruby_version: !ruby/object:Gem::Requirement
88
56
  none: false
89
- requirements:
90
- - - ">="
91
- - !ruby/object:Gem::Version
92
- hash: 3
93
- segments:
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ segments:
94
62
  - 0
95
- version: "0"
96
- required_rubygems_version: !ruby/object:Gem::Requirement
63
+ hash: -752574128219649690
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
65
  none: false
98
- requirements:
99
- - - ">="
100
- - !ruby/object:Gem::Version
101
- hash: 3
102
- segments:
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ segments:
103
71
  - 0
104
- version: "0"
72
+ hash: -752574128219649690
105
73
  requirements: []
106
-
107
74
  rubyforge_project: niki
108
- rubygems_version: 1.8.6
75
+ rubygems_version: 1.6.2
109
76
  signing_key:
110
77
  specification_version: 3
111
78
  summary: A DSL to describe and play structured musical pieces, i.e. songs
112
79
  test_files: []
113
-
@@ -1,28 +0,0 @@
1
- alias :L :lambda
2
-
3
- %w{rubygems midilib/sequence midilib/consts}.each {|lib| require lib}
4
-
5
- %w{archaeopteryx/core_ext/struct
6
- archaeopteryx/core_ext/array
7
-
8
- archaeopteryx/loop
9
- archaeopteryx/drum
10
- archaeopteryx/rhythm
11
- archaeopteryx/mix
12
-
13
- archaeopteryx/live_hacks
14
- archaeopteryx/track
15
- archaeopteryx/clip
16
-
17
- archaeopteryx/midi/note
18
- archaeopteryx/midi/clock
19
-
20
- archaeopteryx/midi/file_output/file_midi
21
-
22
- archaeopteryx/midi/practical_ruby_projects/no_midi_destinations
23
- archaeopteryx/midi/practical_ruby_projects/core_midi
24
- archaeopteryx/midi/practical_ruby_projects/core_foundation
25
- archaeopteryx/midi/practical_ruby_projects/live_midi
26
- archaeopteryx/midi/practical_ruby_projects/timer}.each do |archaeopteryx|
27
- require File.expand_path(File.dirname(__FILE__)) + "/../#{archaeopteryx}"
28
- end
@@ -1,37 +0,0 @@
1
- module Archaeopteryx
2
- class Clip
3
- attr_accessor :measures
4
- def initialize(attributes)
5
- @message = Message.create(attributes.merge(:midi_channel => 0,
6
- :value => 127))
7
- @measures = attributes[:measures]
8
- end
9
- def notes(measure)
10
- []
11
- end
12
- def messages(measure)
13
- 0 == measure % @measures ? [@message] : []
14
- # if 0 == @relative_measure % @measures
15
- # [@message]
16
- # else
17
- # []
18
- # @relative_measure += 1
19
- # end
20
- end
21
- def mutate(measure)
22
- end
23
- # def choose
24
- # relative_measure = 1
25
- # etc.
26
- # end
27
- def complete?
28
- [true, false][rand(2)]
29
- # current_measure == final_measure
30
- # BUT! these need to be relative measures in this Clip, not absolute measures in the overall Loop
31
- # possible that Loop should be called Engine or some crazy shit
32
- # I guess relative_measure is really @current_measure
33
- end
34
- end
35
- end
36
-
37
- # probably rename this to make it drum-specific
@@ -1,11 +0,0 @@
1
- class Array
2
- def random
3
- self[rand(self.size)]
4
- end
5
- end
6
-
7
- # the weird thing is, sometimes this method works and sometimes it doesn't. it happens with other
8
- # libs as well as this one. wtf is going on there? gotta investigate that shit.
9
-
10
- # oh fuck I think I know why. I require ActiveSupport in my IRB so I'm assuming Array#rand from
11
- # AS is part of Ruby itself. ooops.
@@ -1,7 +0,0 @@
1
- # this allows you to create Structs and Struct subclasses with pseudo-keyword options hashes
2
- # rather than with long sequences of unnamed args. written by Michael Fellinger.
3
- class Struct
4
- def self.create(hash)
5
- new(*hash.values_at(*members.map{|member| member.to_sym}))
6
- end
7
- end
@@ -1,24 +0,0 @@
1
- module Archaeopteryx
2
- class Drum
3
- attr_accessor :note, :probabilities, :when, :next, :number_generator
4
- def initialize(attributes)
5
- %w{note probabilities when next number_generator}.each do |attribute|
6
- eval("@#{attribute} = attributes[:#{attribute}]")
7
- end
8
- @queue = [attributes[:when]]
9
- generate
10
- end
11
- def play?(beat)
12
- @when[beat]
13
- end
14
- def generate
15
- beats_on_which_to_play = []
16
- @probabilities.each_with_index do |probability, index|
17
- beats_on_which_to_play << index if @number_generator[] <= probability
18
- end
19
- @queue << L{|beat| beats_on_which_to_play.include? beat}
20
- @when = @next[@queue]
21
- end
22
- alias :mutate :generate
23
- end
24
- end
@@ -1,8 +0,0 @@
1
- class Message < Struct.new(:midi_channel, :controller_number, :value)
2
- end
3
-
4
- class TapTempo
5
- def midi_channel; 15 ; end
6
- def controller_number ; 7 ; end
7
- def value ; 127 ; end
8
- end
@@ -1,32 +0,0 @@
1
- module Archaeopteryx
2
- class Loop
3
- attr_reader :midi
4
- def initialize(attributes)
5
- %w{generator
6
- measures
7
- beats
8
- clock
9
- midi
10
- evil_timer_offset_wtf
11
- infinite}.each do |option|
12
- eval("@#{option} = attributes[:#{option}]")
13
- end
14
- end
15
- def generate_beats
16
- (1..@measures).each do |measure|
17
- @generator.mutate(measure)
18
- (0..(@beats - 1)).each do |beat|
19
- play @generator.notes(beat)
20
- @clock.tick
21
- end
22
- end
23
- if @midi.infinite?
24
- @midi.timer.at((@clock.start + @clock.time) - @evil_timer_offset_wtf,
25
- &(L{generate_beats}))
26
- end
27
- end
28
- def play(music)
29
- music.each {|note| @midi.play(note)}
30
- end
31
- end
32
- end
@@ -1,23 +0,0 @@
1
- # get singleton from std lib
2
- module Archaeopteryx
3
- module Midi
4
- class Clock
5
- attr_reader :time, :interval, :start
6
- def initialize(bpm)
7
- # assumes 16-step step sequencer, 4/4 beat, etc.
8
- self.bpm = bpm
9
- @start = Time.now.to_f
10
- @time = 0
11
- end
12
- def bpm=(bpm)
13
- seconds_in_a_minute = 60.0
14
- beats_in_a_measure = 4.0
15
- @interval = seconds_in_a_minute / bpm.to_f / beats_in_a_measure
16
- end
17
- def tick
18
- @time += @interval
19
- @time
20
- end
21
- end
22
- end
23
- end
@@ -1,44 +0,0 @@
1
- # a quick note about FAIL. FileMIDI is actually of the same unspecified superclass as LiveMIDI. both
2
- # need new, clearer names, but the nonexistent abstract superclass is just a class which takes options
3
- # for args and has a method called play() which takes a note argument.
4
-
5
- include MIDI
6
-
7
- # christ this should probably live in some kind of module or something
8
-
9
- class FileMIDI
10
- attr_accessor :filename
11
- def initialize(options)
12
- raise :hell unless options.is_a? Hash
13
- @filename = options[:filename]
14
- @clock = options[:clock]
15
- @events = []
16
-
17
- @sequence = MIDI::Sequence.new
18
- @sequence.tracks << (@track = MIDI::Track.new(@sequence))
19
- @track.events << Tempo.new(Tempo.bpm_to_mpq(options[:tempo])) if options[:tempo]
20
- @track.events << MetaEvent.new(META_SEQ_NAME, options[:name]) if options[:name]
21
-
22
- # I'm not sure if this is actually necessary (?)
23
- @track.events << Controller.new(0, CC_VOLUME, 127)
24
- @track.events << ProgramChange.new(0, 1, 0)
25
- end
26
- def infinite?
27
- false
28
- end
29
- def midilib_delta
30
- # figuring this shit out was an epic fucking nightmare and to be honest I still have no idea why it works
31
- ((@sequence.note_to_delta("16th") / @clock.interval) * @clock.time).to_i
32
- end
33
- def play(note)
34
- @track.merge [NoteOnEvent.new(note.channel,
35
- note.number,
36
- note.velocity,
37
- midilib_delta)]
38
- end
39
- def write
40
- File.open(@filename, 'wb') do |file|
41
- @sequence.write(file)
42
- end
43
- end
44
- end
@@ -1,9 +0,0 @@
1
- module Archaeopteryx
2
- module Midi
3
- class Note < Struct.new(:channel, :number, :duration, :velocity)
4
- def to_code
5
- "Note.new(#{self.channel}, #{self.number}, #{self.duration}, #{self.velocity})"
6
- end
7
- end
8
- end
9
- end
@@ -1,7 +0,0 @@
1
- module CoreFoundation
2
- require 'dl/import'
3
- extend DL::Importable
4
- dlload '/System/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation'
5
-
6
- extern "void * CFStringCreateWithCString (void *, char *, int)"
7
- end
@@ -1,16 +0,0 @@
1
- module CoreMIDI
2
- require 'dl/import'
3
- extend DL::Importable
4
-
5
- dlload '/System/Library/Frameworks/CoreMIDI.framework/Versions/Current/CoreMIDI'
6
-
7
- extern "int MIDIClientCreate(void *, void *, void *, void *)"
8
- extern "int MIDIClientDispose(void *)"
9
- extern "int MIDIGetNumberOfDestinations()"
10
- extern "void * MIDIGetDestination(int)"
11
- extern "int MIDIOutputPortCreate(void *, void *, void *)"
12
- extern "void * MIDIPacketListInit(void *)"
13
- # http://groups.google.com/group/ruby-midi/browse_thread/thread/85de6ea9373c57a4
14
- extern "void * MIDIPacketListAdd(void *, int, void *, int, int, void*)"
15
- extern "int MIDISend(void *, void *, void *)"
16
- end
@@ -1,117 +0,0 @@
1
- module Archaeopteryx
2
- module Midi
3
- module PracticalRubyProjects
4
- class LiveMIDI
5
-
6
- # this object is a gateway between the Objective-C extern-ed functions from the CoreMIDI API
7
- # and good old-fashioned Ruby. As such some of the code gets weird. This is nearly all from
8
- # Topher Cyll's wicked book referenced in the MIT license file (practical_ruby_projects.rb),
9
- # but with some refactoring and modification.
10
-
11
- include CoreMIDI
12
- include CoreFoundation
13
-
14
- attr_reader :interval # this is a totally misleading variable name! real interval lives on Clock
15
- attr_reader :timer
16
- ON = 0x90
17
- OFF = 0x80
18
- PC = 0xC0 # program change, I think; not actually useable in Propellerhead Reason v3
19
- CONTROLLER = 0xB0 # arbitrary controller message
20
-
21
- def infinite?
22
- true
23
- end
24
-
25
- def to_code
26
- "LiveMIDI.new(:clock => @clock = attributes[:clock], :logging => false)"
27
- end
28
-
29
- def initialize(options)
30
- @clock = options[:clock]
31
- @logging = options[:logging]
32
- @midi_destination = options[:midi_destination] || 0
33
- if @logging
34
- puts <<LOG_PLAYBACK
35
- require 'lib/archaeopteryx'
36
- @midi = #{self.to_code}
37
- LOG_PLAYBACK
38
- end
39
- @interval = 60.0/120 # this is just a polling interval for the Thread - not a musical one
40
- @timer = Timer.new(@interval/1000)
41
- open
42
- end
43
-
44
- def play(midi_note, on_time = @clock.time)
45
- if @logging
46
- puts "@midi.play(#{midi_note.to_code}, #{on_time})"
47
- end
48
- on_time += @clock.start
49
- @timer.at(on_time) {note_on(midi_note)}
50
- @timer.at(on_time + midi_note.duration) {note_off(midi_note)}
51
- end
52
-
53
- def send(message)
54
- send_controller_message(message.midi_channel, message.controller_number, message.value)
55
- end
56
- def send_controller_message(midi_channel, controller_number, value, on_time = @clock.time)
57
- on_time += @clock.start
58
- puts "scheduling #{controller_number} for #{on_time}" if @logging
59
- @timer.at(on_time) do
60
- control(midi_channel, controller_number, value)
61
- end
62
- end
63
-
64
- def open
65
- client_name = CoreFoundation.cFStringCreateWithCString(nil, "RubyMIDI", 0)
66
- @client = DL::PtrData.new(nil)
67
- CoreMIDI.mIDIClientCreate(client_name, nil, nil, @client.ref)
68
-
69
- port_name = CoreFoundation.cFStringCreateWithCString(nil, "Output", 0)
70
- @outport = DL::PtrData.new(nil)
71
- CoreMIDI.mIDIOutputPortCreate(@client, port_name, @outport.ref)
72
-
73
- number_of_destinations = CoreMIDI.mIDIGetNumberOfDestinations()
74
- raise NoMIDIDestinations if number_of_destinations < 1
75
- @destination = CoreMIDI.mIDIGetDestination(@midi_destination)
76
- end
77
-
78
- def close
79
- CoreMIDI.mIDIClientDispose(@client)
80
- end
81
-
82
- def clear
83
- @timer.flush
84
- end
85
-
86
- def message(*args)
87
- format = "C" * args.size
88
- bytes = args.pack(format).to_ptr
89
- packet_list = DL.malloc(256)
90
- packet_ptr = CoreMIDI.mIDIPacketListInit(packet_list)
91
- # http://groups.google.com/group/ruby-midi/browse_thread/thread/85de6ea9373c57a4
92
- packet_ptr = CoreMIDI.mIDIPacketListAdd(packet_list, 256, packet_ptr, 0, args.size, bytes)
93
- CoreMIDI.mIDISend(@outport, @destination, packet_list)
94
- end
95
-
96
- def note_on(midi_note)
97
- message(ON | midi_note.channel, midi_note.number, midi_note.velocity)
98
- end
99
-
100
- def note_off(midi_note)
101
- message(OFF | midi_note.channel, midi_note.number, midi_note.velocity)
102
- end
103
-
104
- def program_change(channel, preset)
105
- message(PC | channel, preset)
106
- end
107
-
108
- def pulse(channel, controller_id, value)
109
- # puts "sending now: #{Time.now.to_f}" if @logging
110
- # puts "#{[channel, controller_id, value].inspect}" if @logging
111
- message(CONTROLLER | channel, controller_id, value)
112
- end
113
- alias :control :pulse
114
- end
115
- end
116
- end
117
- end
@@ -1,8 +0,0 @@
1
- module Archaeopteryx
2
- module Midi
3
- module PracticalRubyProjects
4
- class NoMIDIDestinations < Exception
5
- end
6
- end
7
- end
8
- end
@@ -1,30 +0,0 @@
1
- module Archaeopteryx
2
- module Midi
3
- # most of this code comes from "Practical Ruby Projects" by Topher Cyll, published by
4
- # APress, which is a pretty bad-ass book. here's the license:
5
- #
6
- # The MIT License
7
- #
8
- # Copyright (c) 2006, 2007 Topher Cyll
9
- #
10
- # Permission is hereby granted, free of charge, to any person obtaining a copy
11
- # of this software and associated documentation files (the "Software"), to deal
12
- # in the Software without restriction, including without limitation the rights
13
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- # copies of the Software, and to permit persons to whom the Software is
15
- # furnished to do so, subject to the following conditions:
16
- #
17
- # The above copyright notice and this permission notice shall be included in
18
- # all copies or substantial portions of the Software.
19
- #
20
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
- # THE SOFTWARE.
27
- module PracticalRubyProjects
28
- end
29
- end
30
- end
@@ -1,35 +0,0 @@
1
- module Archaeopteryx
2
- module Midi
3
- module PracticalRubyProjects
4
- class Timer
5
- def initialize(resolution)
6
- @resolution = resolution
7
- @queue = []
8
-
9
- Thread.new do
10
- while true
11
- dispatch
12
- sleep(@resolution)
13
- end
14
- end
15
- end
16
-
17
- def flush
18
- @queue = []
19
- end
20
-
21
- def at(time, &block)
22
- time = time.to_f if time.kind_of?(Time)
23
- @queue.push [time, block]
24
- end
25
-
26
- private
27
- def dispatch
28
- now = Time.now.to_f
29
- ready, @queue = @queue.partition {|time, proc| time <= now}
30
- ready.each {|time, proc| proc.call(time)}
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,27 +0,0 @@
1
- module Archaeopteryx
2
- class Mix
3
- attr_accessor :rhythms
4
- def initialize(attributes)
5
- @rhythms = attributes[:rhythms]
6
- end
7
- def notes(beat)
8
- notes = []
9
- @rhythms.each do |rhythm|
10
- notes << rhythm.notes(beat)
11
- end
12
- notes.flatten
13
- end
14
- def messages(measure) # obviously these should be refactored to inject()s
15
- messages = []
16
- @rhythms.each do |rhythm|
17
- messages << rhythm.messages(measure)
18
- end
19
- messages.flatten
20
- end
21
- def mutate(measure)
22
- @rhythms.each {|rhythm| rhythm.mutate(measure)}
23
- end
24
- end
25
- end
26
-
27
- # Mix nearly identical to Rhythm
@@ -1,29 +0,0 @@
1
- module Archaeopteryx
2
- class Rhythm
3
- def initialize(attributes)
4
- @mutation = attributes[:mutation]
5
- @drumfile = attributes[:drumfile]
6
- reload
7
- end
8
- def reload
9
- puts "\a" # flash the screen ; only valid on my box and similarly configured machines!
10
- @drums = eval(File.read(@drumfile))
11
- end
12
- def notes(beat)
13
- (@drums.collect do |drum|
14
- drum.note if drum.play? beat
15
- end).compact
16
- end
17
- def messages(beat)
18
- []
19
- end
20
- def mutate(measure)
21
- if @mutation[measure]
22
- reload # reloading can kill mutations!
23
- @drums.each {|drum| drum.mutate}
24
- end
25
- end
26
- end
27
- end
28
-
29
- # probably rename this to make it drum-specific
@@ -1,16 +0,0 @@
1
- module Archaeopteryx
2
- class Track < Mix
3
- attr_accessor :current
4
- def initialize(attributes)
5
- @rhythms = attributes[:rhythms]
6
- @measure = 1
7
- new_clip
8
- end
9
- def new_clip
10
- @current = @rhythms.random
11
- end
12
- def messages(measure)
13
- @current.messages(measure)
14
- end
15
- end
16
- end