sonic-midi 0.1.0 → 0.2.0

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/melody_to_mid.rb +61 -16
  3. data/lib/sonic_play.rb +34 -9
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3dee7fe63f4fce57922002d7691fb72ab6f06608
4
- data.tar.gz: d94ff3f2d95ecdc20f15d84a1896d2ef86c302ec
3
+ metadata.gz: 6d7a570b58f2debf9059e51205ea158848dfd055
4
+ data.tar.gz: f4729980238abceb71058315950d1e5b46fcfeb2
5
5
  SHA512:
6
- metadata.gz: 4213548e921eb67791b6ab29e687e46c0164390515372294af9e165756b356d30e6b76c2f0bfb3d85819471f9e6968e2d3b697a6861e4f201eac3966da23a0f7
7
- data.tar.gz: cf354a568eb3710633fb80a007fdaf4551d8431aadcf74972d668c6946f2b4166c23104c38d93eddd319e7d28f48be845c8581ab23d9b3531722fb449805fe2a
6
+ metadata.gz: 4c4d6308413aa8683fc7f3b03c3ffde736bfee0ab7b4fec49f6701b8f14874fefbdfc13e95801edf1e37cb156547bf5c3c566de09da3cefad7887b8753b2c109
7
+ data.tar.gz: c70e9fb3d13ab1756cf22fd561cd38a477f40e75562953b35fd2d0b24d59ccdf561e234197c5de6b0f9c1813a0f8682e0de7ceca4382b466ea1cce9f786c3a2c
@@ -14,13 +14,71 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
- $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
18
-
19
17
  require 'midilib/sequence'
20
18
  require 'midilib/consts'
21
19
  include MIDI
22
20
 
21
+ ##
22
+ # Converts a programmatically-defined melody to Midi.
23
23
  class MelodyToMidi
24
+
25
+ ##
26
+ # Adds a single note of a specified pitch, volume and duration to track at position time_offset.
27
+ # Make sure to call track.recalc_delta_from_times() after all notes have been added.
28
+ private def add_note(track, note, time_offset, duration, volume)
29
+ event = NoteOn.new(0, note, volume, 0)
30
+ event.time_from_start = time_offset
31
+ track.events << event
32
+ event = NoteOff.new(0, note, volume, 0)
33
+ event.time_from_start = time_offset + duration
34
+ track.events << event
35
+ end
36
+
37
+ ##
38
+ # Adds a melody to track.
39
+ # The melody is specified as an an array containg up to three sub-arrays:
40
+ # - List of pitches (as MIDI notes. eg: 60 is C4)
41
+ # - List of durations (as fractions of a measure. eg: 0.25 is a quarter)
42
+ # - (Optional) list of volumes (0..5 range)
43
+ private def add_to_track(track, melody)
44
+ time_offset = 0
45
+ (0..melody[0].length-1).each do |i|
46
+ note = melody[0][i].to_i
47
+ duration = (480 * melody[1][i]).to_i
48
+ volume = 127
49
+ if melody.length >= 3
50
+ volume = ((127 * melody[2][i]) / 5).to_i
51
+ end
52
+ if note != 0
53
+ add_note(track, note, time_offset, duration, volume)
54
+ end
55
+ time_offset += duration
56
+ end
57
+ end
58
+
59
+ ##
60
+ # Adds a melody to the track.
61
+ # The melody object can optionally contain sub-melodies if specified as a dictionary.
62
+ # For example, it can be of the form {m1: ..., m2: ..., m3: ...}
63
+ # Alternatively, the melody object can be a single melody (see add_to_track for details).
64
+ private def process_melody(track, melody)
65
+ if melody.respond_to?(:key)
66
+ melody.values().each do |m|
67
+ add_to_track(track, m)
68
+ end
69
+ elsif melody.respond_to?(:length)
70
+ add_to_track(track, melody)
71
+ end
72
+ track.recalc_delta_from_times()
73
+ end
74
+
75
+ ##
76
+ # Creates a Midi file from a ruby program defining a melody.
77
+ # It writes its output to the path specified by +output_file+.
78
+ #
79
+ # +melody_file+ is expected to contain the following functions:
80
+ # melody() - contains a melody (see process_melody for details)
81
+ # mybpm() - optional, sets the BPM (or default to 60 if not specified)
24
82
  def make_midi(melody_file, output_file)
25
83
  seq = Sequence.new()
26
84
 
@@ -45,20 +103,7 @@ class MelodyToMidi
45
103
  track.events << Controller.new(0, CC_VOLUME, 127)
46
104
  track.events << ProgramChange.new(0, 1, 0)
47
105
 
48
- music = melody()
49
-
50
- delta_time = 0
51
- (0..music[0].length-1).each do |i|
52
- note = music[0][i].to_i
53
- duration = (480 * music[1][i]).to_i
54
- if note == 0
55
- delta_time = duration
56
- else
57
- track.events << NoteOn.new(0, note, 127, delta_time)
58
- track.events << NoteOff.new(0, note, 127, duration)
59
- delta_time = 0
60
- end
61
- end
106
+ process_melody(track, melody())
62
107
 
63
108
  File.open(output_file, 'wb') { |file| seq.write(file) }
64
109
  end
@@ -12,15 +12,40 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+
16
+ ##
17
+ # Plays a single melody in SonicPi.
18
+ def play_melody(m)
19
+ in_thread do
20
+ set_volume! 5
21
+ if defined? mybpm
22
+ use_bpm mybpm()
23
+ end
24
+ if defined? mysynth
25
+ use_synth mysynth()
26
+ end
27
+
28
+ (0..m[0].length-1).each do |i|
29
+ if m.length > 2
30
+ set_volume! m[2][i]
31
+ end
32
+ if m[0][i] != 0
33
+ play m[0][i]
34
+ end
35
+ sleep m[1][i]
36
+ end
37
+ end
38
+ end
39
+
40
+ ##
41
+ # Plays a melody in SonicPi, optionally supporting multiple sub-melodies in parallel.
15
42
  def sonic_play
16
- set_volume! 5
17
- use_synth mysynth()
18
- use_bpm mybpm()
19
- m = melody()
20
- (0..m[0].length-1).each do |i|
21
- if m[0][i] != 0
22
- play m[0][i]
43
+ melody = melody()
44
+ if melody.respond_to?(:key)
45
+ melody.values().each do |m|
46
+ play_melody(m)
23
47
  end
24
- sleep m[1][i]
48
+ elsif melody.respond_to?(:length)
49
+ play_melody(melody)
25
50
  end
26
- end
51
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sonic-midi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Airapetyan