2010/3/3 Jesús Gabriel y Galán [email protected]:
Jesus.
Ha, that’s probably Ruby 101. Cool, thanks.
@Brian C. – I see you took the modal approach. Pretty cool. I like
this:
(mode-1).times { a.push(a.shift) }
2010/3/3 Jesús Gabriel y Galán [email protected]:
Jesus.
Ha, that’s probably Ruby 101. Cool, thanks.
@Brian C. – I see you took the modal approach. Pretty cool. I like
this:
(mode-1).times { a.push(a.shift) }
Evan H. wrote all single >'d lines
On Wed, Mar 3, 2010 at 8:50 AM, Ben R. [email protected] wrote:
picked up)
I’m not sure when “susb” would cause an issue – do you mean an
instance like Gsusb? I don’t know that that’s a chord, but the G would
be read and the susb would raise an exception as an invalid chord
symbol.
Yes, I do mean like Gsusb. Susb wasn’t a valid example, but if it was
actually the name of a type of chord, it would be counted as Gbsusb.
Your code (with my comments):
note[1…-1].scan(/./).each do |n| #for every
character in the input except the first store it in n and do the
following ( btw an easier way is simply note[1…-1].each(‘’) ):
if n == ‘b’ then flat! #if n is b
then flat the base note
elsif n == ‘#’ then sharp! #else, if n
is # then sharp the base note
else raise ArgumentError, ‘Invalid note name!’ end #otherwise
rase an error
end #endfor
For Gbsusb (assuming susb is a valid chord type) get the second
character, which is b, and if it is b (which it is), flat the base note.
Then do the same for s, then u, then s, then b (and flat it again), etc.
On second thought, the sus should hit the ArgumentError, hm… Can anyone
explain that?
Chord#to_s:
�Returns @name
But that would only return the input in a fancy way! I don’t see how it
returns chords.It actually returns the individual notes in the chord, as generated by
Chord#names.
#…#
@name = note[0,1] #if note=“Hello World” this sets
@name to ‘H’
#…#
#apply sharps and flats to @name
#…#
def to_s
@name
end
#…#
To me it seems that the base note is stored in @name, which is what is
returned in to_s - so it would just return the base note.
As an aside, can anyone tell me if there is a slick Ruby way to do
what is done in cases like my Key#names, Chord#names, Chord#to_s, etc.
functions, where you’re just mapping things from one array to another,
or from one array to a string, etc?
See Jesús’ answer. One thing to add - array * str is the same as
array.join(str):
[1,2,3]* #=> ERROR
[1,2,3]‘’ #=> ‘123’
[1,2,3]’ ’ #=> ‘1 2 3’
[1,2,3].join #=> ‘123’
[1,2,3].join(’ ') #=> ‘1 2 3’
Wait, I just realized some of my arguments are invalid! For some reason
I’ve been looking at the Note class all along, instead of the Chord
class! facepalm Sorry about that…
I started re-doing this Quiz from scratch about an hour ago without
looking at anyone else’s code, unfortunately, the results look about the
same as David S.'s code (mine follows).
Sharps the note in place… Comments are fun!
I agree! They’re also useful for anyone trying to read or debug your
code!
#I’ll be making this library bigger, given enough time. I’d copy David
Springer’s (he has given me his permission), but this is case
insensitive and doesn’t have optional notes).
chord_library={
‘7’ => [3,4,3],
‘major’ => [4,3],
‘maj’ => [4,3],
‘major7’ => [4,3,4],
‘maj7’ => [4,3,4],
‘minor’ => [3,4],
‘min’ => [3,4],
‘minor7’ => [3,4,3],
‘min7’ => [3,4,3],
‘m7’ => [3,4,3],
‘sus’ => [7],
‘sus2’ => [2,5],
‘sus4’ => [5,2]
}
rotated_notes = []
while !((chord=gets.to_s.chomp.downcase).empty?)
base_note = chord[/^[abdeg]b/]
if (base_note == nil)
base_note = chord[/^[acdfg]#/]
if (base_note == nil)
base_note = chord[/^[a-g]/]
if (base_note == nil)
puts ‘ERROR: Invalid base note’
next
end
end
end
note_list = ((base_note[1,1]==‘b’) ? (%w(ab a bb b c db d eb e f gb
g)) : (%w(a a# b c c# d d# e f f# g g#)))
i=-1
rotated_notes = note_list.map do
i += 1
note_list[(i + note_list.index(base_note))%12]
end
variation = ($‘.empty?) ? (‘major’) : ($’)
if (chord_library[variation] == nil)
puts ‘ERROR: Chord not found’
next
end
out = 'The notes in the chord ’ + chord.capitalize + ’ are: ’ +
base_note.capitalize
note_tally = 0
chord_library[variation].each do |note|
if (note<0)
next
end
out << ’ ’ + rotated_notes[((note+note_tally)%12)].capitalize
note_tally += note
end
puts out
end
Here’s an updated version. I now explicitly give out the scales as well
as chords, and have attempted to optimise the “spelling” of 8-note
scales (which don’t sit naturally on the 7 letters A-G). I made some
simplifications too.
Sample interaction:
C7
Scale: C D E F G A Bb
Chord: C E G Bb
C7alt
Scale: C Db Eb Fb Gb Ab Bb
Chord: C Eb Gb Bb
C7b9
Scale: C Db Eb E F# G A Bb
Chord: C E G Bb Db Eb F# A
Cdim7
Scale: C D Eb F Gb Ab A B
Chord: C Eb Gb A
C#dim7
Scale: C# D# E F# G A A# B#
Chord: C# E G A#
#I’ll be making this library bigger, given enough time.
Sorry for the double post, but by ‘library’ I meant ‘chord dictionary’…
And just to clarify what I mean by ‘spelling’, I mean the sequence of
letters and accidentals which make a scale. On a 7-note scale each scale
corresponds to the next letter.
Example: G# major
Correct spelling: G# A# B# C# D# E# Fx
(Fx = F double-sharp)
It would be wrong to write something like: G# A# C C# D# F G
because of the gaps and duplications of letters.
This is the reason why a C7 chord is C E G Bb not C E G A#. It’s because
the associated scale is
C D E F G A Bb
This scale is called the ‘Mixolydian mode’ (named by the ancient Greeks,
I believe). It also happens to be the notes of F major with a different
starting point, so fortunately you can re-use your major scale finger
patterns
Regards,
Brian.
On 03/08/2010 15:08, Brian C. wrote:
Here’s an updated version. I now explicitly give out the scales as well
as chords, and have attempted to optimise the “spelling” of 8-note
scales (which don’t sit naturally on the 7 letters A-G). I made some
simplifications too.
C
Scale: C D E F G A B
Chord: C E G B
Why does the Chord contain a B?
Alexander J. wrote:
C
Scale: C D E F G A B
Chord: C E G BWhy does the Chord contain a B?
Explained earlier in the thread. If you’ve just joined:
http://www.ruby-forum.com/topic/204996
But if you want traditional triads, it’s easy enough to reconfigure the
program so that it returns, say,
C => C E G
CMaj7 => C E G B
Here is a partial solution, it does not support all type of chord
symbols (see
comments in the main script).
The main script is chord_translator.rb, it contains the ‘chord’ API and
also
translates chords when run from the command line.
Usage: ruby chord_translator.rb {chord-symbols}
Example:
ruby chord_translator.rb C Cdim7 Am F#+7
C => C E G
Cdim7 => C Eb Gb A
Am => A C E
F#+7 => F# A# D E
The chord_translator_test.rb file contain tests validating the currently
supported chord symbols and also presents the APIs implemented by
chord_translator.rb.
The other scripts are files for the GUI version of the tool, they use
the
current trunk version of Swiby (a layer on top of Java/Swing) and need
jRuby.
(the first two scripts can run with any Ruby interpreter)
The startup command is: jruby -I/core/lib
chord_translator_ui.rb
(chord_translator_ui.rb contains the GUI definition and the GUI logic -
pretty
basic -, score_painter.rb paints the musical notation - staff - and
styles.rb
defines the CSS-like styles of the GUI). It uses a font to draw the
musical symbols, downloaded from
http://simplythebest.net/fonts/fonts/musical_symbols.html
The attached ZIP-file contains all the scripts and an image file
presenting a run of the GUI version.
Regards to all…
Introduction
This summary was written by Jean L…
This quiz has some tricky aspects; translating a chord symbol to the
notes
that it comprise may be ambiguous. The rule to follow when interpreting
a
chord symbol may be different from one person to another and still be
correct.
As an example take the C major chord, one may expect three notes (C, E
and
G). On a guitar you can produce 6 notes, on a piano you can produce 10
notes. Would you only play three notes? Another example: Cb on the piano
results in hitting the B key. Should a chord like Ab-Cb be rendered as
Ab-B?
Have a look at the thread of discussion…
Therefore, I am not considering all interpretation differences. I am not
an
expert and I would probably be wrong.
Terminology
In the hope to make the summary clear, let’s first present the
terminology
we use.
The ‘thing’ the solution program expects as input is a chord symbol.
The chord symbol has a specific syntax. It starts with a note name, a
letter
from A to G, that we name the chord root. We can assign a pitch to the
note,
a flat or a sharp. We write a flatted note by adding the lower case
letter
‘b’ and a sharped by adding the ‘#’ symbol.
After the root note follows the quality and the extension, they are a
limited set of strings defining what notes to include in the produced
chord.
The rule to apply to get the notes is the same whatever the root note
is. We
are going to call it the modifier (Evan’s term) or chord modifier.
Scales are sequences of notes, the basic sequence is for instance the C
major: C D E F G A B. We use degree to refer to the distance between
notes,
for instance in the C scale, G is at a higher degree compared to C.
Solutions analysis
We see two trends in the solutions: three solutions defined classes,
like
Note, and two went for a straight implementation of the solution. The
solutions with classes are intended to be musical APIs. They check if
the
script is used as a main script to run in interactive mode. Typically
code
expecting to run both as a utility and as a main script contain code
like:
if FILE == $0
end
All the solutions parse the chord input and validate it in some way.
They
all use regular expressions but in different ways.
Ben R.
Ben’s code, that parses the chord symbol, uses an array notation where
the
index is a regular expression that returns a matching string or nil:
value = “hello”
p value[/hel/] # => “hel”
p value[/aa/] # => nil
(not common to me)
He uses nested if statements with different regular expressions to
validate
the chord symbol and builds the result at the same time. Using the root
note, he creates a note list as an array of possible notes sorted by
degree
(either with sharps, either with flats). To define the array he uses a
standard array, then rotates the sequence so that the root note becomes
the
first. The code looks like:
note_list = (%w(ab a bb b c db d eb e f gb g))
rotated_list = note_list.map { … }
He uses a hash object that defines the ‘chord library’. The chord
library
maps the modifier to an array of indexes. The indexes give the sequence
of
elements to select from the note list to build the chord. Each index
being
an offset from the previous one.
Basically as the note list expresses all the semitones, the first index
gives the number of semitones between the root note and the first not to
appear in the chord. The next one gives the number of semitones between
the
second note and the third one, and so on.
Because adding indexes may result in overflowing, he use a modulo 12 to
restart from 0.
David S.
David’s solution is pretty similar, except that his indexes are not
cumulative, they all give the offset from the root note.
He has a more complete list of modifiers.
Two differences are worth to notice. David does not use the regular
expression literals, supported by Ruby. He explicitly uses the Regexp
class.
To combine alternative patterns he uses the union method:
sharps = Regexp.new(“^[ACDFG]#”)
flats = Regexp.new(“^[ABDEG]b”)
naturals = Regexp.new(“^[A-G]”)
get_root = Regexp.union(sharps,flats,naturals)
The second difference is that David does not rotate the arrays
containing
the scale, to move the root note at the first position. He addresses the
items using modulo, after computing the position of the root note. He
also
uses negative indexes to mark optional notes.
Evan H.
Evan defines several classes: Note, Chord and Key. He also extends the
Map class
with class level methods
He creates a Chord instance with the chord symbol as parameter. The
initializer parses the chord symbol and actually creates the result.
Because he accepts a combination of any number of modifiers, he tries to
match as much strings as possible, by removing one character from the
modifier string at a time, until it finds some match. Evan uses a hash
(dictionary) with the supported modifiers. The code searching for all
the
modifiers looks like:
modifier
mod = original_modifier.dup
until chords_dictionary.include?(mod) or mod.empty? do
mod.slice! -1 # remove last character
end
x = chords_dictionary[mod] unless mod.empty?
The values stored in the chord dictionary are arrays of method names.
The
methods must be sent to a Note object. The methods return a note at some
interval (or distance) from the root note. Calling all the methods
produces
the chord.
At the end he uses the uniq! method of the Chord class to remove any
duplicate note.
Brian C.
Brian introduces one class, named Note. The class level scale method
returns
the chord and the scale. A Note object has two attributes: the note
expressed as an index (A note is 0) and the distance from A (as
semitones).
The scale method uses another class level method named parse. The parse
method
returns a Note instance and the chord modifier. Again, the code
validates
the modifier with a dictionary. The dictionary provides the scale in an
array of numbers, each number gives the number of semitones between the
root
note and each note. Actually, dictionary entries may contain two other
arrays to add more notes in the scale and the notes making up the chord.
The
code converts the array of semitones to an array of notes (strangely the
method making the conversion is also named scale but is not in the same
scope, instance method here).
Brian’s dictionary contains a pretty complete list of modes.
Once he gets the scale, he selects the notes for the chord.
An interesting usage in parse is the call to new to create an instance.
As
the method belongs to the Note class, the new method, with respect to
the
current self, is the one in the class object.
def parse str
[new(note, semi), $3]
end
Using new this way, instead of calling Note.new has the advantage of
making
easier class renaming or copy-pasting code.
Jean L.
The last solution is mine. It contains three classes: Note, Chord, and
Interval.
The Interval represents a distance and is expressed as a number of
degrees
and a number of semitones. A Note has a name, an index (like Brian’s
index,
0 is A) and the pitch.
The Chord class parses a chord symbol in the initializer. It does not
build
the solution, it stores the elements of the chord definition. The
parsing
uses only one regular expression with the group options to retrieve each
element:
if @chord_symbol =~ /^([A-G])([#b]{0,1})(maj|m|mi|min|){0,1}$/
puts “root note #{$1}”
puts “pitch is #{$2}”
puts “quality is #{$3}”
end
As you see in the example above the expression has 3 groups: [A-G],
[#b]
andmaj|m|mi|min.
The Chord class has a dictionary providing arrays of intervals. When it
comes to generate the chord, the code calls the to_a method. to_a
creates an
array by adding each interval to the root note. The Note class overloads
the
add and subtract operators:
def + interval
# code here
end
def - interval
# code here
end
I also added tests to test the chord parsing, adding intervals to notes
and
the chord generation. To make the tests easier to read I created
constants
named A, B, C, etc.
Finally, I added a script to run a GUI version displaying the notes
using
the score notation, see screenshot. It uses Swiby as the GUI layer on
top of
Java/Swing, that’s why I wrote the solution as an API.
Comments
Reading the code with classes was not very easy.
I will not start a debate about API design, still I think some questions
should help in writing APIs:
Let’s try to look at the solutions with the questions in mind.
Should they be easy to understand?
Brian’s Note class contains a class level method named parse, it parses
a
string and creates a Note object.
Should they be intuitive to use?
Evan has a class named Key. It contains a method named include? taking a
note as parameter and easy to guess what it is supposed to do: it
returns
true if the given notes belongs to key.
Should they allow doing different things or only a specific one?
My Note class allows to add intervals to calculate another note. The
feature
is used to build the chord, it could also be used to generate sequences
of
notes by applying rules like oriental music style or jazz style.
Should they be helpful?
The solution make use of their APIs and do not contains unused feature,
obviously they are helpful.
Conclusion
So, Steve, I would suggest that you make use of Brian’s solution because
it
seems to support more chords. But, I think you can rather easily improve
or
enhance any of the solutions to meet your needs.
Music Theory (#229) -
Solutionshttp://rubyquiz.strd6.com/quizzes/229.tar.gz
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.
Sponsor our Newsletter | Privacy Policy | Terms of Service | Remote Ruby Jobs