Module: MidiPatch

Defined in:
lib/midi_patch.rb

Constant Summary collapse

STEPWISE_X_DELTA =
0.0001

Class Method Summary collapse

Class Method Details

.add_to_stack(stack, event) ⇒ Object



100
101
102
# File 'lib/midi_patch.rb', line 100

def self.add_to_stack(stack, event)
  stack << event
end

.build_gate_node(seq) ⇒ Object



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
# File 'lib/midi_patch.rb', line 65

def self.build_gate_node(seq)
  note_on_off_events = seq.first.select {|e| MIDI::NoteOn === e || MIDI::NoteOff === e}

  note_stack = []

  times = note_on_off_events.map(&:time_from_start)
  min_time, max_time = [times.min, times.max].map(&:to_f)
  coordinates = note_on_off_events.map {|event|
    time = scale_time(event.time_from_start, min_time, max_time)
    case event
    when MIDI::NoteOn
      add_to_stack(note_stack, event)
      [time, scale_velocity(event.velocity)]
    when MIDI::NoteOff
      remove_from_stack(note_stack, event)
      if note_stack.empty?
        [time, 0.0]
      else
        [time, scale_velocity(note_stack[-1].velocity)]
      end
    end
  }

  stepwise_coordinates = make_stepwise(coordinates)
  SplineHelper.build_spline_node_from_coordinates(stepwise_coordinates)
end

.build_patch(path) ⇒ Object



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
# File 'lib/midi_patch.rb', line 9

def self.build_patch(path)
  seq = MIDI::Sequence.new()

  File.open(path, 'rb') do |file|
    seq.read(file)
  end

  doc = Audulus.build_init_doc
  patch = doc['patch']

  pitch_node = build_pitch_node(seq)
  Audulus.add_node(patch, pitch_node)
  Audulus.move_node(pitch_node, 0, 0)

  scaler_node = Audulus.build_expr_node('x*10-5')
  Audulus.add_node(patch, scaler_node)
  Audulus.move_node(scaler_node, 425, 55)
  Audulus.wire_output_to_input(patch, pitch_node, 0, scaler_node, 0)

  pitch_label = Audulus.build_text_node("pitch")
  Audulus.add_node(patch, pitch_label)
  Audulus.move_node(pitch_label, -50, 100)

  gate_node = build_gate_node(seq)
  Audulus.add_node(patch, gate_node)
  Audulus.move_node(gate_node, 0, 200)

  pitch_label = Audulus.build_text_node("gate")
  Audulus.add_node(patch, pitch_label)
  Audulus.move_node(pitch_label, -50, 300)

  _, file = File.expand_path(path).split("/")[-2..-1]
  basename = File.basename(file, ".mid")
  File.write("#{basename}.audulus", JSON.generate(doc))
end

.build_pitch_node(seq) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/midi_patch.rb', line 45

def self.build_pitch_node(seq)
  note_on_events = seq.first.select {|e| MIDI::NoteOn === e}

  times = note_on_events.map(&:time_from_start)
  min_time, max_time = [times.min, times.max].map(&:to_f)
  scaled_times = times.map {|time|
    scale_time(time, min_time, max_time)
  }

  notes = note_on_events.map(&:note)
  scaled_notes = notes.map {|note|
    one_per_octave = (note.to_f - 69.0)/12.0
    (one_per_octave+5)/10 # scale to be > 0
  }

  coordinates = scaled_times.zip(scaled_notes)
  stepwise_coordinates = make_stepwise(coordinates)
  SplineHelper.build_spline_node_from_coordinates(stepwise_coordinates)
end

.make_stepwise(coordinates) ⇒ Object



108
109
110
111
112
113
114
115
116
117
# File 'lib/midi_patch.rb', line 108

def self.make_stepwise(coordinates)
  last_y = nil
  coordinates.flat_map {|x, y|
    result = []
    result << [x-STEPWISE_X_DELTA, last_y] unless last_y.nil?
    result << [x, y]
    last_y = y
    result
  }
end

.remove_from_stack(stack, event) ⇒ Object



104
105
106
# File 'lib/midi_patch.rb', line 104

def self.remove_from_stack(stack, event)
  stack.delete_if {|e| e.note == event.note}
end

.scale_time(time, min_time, max_time) ⇒ Object



92
93
94
# File 'lib/midi_patch.rb', line 92

def self.scale_time(time, min_time, max_time)
  (time - min_time).to_f / (max_time - min_time).to_f
end

.scale_velocity(velocity) ⇒ Object



96
97
98
# File 'lib/midi_patch.rb', line 96

def self.scale_velocity(velocity)
  velocity.to_f / 127.0
end