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.
- checksums.yaml +4 -4
- data/lib/melody_to_mid.rb +61 -16
- data/lib/sonic_play.rb +34 -9
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d7a570b58f2debf9059e51205ea158848dfd055
|
4
|
+
data.tar.gz: f4729980238abceb71058315950d1e5b46fcfeb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c4d6308413aa8683fc7f3b03c3ffde736bfee0ab7b4fec49f6701b8f14874fefbdfc13e95801edf1e37cb156547bf5c3c566de09da3cefad7887b8753b2c109
|
7
|
+
data.tar.gz: c70e9fb3d13ab1756cf22fd561cd38a477f40e75562953b35fd2d0b24d59ccdf561e234197c5de6b0f9c1813a0f8682e0de7ceca4382b466ea1cce9f786c3a2c
|
data/lib/melody_to_mid.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/sonic_play.rb
CHANGED
@@ -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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
48
|
+
elsif melody.respond_to?(:length)
|
49
|
+
play_melody(melody)
|
25
50
|
end
|
26
|
-
end
|
51
|
+
end
|