rhythmruby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.midi
19
+ *~
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>rhythmruby</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ </buildSpec>
9
+ <natures>
10
+ <nature>com.aptana.ruby.core.rubynature</nature>
11
+ </natures>
12
+ </projectDescription>
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem rhythmruby
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Luuk van der Velden
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,91 @@
1
+ RhythmRuby: Midi rhythms through String manipulation
2
+ ====================================================
3
+
4
+ **Author**: Luuk van der Velden, (Amsterdam, 2013)
5
+ **Website**:
6
+ **Git**: https://github.com/Lvelden/rhythmruby.git
7
+
8
+ Synopsis
9
+ --------
10
+
11
+ RhythmRuby enables the creation of midi rhythms, through string manipulation.
12
+ It provides methods for rhythm/string creation, parsing and writing to midi files.
13
+ Ruby and MIDI based rhythm delight!
14
+
15
+ Basics
16
+ ------
17
+
18
+ **installation:** gem install rhythmruby
19
+
20
+ **1. rhythm string** is a string consisting of two symbols,
21
+ one for silence (default '-') and one for an event or hit (default '#'). An example:
22
+ '#---#---#---#---', is read as one (drum) hit every three silences.
23
+
24
+ **2. rhythm snippets** are the building blocks of a rhythm string. The
25
+ StringCompiler Class allows the creation of rhythm snippets and compiles them into a rhythm string.
26
+ These snippets capture the repetitive aspects of most rhythms.
27
+
28
+ **3. countBase** is the rhythmical length that is assigned to one symbol in a string.
29
+ It is measured in quartenote lengths (relative to bpm), thus countBase: 1.0, means one quarter note per symbol.
30
+
31
+ Classes
32
+ -------
33
+ The **RhythmCompiler** (class) generates rhythm snippets and combines these into rhythm strings.
34
+ These can be parsed to MIDI ready data by the **RhythmParser** (class).
35
+ The parsed output can be written to a MIDI file by the **MidiWriter** (class instance).
36
+
37
+ Examples and Docs
38
+ -----------------
39
+
40
+ Check out the examples provided and the documentation based on YARD. The examples are aimed at
41
+ creating single and multi instrument rhythms. For instance **Meshuggah_example.rb** explains the creation of the
42
+ intro rhythm of 'Perpetual Black Second' by Meshuggah (Nothing, 2002), using ruby scripting.
43
+
44
+
45
+ **minimal example**
46
+
47
+ In the example below we have skipped the creation of the rhythm string.
48
+ A predefined rhythm is written to midi.
49
+
50
+ require 'rhythmruby'
51
+
52
+ midiNote = 50 # midi note assigned to drum hits
53
+ bpm = 120 # beats per minute of midi file
54
+ fileName = 'testing.midi' # name of midi file
55
+
56
+ countBase = 1.0/4.0 # one symbol represents a sixteenth note (one fourth of a quarter note)
57
+
58
+ rhythm = '#---#---' # two quarter note drum hits, on sixteenth note base
59
+
60
+ # parse the rhythm string to MIDI ready information (array of [midiNote, noteLength] sub-arrays)
61
+ midiInfo = RhythmParser.parseRhythm(rhythm, countBase, midiNote)
62
+
63
+ midiWriter = MidiWriter.new(bpm) # midiWriter instance administrating one MIDI song
64
+ midiTrack = midiWriter.createTrack() # create track in the MIDI song
65
+ midiWriter.writeSeqToTrack(midiInfo, midiTrack) # write the parsed rhythm to a MIDI track
66
+ midiWriter.writeToFile(fileName) # write MIDI song to file
67
+
68
+ Workflow on Linux
69
+ --------
70
+
71
+ On linux the MIDI files created with rhythmruby can be played from the command-line by programs like
72
+ pmidi. pmidi can send the MIDI events to any ALSA channel f.i. the MIDI-in of the Hydrogen sampler, which
73
+ is a nice drum sampler and sequencer. In this way you can listen to the rhythms you create without leaving your
74
+ IDE or editor.
75
+
76
+ Rational
77
+ --------
78
+
79
+ RhythmRuby's strength lies in the rhythm string abstraction. It allows easy computer
80
+ manipulation of rhythm snippets to create rhythm patterns. These can then be
81
+ written to a midi file, possibly consisting of several tracks (f.i. hihat, snare, kick)
82
+
83
+ With RhythmRuby you can capture polyrhythmics at their essence by creating patterns of rhythm snippets.
84
+ The resulting MIDI files are perfect for further use in DAW's such as (cubase, logic, etc.)
85
+
86
+ RhythmRuby was made to create drum rhythms for my band Hologram Earth (www.hologramearth.nl)
87
+
88
+ Acknowledgment
89
+ --------------
90
+ thanks Jim Menard for **midilib**, a pure ruby MIDI library.
91
+ https://github.com/jimm/midilib
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,91 @@
1
+ # ruby version testing
2
+ versionInfo = RUBY_VERSION.split('.').map{|num| num.to_i}
3
+
4
+ if versionInfo[1]==9
5
+ require "rhythmruby"
6
+ elsif versionInfo[1]<=8
7
+ require "rubygems"
8
+ require "rhythmruby"
9
+ end
10
+
11
+ # In this example we recreate the initial drum pattern of the song:
12
+ #'Perpetual Black Second' by Meshuggah, as played on the album Nothing (2002)
13
+ # hi-hat and snare are quite straightforward, but we will use
14
+ # RhythmRuby to concisely capture the polyrhythmics in the kick pattern
15
+
16
+ fileName = 'Meshuggah.midi'
17
+ bpm = 140 # beats per minute of midi Song
18
+ countBase = 1.0/4.0 # countbase in multiples/divisions of the quarternote (1/4 countbase is sixteenth notes, 4 notes per quarter note)
19
+
20
+ # define midiNotes for the midi percussion tracks,
21
+ # these default notes work with the Hydrogen sampler
22
+ midiNotes = {'kick'=>36, 'snare'=>38, 'hihat'=>42}
23
+
24
+ # empty hash to store rhythm parameters
25
+ stringParam ={'kick'=>{}, 'snare'=>{}, 'hihat'=>{}}
26
+
27
+
28
+ # create rhythm by specifying snippets and snippet patterns
29
+
30
+ # basic quarter note beat on the hihat
31
+ stringParam['hihat']['snippetL'] = [4] # snippet has length of 4 sixteenth notes (see countBase)
32
+ stringParam['hihat']['eventPos'] = [[0]] # event/hit happens at position 0
33
+ stringParam['hihat']['idx'] = [0] # only snippet '0' is repeated
34
+ stringParam['hihat']['nRep'] = [32] # repeat the snippet 32 times (32 quarternotes)
35
+ stringParam['hihat']['rhythmRep'] = 1 # repeat this rhythm once
36
+
37
+ # snare pattern of four quarter note, snare on third quarter note
38
+ stringParam['snare']['snippetL'] = [4,4] # two snippets of length 4 sixteenth notes
39
+ stringParam['snare']['eventPos'] = [[], [0]] #. one snippet with hit on first sixteenth note and one empty snippet
40
+ stringParam['snare']['idx'] = [0,0,1,0] # pattern of snippets (empty, empty, hit, empty)
41
+ stringParam['snare']['nRep'] = nil # repeat snippets once
42
+ stringParam['snare']['rhythmRep'] = 8 # repeat complete rhythm 8 times
43
+
44
+
45
+ # interesting kick pattern of 14 sixteenth notes long, it shifts respective to the hi-hat
46
+ # it can be understood as 7/8 (kick) over 4/4 (snare) polyrhythmic
47
+ stringParam['kick']['snippetL'] = [14, 2] # main pattern and 2 note filler at end
48
+
49
+ # the main 14 sixteenth note snippet looks like this: #--#--#-##-#--
50
+ stringParam['kick']['eventPos'] = [[0, 3, 6, 8, 9, 11],[0]] # positions at which hits/events occur per snippet
51
+ stringParam['kick']['idx'] = [0,1] # sequential order of snippets, first repeat main snippet then add filler
52
+ stringParam['kick']['nRep'] = [9,1] # repeat main snippet 9 times, then play filler pattern once
53
+ stringParam['kick']['rhythmRep'] = 1 # repeat total rhythm once
54
+
55
+ # for each instrument
56
+ # create rhythm snippet strings and combine them in a rhythm string
57
+ rhythms = {} # empty hash for storing rhythms per instrument
58
+ stringParam.each_key do
59
+ |instr|
60
+ snippets = RhythmCompiler.createSnippets(stringParam[instr]['snippetL'],stringParam[instr]['eventPos'])
61
+ # compile rhythm pattern/string
62
+ rhythms[instr] = RhythmCompiler.createRhythm(snippets, stringParam[instr]['idx'], stringParam[instr]['nRep'], stringParam[instr]['rhythmRep'])
63
+
64
+ end
65
+
66
+ # print out the compiled rhythm
67
+ puts 'rhythm'
68
+ for instr in ['hihat','snare','kick']
69
+ puts rhythms[instr]
70
+ end
71
+
72
+
73
+ # create the midiWriter instance, to write the midi info to a file
74
+ midiWriter = MidiWriter.new(bpm) # instance creates a midi song with a fixed bpm
75
+
76
+ # parse and write rhythm to midi track for each instrument
77
+ rhythms.each_key do
78
+ |instr|
79
+ # parse the string and return the midi info
80
+ midiSequence = RhythmParser.parseRhythm(rhythms[instr], countBase, midiNotes[instr])
81
+
82
+ # create an empty midi track within the midiSong, to write the midi info to
83
+ midiTrack = midiWriter.createTrack()
84
+
85
+ # write the midi info/sequence to the midiTrack
86
+ midiWriter.writeSeqToTrack(midiSequence, midiTrack)
87
+
88
+ end
89
+
90
+ # write the midiSong to a file
91
+ midiWriter.writeToFile(fileName)
@@ -0,0 +1,80 @@
1
+ # ruby version testing
2
+ versionInfo = RUBY_VERSION.split('.').map{|num| num.to_i}
3
+
4
+ if versionInfo[1]==9
5
+ require "rhythmruby"
6
+ elsif versionInfo[1]<=8
7
+ require "rubygems"
8
+ require "rhythmruby"
9
+ end
10
+
11
+ fileName = 'drumtest.midi'
12
+ bpm = 120 # beats per minute of midi Song
13
+ countBase = 1.0/4.0 # countbase in multiples/divisions of the quarter note (1/4 countbase is sixteenth notes, 4 notes per quarter note)
14
+
15
+
16
+ midiNotes = {'kick'=>36, 'snare'=>38, 'hihat'=>42}
17
+ stringParam ={'kick'=>{}, 'snare'=>{}, 'hihat'=>{}}
18
+
19
+
20
+ # parameters for rhythm composition
21
+
22
+ # quarter note beat on the hi-hat
23
+ stringParam['hihat']['snippetL'] = [4] # snippet is 4 siixteenthnotes long (one quarter note)
24
+ stringParam['hihat']['eventPos'] = [[0]] # hi-hat hit is on the first sixteenth note (the beat)
25
+ stringParam['hihat']['idx'] = [0] # rhythm is made up out of one snippet
26
+ stringParam['hihat']['nRep'] = [8] # repeat snippet 30 times for 30 quarter notes
27
+ stringParam['hihat']['rhythmRep'] = 1 # repeat total rhythm once for 8 beat bar
28
+
29
+
30
+ # snare on two and four
31
+ stringParam['snare']['snippetL'] = [4,4] # two snippets of 4 sixteenth notes
32
+ stringParam['snare']['eventPos'] = [[],[0]] # two snippets, one has no hits, one has it on the first sixteenth note
33
+ stringParam['snare']['idx'] = [0,1] # alternate the two snippets
34
+ stringParam['snare']['nRep'] = nil # play snippets once before alternating
35
+ stringParam['snare']['rhythmRep'] = 4 # repeat the two beat rhythm 4 times for 8 beat bar
36
+
37
+ # kick on first two eight notes
38
+ stringParam['kick']['snippetL'] = [4,4] # two snippets of length 4
39
+ stringParam['kick']['eventPos'] = [[0,2],[]] # kick on first and third sixteenth note and empty snippet
40
+ stringParam['kick']['idx'] = [0,1] # alternate the snippets
41
+ stringParam['kick']['nRep'] = nil # play snippets once before alternating
42
+ stringParam['kick']['rhythmRep'] = 4 # repeat rhythm 4 times for 8 beat bar
43
+
44
+ # for each instrument
45
+ # create rhythm snippet strings and combine them in a rhythm string
46
+ rhythms = {} # empty hash for storing rhythms per instrument
47
+ stringParam.each_key do
48
+ |instr|
49
+ snippets = RhythmCompiler.createSnippets(stringParam[instr]['snippetL'],stringParam[instr]['eventPos'])
50
+ # compile rhythm pattern/string
51
+ rhythms[instr] = RhythmCompiler.createRhythm(snippets, stringParam[instr]['idx'], stringParam[instr]['nRep'], stringParam[instr]['rhythmRep'])
52
+
53
+ end
54
+
55
+ # print out the compiled rhythm
56
+ puts 'rhythm'
57
+ for instr in ['hihat', 'snare', 'kick']
58
+ puts rhythms[instr]
59
+ end
60
+
61
+
62
+ # create the midiWriter instance, to write the midi info to a file
63
+ midiWriter = MidiWriter.new(bpm) # instance creates a midi song with a fixed bpm
64
+
65
+ # parse and write rhythm to midi track for each instrument
66
+ rhythms.each_key do
67
+ |instr|
68
+ # parse the string and return the midi info
69
+ midiSequence = RhythmParser.parseRhythm(rhythms[instr], countBase, midiNotes[instr])
70
+
71
+ # create an empty midi track within the midiSong, to write the midi info to
72
+ midiTrack = midiWriter.createTrack()
73
+
74
+ # write the midi info/sequence to the midiTrack
75
+ midiWriter.writeSeqToTrack(midiSequence, midiTrack)
76
+
77
+ end
78
+
79
+ # write the midiSong to a file
80
+ midiWriter.writeToFile(fileName)
@@ -0,0 +1,51 @@
1
+ # ruby version testing
2
+ versionInfo = RUBY_VERSION.split('.').map{|num| num.to_i}
3
+
4
+ if versionInfo[1]==9
5
+ require "rhythmruby"
6
+ elsif versionInfo[1]<=8
7
+ require "rubygems"
8
+ require "rhythmruby"
9
+ end
10
+
11
+ fileName = 'test.midi'
12
+ bpm = 140 # beats per minute of midi Song
13
+ midiNote = 46 # midiNote of events, we are testing with one instrument, so one note
14
+ countBase = 1.0/4.0 # countbase in multiples/divisions of the quarter note (1/4 countbase is sixteenth notes, 4 notes per quarter note)
15
+
16
+ # note 36=kick, 38=snare 46=hi-hat
17
+
18
+ # snippet creation parameters
19
+ snippetLengths = [4] # length per snippet (or length for all snippets, if snippeLength.length == 1)
20
+ eventPositions = [[0]] # positions per snippet, where events happen (zero-based)
21
+
22
+ # pattern creation parameters
23
+ snippetIdx = [0] # indexes of snippets to be added sequentially to the rhythm string
24
+ snippetRep = [10] # number of repeats per snippet, if undefined (nil) all snippets are repeated once
25
+ totalRepeat = 1 # repeat total rhythm once
26
+
27
+ # create rhythm snippet strings
28
+ snippets = RhythmCompiler.createSnippets(snippetLengths, eventPositions)
29
+
30
+ puts 'snippets', snippets
31
+
32
+ # compile rhythm pattern/string
33
+ rhythm = RhythmCompiler.createRhythm(snippets, snippetIdx, snippetRep, totalRepeat)
34
+
35
+ # print out the compiled rhythm
36
+ puts 'rhythm', rhythm
37
+
38
+ # parse the string and return the midi info
39
+ midiSequence = RhythmParser.parseRhythm(rhythm, countBase, midiNote)
40
+
41
+ # create the midiWriter instance, to write the midi info to a file
42
+ midiWriter = MidiWriter.new(bpm)
43
+
44
+ # create an empty midi track within the midiSong, to write the midi info to
45
+ midiTrack = midiWriter.createTrack()
46
+
47
+ # write the midi info/sequence to the midiTrack
48
+ midiWriter.writeSeqToTrack(midiSequence, midiTrack)
49
+
50
+ # write the midiSong to a file
51
+ midiWriter.writeToFile(fileName)
@@ -0,0 +1,5 @@
1
+ # main file to load seperate class files
2
+
3
+ require "rhythmruby/RhythmParser"
4
+ require "rhythmruby/RhythmCompiler"
5
+ require "rhythmruby/MidiWriter"
@@ -0,0 +1,60 @@
1
+ # ruby version testing
2
+ versionInfo = RUBY_VERSION.split('.').map{|num| num.to_i}
3
+
4
+ # MIDI interface was build on midilib, by Jim Menard, https://github.com/jimm/midilib
5
+ if versionInfo[1]==9
6
+ require "midilib"
7
+ elsif versionInfo[1]<=8
8
+ require "rubygems"
9
+ require "midilib"
10
+ end
11
+
12
+ # writes sequences of noteData (from a RhythmParser class) to midi tracks
13
+ # a 'MidiWriter' represents one midi Song, which can be written to one file,
14
+ # the midiSong can contain multiple tracks/noteSequences
15
+ #
16
+ # @param [Float] bpm beats per minute of the midi song/file that the tracks are part of
17
+ # @return [Object] MidiWriteInstance instance of the midi writer.
18
+ # creator of one midi file with potential multiple tracks
19
+ class MidiWriter
20
+ def initialize(bpm)
21
+ @song = MIDI::Sequence.new() # this is a midi song/sequence
22
+ @bpm = bpm # beats per minute of the midi song/sequence
23
+ end
24
+
25
+ # creates a track within the midi song of current MidiWriter instance
26
+ # @return [MIDI::Track] newTrack a new track within the midi song
27
+ def createTrack()
28
+ @song.tracks << (newTrack = MIDI::Track.new(@song))
29
+ newTrack.events << MIDI::Tempo.new(MIDI::Tempo.bpm_to_mpq(@bpm))
30
+ newTrack.events << MIDI::ProgramChange.new(0, 0)
31
+ return newTrack
32
+ end
33
+
34
+ # writes a note sequence generate by a RhythmParser to a midi track
35
+ # @param [Array<Array>] midiSeq an array of [midiNote, noteLength] (created by RhythmParser)
36
+ # @param [MIDI:Track] midiTrack target track, generated with 'createTrack'.
37
+ # if a track is reused the event sequence will be appended
38
+ def writeSeqToTrack(midiSeq, midiTrack)
39
+ midiSeq.each do
40
+ |midiNote, noteLength|
41
+ writeNote(midiNote, noteLength, midiTrack)
42
+ end
43
+ end
44
+
45
+ # writes one midiNote to a midiTrack
46
+ # @param [Fixnum] midiNote of the event to be written to a midiTrack
47
+ # @param [Float] noteLength length of the midiEvent, in multiples of the quarternote
48
+ # @param [MIDI::Track] midiTrack where the event is written to
49
+ def writeNote(midiNote, noteLength, midiTrack)
50
+ midiTrack.events << MIDI::NoteOnEvent.new(0, midiNote, 127, 0)
51
+ midiTrack.events << MIDI::NoteOffEvent.new(0, midiNote, 127, \
52
+ @song.length_to_delta(noteLength))
53
+ end
54
+
55
+ # write the midiSong to file consisting of all the created midiTracks
56
+ # @param [String] fileName where the midiSong is written to, can be a path
57
+ def writeToFile(fileName)
58
+ open(fileName, 'w') {|f| @song.write(f) }
59
+ end
60
+ end
@@ -0,0 +1,78 @@
1
+
2
+ # rhythm composition class (use as a class)
3
+ # compiles rhythm snippets into a rhythm string/pattern
4
+ class RhythmCompiler
5
+ @@silenceMarker = '-' # symbol for silence
6
+ @@eventMarker = '#' # symbol for an event / hit
7
+
8
+ # create an array of snippets of length in snippetLengths,
9
+ # with events at position(s) in eventPositions
10
+ # @param [Array<Fixnum>] snippetLengths length of each snippet
11
+ # (if snippetLengths.length == 1, length is constant for all snippets )
12
+ # @param [Array<Array>] eventPositions containing arrays of event positions per snippet
13
+ # (if eventPositions.length == 1, then event positions are constant for all snippets)
14
+ # @return [Array<String>] snippets array of string rhythm snippets,
15
+ # for use with 'createString' method
16
+ def self.createSnippets(snippetLengths, eventPositions)
17
+ snippets = [] # empty array to append the snippets
18
+
19
+ # make input arrays suitable for fur
20
+ if snippetLengths.length == 1
21
+ # make snippetLengths as long as eventPositions
22
+ snippetLengths *= eventPositions.length
23
+
24
+ elsif eventPositions.length == 1
25
+ # make eventPositions as long as snippetLengths
26
+ eventPositions *= snippetLengths.length
27
+
28
+ else # both input arrays have more than one element and should be the same length
29
+ if not(eventPositions.length == snippetLengths.length)
30
+ raise ArgumentError, 'lengths of both arrays should be the same, \
31
+ or one should have length 1'
32
+ end
33
+ end
34
+
35
+ #create a rhythm snippet for each set of event positions and snippetlength
36
+ snippetLengths.zip(eventPositions).each do
37
+ |length, positions| # unpack the length and position information
38
+ # create a snippet of defined length consisting of silences
39
+ snippet = @@silenceMarker*length
40
+
41
+ positions.each{|pos| # for each provided event positon of the snippet
42
+ snippet[pos] = @@eventMarker # add an event marker at the position
43
+ }
44
+
45
+ snippets<<snippet # add the snippet to the returned snippets array
46
+ end
47
+ return snippets # return the array with the snippets
48
+ end
49
+
50
+ # creates a rhythm string from rhythm snippets
51
+ # @param [Array<String>] snippets array of rhythm snippets (from 'createSnippets)
52
+ # @param [Array<Fixnum>] snippetIDx indexes, defining sequential order of snippets
53
+ # @param [Array<Fixnum>] snippetRep defining how often snippets are repeated...
54
+ # if nil, all snippets are repeated once, if snippetRep.length==1, all snippets are repeated the same amount
55
+ # otherwise snippetRep should be the same length as snippetIdx and define repetition per snippet
56
+ # @param [Fixnum] totalRepeat, number of times the total rhythm is repeated
57
+ # @return [String] rhythmString, symbolic rhythm string
58
+ def self.createRhythm(snippets, snippetIDx, snippetRep, totalRepeat)
59
+ if snippetRep == nil # test whether snippetRep is defined, if not all snippets are repeated once
60
+ snippetRep = [1]*snippetIDx.length # make snippetRep and snippetIDx the same length for iteration
61
+ end
62
+ if totalRepeat == nil # if undefined set repeats equal to 1
63
+ totalRepeat = 1
64
+ end
65
+
66
+
67
+ rhythmString = "" # empty string where all the snippets will be added to
68
+
69
+ # iterate over combinations of snippetID and repeats
70
+ snippetIDx.zip(snippetRep).each do
71
+ |snippetID, repeats|
72
+ # add the snippet with snippetID, repeat times, to the rhythm string
73
+ rhythmString += snippets[snippetID]*repeats
74
+ end
75
+
76
+ return rhythmString*totalRepeat # return the rhythm string repeated totalRepeat times, ready for parsing
77
+ end
78
+ end
@@ -0,0 +1,59 @@
1
+
2
+ # Parser (use as class) of the rhythm strings, consisting event and silence symbols
3
+ class RhythmParser
4
+ @@silenceNote = 1 # pitch of a note played at during initial silence in a sequence
5
+ @@silenceMark = '-' # symbol identified as a silence
6
+ @@eventMark = '#' # symbol identified as an event
7
+ @@eventNote = 50 # (unused default) midi note of an event
8
+ @@countBase = 1.0/4.0 # (unused default) time duration of one symbol in quarter note lengths
9
+
10
+ # parses the rhythm string and generates midi info ready for MidiWriter
11
+ # side note: a string starting with a silence needs an initial 'silence note' (note value: @@silenceNote)
12
+ # @param [String] rhythmString sequence of silence and event markers
13
+ # @param [Float] countBase rhyhtmic length of one symbol (in quarternote units)
14
+ # @param [Fixnum] midiNote note assigned to events in the parsed output
15
+ # @return [Array<Array>] midiSequence array of [midiNote, noteLength]
16
+ # can be written to midi with midiWriter.writeSegToTrack
17
+ def self.parseRhythm(rhythmString, countBase, midiNote)
18
+ @@countBase = countBase # time duration of one symbol (quarternote lengths)
19
+ @@eventNote = midiNote # midi note assigned to events
20
+
21
+ midiSequence = [] # empty array to store parsed midi data
22
+ curNote = nil # initialize current midi note
23
+ curLen = nil # initialze current midi note length
24
+ first = true # state variable testing for first / sequential events
25
+
26
+ rhythmString.each_char do
27
+ |symbol| # symbol in the string either a silence or an event
28
+
29
+ if first # if true this is the first parsed symbol
30
+ first = false # skip on next symbol
31
+
32
+ # if first symbol is an event (handles when the rhythm starts with a silence)
33
+ if symbol == @@eventMark
34
+ curNote = @@eventNote
35
+ curLen = 1
36
+
37
+ elsif symbol == @@silenceMark
38
+ curNote = @@silenceNote
39
+ curLen = 1
40
+
41
+ end
42
+ else # when first symbol has already been parsed
43
+ if symbol == @@eventMark # an event is parsed, write previous event and generate new one
44
+ # add midi event to midiSequence (note length in countbase units)
45
+ midiSequence << [curNote,(curLen*@@countBase)]
46
+ curNote = @@eventNote # generate new event
47
+ curLen = 1 # reset note length to 1
48
+
49
+ elsif symbol == @@silenceMark # a silence is parsed
50
+ curLen += 1 # add one countbase note length to the current note
51
+
52
+ end
53
+ end
54
+ end
55
+
56
+ return midiSequence
57
+ end
58
+ end
59
+
@@ -0,0 +1,3 @@
1
+ module Rhythmruby
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rhythmruby/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "rhythmruby"
8
+ gem.version = Rhythmruby::VERSION
9
+ gem.authors = ["Luuk van der Velden"]
10
+ gem.email = ["l.j.j.vandervelden@gmail.com"]
11
+ gem.description = %q{represent rhythms as symbolic Strings and write them to MIDI}
12
+ gem.summary = %q{represent rhythms as symbolic Strings and write them to MIDI}
13
+ gem.homepage = "https://github.com/Lvelden/rhythmruby"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ gem.add_dependency 'midilib'
20
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rhythmruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Luuk van der Velden
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: midilib
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: represent rhythms as symbolic Strings and write them to MIDI
31
+ email:
32
+ - l.j.j.vandervelden@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - .project
39
+ - Gemfile
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - examples/Meshuggah_example.rb
44
+ - examples/drum_kit_example.rb
45
+ - examples/single_rhythm_example.rb
46
+ - lib/rhythmruby.rb
47
+ - lib/rhythmruby/MidiWriter.rb
48
+ - lib/rhythmruby/RhythmCompiler.rb
49
+ - lib/rhythmruby/RhythmParser.rb
50
+ - lib/rhythmruby/version.rb
51
+ - rhythmruby.gemspec
52
+ homepage: https://github.com/Lvelden/rhythmruby
53
+ licenses: []
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 1.8.24
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: represent rhythms as symbolic Strings and write them to MIDI
76
+ test_files: []
77
+ has_rdoc: