xaviershay / curlophone-orchestra

Uses bonjour to play a great symphony across many computers

This URL has Read+Write access

curlophone-orchestra / musician.rb
100644 148 lines (127 sloc) 3.882 kb
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
74
75
76
77
78
79
80
81
82
83
84
85
86
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
require 'rack'
require 'midiator'
require 'music_player'
 
alias :L :lambda
 
class Player
  include MIDIator::Notes
  include AudioToolbox
 
  attr_accessor :current_part
  
  def initialize
    reset(:drums)
  end
 
  def reset(part = current_part)
    @player.stop if @player
 
    self.current_part = part
    @player = MusicPlayer.new
    @sequence = MusicSequence.new
    @track = @sequence.tracks.new
    event = ExtendedTempoEvent.new(:bpm => 600)
    event.add(0.0, @sequence.tracks.tempo)
    @track.add(0.0, MIDIProgramChangeMessage.new(:channel => 10, :program => 26))
    @track.add(0.0, MIDIControlChangeMessage.new(:channel => 10, :number => 32, :value => 1))
    @track.add(0.0, MIDIProgramChangeMessage.new(:channel => 1, :program => 17))
    @track.add(0.0, MIDIProgramChangeMessage.new(:channel => 2, :program => 33))
    # Use the following call sequence to use an alternate midi destination.
    # Hopefully a more complete interface will be implemented soon. MIDI
    # destinations are referenced by their index beginning at 0.
    # See also CoreMIDI::get_number_of_destinations().
    #
    #@sequence.midi_endpoint = CoreMIDI.get_destination(ARGV.shift.to_i)
    @player.sequence = @sequence
  
    parts = {
      :drums => L{
        (0..1).each do |bar|
          kick1(bar * 24 + 0)
          kick1(bar * 24 + 5)
          snare(bar * 24 + 6)
          kick1(bar * 24 + 9)
          kick1(bar * 24 + 15)
          snare(bar * 24 + 18)
 
          (0..3).each do |beat|
            #hihat(beat * 6)
            hihat(bar * 24 + beat * 6 + 3)
            #hihat(beat * 6 + 5)
          end
        end
      },
      :bass => L{
        duration = 3
        @track.add(0,
          MIDINoteMessage.new(:note => 43,
                              :velocity => 80,
                              :channel => 2,
                              :duration => duration))
        @track.add(6,
          MIDINoteMessage.new(:note => 53,
                              :velocity => 80,
                              :channel => 2,
                              :duration => duration))
        @track.add(9,
          MIDINoteMessage.new(:note => 55,
                              :velocity => 80,
                              :channel => 2,
                              :duration => duration))
      },
      :organ => L{
        def note(beat, pitch, duration = 1)
          @track.add(beat,
            MIDINoteMessage.new(:note => pitch,
                                :velocity => 80,
                                :channel => 1,
                                :duration => duration))
        end
 
        note(3, A3)
        note(3, Eb4)
        note(3, F4)
        note(3, Ab4)
 
        note(9, A3, 12)
        note(9, Eb4, 12)
        note(9, F4, 12)
        note(9, Ab4, 12)
      }
    }[part].call
    
    @track.length = 48
    @track.loop_info = { :duration => @track.length, :number => 0 }
  end
 
  def drum(beat, note)
    @track.add(beat,
      MIDINoteMessage.new(:note => note,
                          :velocity => 80,
                          :channel => 10,
                          :duration => 0.1))
  end
  
  def kick1(beat)
    drum(beat, 35)
  end
  
  def kick2(beat)
    drum(beat, 36)
  end
  
  def snare(beat)
    drum(beat, 40)
  end
 
  def hihat(beat)
    drum(beat, 44)
  end
 
  def call(env)
    req = Rack::Request.new(env)
 
    case req.path_info
    when '/track/drums'
      reset(:drums)
    when '/track/bass'
      reset(:bass)
    when '/track/organ'
      reset(:organ)
    when '/go'
      @player.start
    when '/stop'
      @player.stop
      reset
    end
 
    # Default content-type is text/html
    # Default status is 200
    Rack::Response.new.finish do |res|
       res.write(req.path_info)
    end
  end
end
 
Rack::Handler::WEBrick.run(Player.new, :Port => ARGV[0] || 2000)