songbooks 0.1.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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/Guardfile +29 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/application.coffee +4 -0
- data/app/assets/javascripts/components/filters/convertWhitespace.filter.coffee +3 -0
- data/app/assets/javascripts/components/song_component/chord.directive.coffee +9 -0
- data/app/assets/javascripts/components/song_component/literal.directive.coffee +6 -0
- data/app/assets/javascripts/components/song_component/song_component.directive.coffee +6 -0
- data/app/assets/javascripts/components/songbook/songbook.controller.coffee +29 -0
- data/app/assets/javascripts/components/songs/song.controller.coffee +10 -0
- data/app/assets/javascripts/components/songs/song.directive.coffee +7 -0
- data/app/assets/javascripts/components/songs/song.service.coffee +98 -0
- data/app/assets/javascripts/components/songs/songs.controller.coffee +21 -0
- data/app/assets/javascripts/routes.coffee +11 -0
- data/app/assets/stylesheets/application.scss +34 -0
- data/app/fonts/DejaVuSans-Bold.ttf +0 -0
- data/app/fonts/DejaVuSans-BoldOblique.ttf +0 -0
- data/app/fonts/DejaVuSans-ExtraLight.ttf +0 -0
- data/app/fonts/DejaVuSans-Oblique.ttf +0 -0
- data/app/fonts/DejaVuSans.ttf +0 -0
- data/app/fonts/DejaVuSansCondensed-Bold.ttf +0 -0
- data/app/fonts/DejaVuSansCondensed-BoldOblique.ttf +0 -0
- data/app/fonts/DejaVuSansCondensed-Oblique.ttf +0 -0
- data/app/fonts/DejaVuSansCondensed.ttf +0 -0
- data/app/fonts/DejaVuSansMono-Bold.ttf +0 -0
- data/app/fonts/DejaVuSansMono-BoldOblique.ttf +0 -0
- data/app/fonts/DejaVuSansMono-Oblique.ttf +0 -0
- data/app/fonts/DejaVuSansMono.ttf +0 -0
- data/app/fonts/DejaVuSerif-Bold.ttf +0 -0
- data/app/fonts/DejaVuSerif-BoldItalic.ttf +0 -0
- data/app/fonts/DejaVuSerif-Italic.ttf +0 -0
- data/app/fonts/DejaVuSerif.ttf +0 -0
- data/app/fonts/DejaVuSerifCondensed-Bold.ttf +0 -0
- data/app/fonts/DejaVuSerifCondensed-BoldItalic.ttf +0 -0
- data/app/fonts/DejaVuSerifCondensed-Italic.ttf +0 -0
- data/app/fonts/DejaVuSerifCondensed.ttf +0 -0
- data/app/tex/songbook.tex +12 -0
- data/app/views/application.haml +54 -0
- data/app/views/layouts/application.haml +40 -0
- data/app/views/songbook.prawn +80 -0
- data/app/views/songs/component.haml +2 -0
- data/app/views/songs/index.jbuilder +5 -0
- data/app/views/songs/show.haml +23 -0
- data/app/views/songs/show.jbuilder +24 -0
- data/app/views/songs/song.haml +4 -0
- data/exe/sb_server +6 -0
- data/lib/prawn_ext/font/afm.rb +20 -0
- data/lib/songbooks.rb +32 -0
- data/lib/songbooks/CLI.rb +13 -0
- data/lib/songbooks/components/chord.rb +98 -0
- data/lib/songbooks/components/literal.rb +57 -0
- data/lib/songbooks/components/metadata.rb +69 -0
- data/lib/songbooks/components/section.rb +73 -0
- data/lib/songbooks/controllers/controller.rb +18 -0
- data/lib/songbooks/controllers/songs_controller.rb +30 -0
- data/lib/songbooks/folder.rb +32 -0
- data/lib/songbooks/server.rb +65 -0
- data/lib/songbooks/song.rb +53 -0
- data/lib/songbooks/version.rb +3 -0
- data/lib/songbooks/view_helper.rb +9 -0
- data/public/javascripts/angular-local-storage.js +449 -0
- data/public/javascripts/angular-route.js +991 -0
- data/public/javascripts/angular.js +28904 -0
- data/public/javascripts/application.js +8 -0
- data/public/javascripts/components/filters/convertWhitespace.filter.js +8 -0
- data/public/javascripts/components/song_component/chord.directive.js +13 -0
- data/public/javascripts/components/song_component/literal.directive.js +9 -0
- data/public/javascripts/components/song_component/song_component.directive.js +9 -0
- data/public/javascripts/components/songbook/songbook.controller.js +33 -0
- data/public/javascripts/components/songs/song.controller.js +12 -0
- data/public/javascripts/components/songs/song.directive.js +9 -0
- data/public/javascripts/components/songs/song.service.js +107 -0
- data/public/javascripts/components/songs/songs.controller.js +22 -0
- data/public/javascripts/jquery-2.1.4.min.js +4 -0
- data/public/javascripts/routes.js +16 -0
- data/public/stylesheets/application.css +24 -0
- data/public/stylesheets/bootstrap-theme.min.css +5 -0
- data/public/stylesheets/bootstrap.min.css +5 -0
- data/songbooks.gemspec +51 -0
- metadata +369 -0
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
\documentclass[10pt,a4paper,twoside]{article}
|
2
|
+
\usepackage[latin1]{inputenc}
|
3
|
+
\usepackage{amsmath}
|
4
|
+
\usepackage{amsfonts}
|
5
|
+
\usepackage{amssymb}
|
6
|
+
\usepackage{graphicx}
|
7
|
+
\begin{document}
|
8
|
+
\begin{tabular}{|r|l|}
|
9
|
+
asdf & fdsa \\
|
10
|
+
ss & gg \\
|
11
|
+
\end{tabular}
|
12
|
+
\end{document}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
!!! 5
|
2
|
+
%html{'ng-app' => 'songbooks', 'ng-controller' => 'SongsController as songsCtrl'}
|
3
|
+
%head
|
4
|
+
%link{:href => '/stylesheets/bootstrap.min.css', :rel => 'stylesheet'}/
|
5
|
+
%link{:href => '/stylesheets/bootstrap-theme.min.css', :rel => 'stylesheet'}/
|
6
|
+
%link{:href => '/stylesheets/application.css', :rel => 'stylesheet'}/
|
7
|
+
|
8
|
+
- %w(jquery-2.1.4.min angular angular-route angular-local-storage application routes).each do |file|
|
9
|
+
%script{:src => "/javascripts/#{file}.js", :type => 'text/javascript'}
|
10
|
+
- Songbooks::ViewHelper.javascripts.each do |file|
|
11
|
+
%script{:src => "/javascripts/#{file}", :type => 'text/javascript'}
|
12
|
+
|
13
|
+
%title
|
14
|
+
Songbooks
|
15
|
+
|
16
|
+
%body{:ng => {:controller => 'SongbookController as songbook'}}
|
17
|
+
.container-fluid
|
18
|
+
.row
|
19
|
+
.col-sm-3.hidden-print
|
20
|
+
.panel.panel-default
|
21
|
+
.panel-heading
|
22
|
+
%h3.panel-title
|
23
|
+
Available Songs
|
24
|
+
%a.btn.btn-default.btn-sm.pull-right{:ng => {:click => 'songsCtrl.addAllToBook()'}} +
|
25
|
+
%br/
|
26
|
+
%small= Songbooks.folder.path
|
27
|
+
.list-group#songs
|
28
|
+
.list-group-item
|
29
|
+
%input.form-control{:type => 'text', :ng => {:model => 'songsCtrl.filterQuery'}}
|
30
|
+
.list-group-item{:ng => {:click => 'songsCtrl.setActive(song)',
|
31
|
+
:repeat => 'song in songsCtrl.songs() | filter: songsCtrl.filterQuery',
|
32
|
+
:class => "{ 'active': song.active }"},
|
33
|
+
:style => 'cursor: default'}
|
34
|
+
%span.pull-right
|
35
|
+
%a.btn.btn-sm.btn-default{:ng => {:click => 'songbook.addSong(song)'}} +
|
36
|
+
%song
|
37
|
+
.col-sm-6
|
38
|
+
.panel.panel-default
|
39
|
+
.panel-body#main-content{:ng => {:view => true}}
|
40
|
+
.col-sm-3.hidden-print
|
41
|
+
.panel.panel-default
|
42
|
+
.panel-heading
|
43
|
+
%h3.panel-title
|
44
|
+
{{ songbook.title() }}
|
45
|
+
%a.btn.btn-default.btn-sm.pull-right{:ng => {:click => 'songbook.sendToServer()'}} PDF
|
46
|
+
%a.btn.btn-default.btn-sm.pull-right{:ng => {:click => 'songbook.clear()'}} Clear
|
47
|
+
.clearfix
|
48
|
+
.list-group#songs
|
49
|
+
.list-group-item
|
50
|
+
%input.form-control{:type => 'text', :ng => {:model => 'songbook.filterQuery'}}
|
51
|
+
.list-group-item{:ng => {:repeat => 'song in songbook.songs() | filter: songbook.filterQuery'}}
|
52
|
+
%span.pull-right
|
53
|
+
%a.btn.btn-sm.btn-default{:ng => {:click => 'songbook.removeSong(song)'}} -
|
54
|
+
%song
|
@@ -0,0 +1,40 @@
|
|
1
|
+
!!! 5
|
2
|
+
|
3
|
+
%html
|
4
|
+
%head
|
5
|
+
%link{:href => '/stylesheets/bootstrap.min.css', :rel => 'stylesheet'}/
|
6
|
+
%link{:href => '/stylesheets/bootstrap-theme.min.css', :rel => 'stylesheet'}/
|
7
|
+
%link{:href => '/stylesheets/application.css', :rel => 'stylesheet'}/
|
8
|
+
%script{:src => '/javascripts/jquery-2.1.4.min.js', :type => 'text/javascript'}
|
9
|
+
%script{:src => '/javascripts/angular.min.js', :type => 'text/javascript'}
|
10
|
+
|
11
|
+
%body
|
12
|
+
.container-fluid
|
13
|
+
.row
|
14
|
+
.col-sm-3
|
15
|
+
.panel.panel-default
|
16
|
+
.panel-heading
|
17
|
+
%h3.panel-title
|
18
|
+
Verfügbare Lieder
|
19
|
+
%br/
|
20
|
+
%small= Songbooks.chords_directory.path
|
21
|
+
.list-group#file-list
|
22
|
+
- Songbooks.chords_directory.song_list.songs.each do |song|
|
23
|
+
%a.list-group-item{:href => "/songs/#{song.identifier}", :data => {:remote => true}}
|
24
|
+
= song.title
|
25
|
+
= ' - '
|
26
|
+
%i= song.artist
|
27
|
+
.col-sm-6
|
28
|
+
.panel.panel-default
|
29
|
+
.panel-heading
|
30
|
+
%h3.panel-title Inhalt
|
31
|
+
.panel-body#main-content
|
32
|
+
= yield
|
33
|
+
.col-sm-3
|
34
|
+
.panel.panel-default
|
35
|
+
.panel-heading
|
36
|
+
%h3.panel-title
|
37
|
+
Ausgewählte Lieder
|
38
|
+
.panel-body
|
39
|
+
ll
|
40
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#----------------------------------------------------------------
|
2
|
+
# Fonts
|
3
|
+
#----------------------------------------------------------------
|
4
|
+
|
5
|
+
pdf.font_families.update('DejaVuSansMono' => {
|
6
|
+
:normal => Songbooks.font_file('DejaVuSansMono'),
|
7
|
+
:italic => Songbooks.font_file('DejaVuSansMono-Oblique'),
|
8
|
+
:bold => Songbooks.font_file('DejaVuSansMono-Bold'),
|
9
|
+
:bold_italic => Songbooks.font_file('DejaVuSansMono-BoldOblique')
|
10
|
+
})
|
11
|
+
|
12
|
+
@toc = {}
|
13
|
+
|
14
|
+
pdf.start_new_page
|
15
|
+
|
16
|
+
@songs.each do |song|
|
17
|
+
pdf.text song.title, :size => 18
|
18
|
+
pdf.text song.artist
|
19
|
+
|
20
|
+
@toc["#{song.title} - #{song.artist}"] = pdf.page_count
|
21
|
+
|
22
|
+
song_y = pdf.cursor
|
23
|
+
text_x = pdf.width_of(song.longest_section_caption) + 10
|
24
|
+
|
25
|
+
pdf.font 'DejaVuSansMono', :size => 9 do
|
26
|
+
|
27
|
+
song.sections.each do |section|
|
28
|
+
pdf.draw_text section.name, :at => [text_x - pdf.width_of(section.name) - 10, song_y], :style => :bold
|
29
|
+
|
30
|
+
section.lines.each do |line|
|
31
|
+
line_x = text_x
|
32
|
+
|
33
|
+
line.each do |component|
|
34
|
+
case component
|
35
|
+
when Songbooks::Components::Literal
|
36
|
+
pdf.draw_text component.to_s, :at => [line_x, song_y]
|
37
|
+
when Songbooks::Components::Chord
|
38
|
+
pdf.fill_color '8B0000'
|
39
|
+
pdf.draw_text component.to_s, :at => [line_x, song_y]
|
40
|
+
pdf.fill_color '000000'
|
41
|
+
end
|
42
|
+
|
43
|
+
line_x += pdf.width_of(component.to_s)
|
44
|
+
end
|
45
|
+
song_y -= 12
|
46
|
+
|
47
|
+
if song_y < 10
|
48
|
+
pdf.start_new_page
|
49
|
+
song_y = 700
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
pdf.start_new_page unless song == @songs.last
|
56
|
+
end
|
57
|
+
|
58
|
+
pdf.go_to_page 1
|
59
|
+
|
60
|
+
#----------------------------------------------------------------
|
61
|
+
# Table of Contents
|
62
|
+
#----------------------------------------------------------------
|
63
|
+
|
64
|
+
pdf.font 'DejaVuSansMono', :size => 11 do
|
65
|
+
@toc.keys.sort.each do |k|
|
66
|
+
min_width = pdf.width_of("#{k} #{@toc[k]}")
|
67
|
+
dots = ''
|
68
|
+
dots << '.' while pdf.width_of(dots) < (530 - min_width)
|
69
|
+
|
70
|
+
pdf.text "#{k} #{dots} #{@toc[k]}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#----------------------------------------------------------------
|
75
|
+
# Page Numbers
|
76
|
+
#----------------------------------------------------------------
|
77
|
+
|
78
|
+
pdf.number_pages '<page>',
|
79
|
+
:page_filter => lambda{ |pg| pg != 1 },
|
80
|
+
:at => [pdf.bounds.right - 50, 0]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
.pull-right
|
2
|
+
%a{:ng => {:click => 'songbook.addSong(songCtrl.song())'}} Add
|
3
|
+
|
4
|
+
%h3
|
5
|
+
%small
|
6
|
+
{{ songCtrl.song().artist }}
|
7
|
+
%br/
|
8
|
+
{{ songCtrl.song().title }}
|
9
|
+
|
10
|
+
%p
|
11
|
+
%span{ng: {repeat: 'i in [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6]'}}
|
12
|
+
%input{type: 'radio', name: 'transposed_by', ng: {model: 'songCtrl.song().transposedBy'}} {{ i }}
|
13
|
+
|
14
|
+
|
15
|
+
%table
|
16
|
+
%tbody
|
17
|
+
%tr{:ng => {:repeat => 'section in songCtrl.song().sections'}}
|
18
|
+
%td.text-right{:style => 'vertical-align: top; padding: 10px; padding-top: 0'}
|
19
|
+
%strong
|
20
|
+
{{ section.name }}
|
21
|
+
%td
|
22
|
+
%div{:ng => {:repeat => 'line in section.lines'}}
|
23
|
+
%song-component{:ng => {:repeat => 'component in line'}}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
json.data do
|
2
|
+
json.(@song, :identifier, :title, :artist)
|
3
|
+
|
4
|
+
json.sections @song.sections do |section|
|
5
|
+
json.(section, :name)
|
6
|
+
|
7
|
+
json.lines section.lines do |line|
|
8
|
+
json.array! line do |component|
|
9
|
+
case component
|
10
|
+
when Songbooks::Components::Literal
|
11
|
+
json.component_type 'literal'
|
12
|
+
json.content component.to_s
|
13
|
+
when Songbooks::Components::Chord
|
14
|
+
json.component_type 'chord'
|
15
|
+
json.name component.name
|
16
|
+
json.transposed_by component.transposed_by.to_s
|
17
|
+
else
|
18
|
+
json.type 'unknown'
|
19
|
+
json.content 'unknown component: '
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/exe/sb_server
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# class Prawn::Font::AFM
|
2
|
+
# # Patch normalize_encoding method to handle conversion more gracefully.
|
3
|
+
# #
|
4
|
+
# # Any valid utf-8 characters that cannot be encoded to windows-1252 are
|
5
|
+
# # replaced with the logic "not" symbol and a warning is issued identifying
|
6
|
+
# # the text that cannot be converted.
|
7
|
+
# def normalize_encoding(text)
|
8
|
+
# text.encode('utf-8')
|
9
|
+
# #text.encode 'windows-1252'
|
10
|
+
# rescue ::Encoding::UndefinedConversionError
|
11
|
+
# warn 'The following text could not be fully converted to the Windows-1252 character set:'
|
12
|
+
# warn %(#{text.gsub(/^/, '| ').rstrip})
|
13
|
+
# warn ''
|
14
|
+
# text.encode 'windows-1252', undef: :replace, invalid: :replace, replace: 'x'
|
15
|
+
# rescue ::Encoding::InvalidByteSequenceError
|
16
|
+
# raise Prawn::Errors::IncompatibleStringEncoding,
|
17
|
+
# %(Your document includes text that's not compatible with the Windows-1252 character set.
|
18
|
+
# If you need full UTF-8 support, use TTF fonts instead of PDF's built-in (AFM) fonts\n.)
|
19
|
+
# end
|
20
|
+
# end
|
data/lib/songbooks.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Songbooks
|
2
|
+
# Gem requires
|
3
|
+
require 'haml'
|
4
|
+
require 'sinatra'
|
5
|
+
require 'active_support/all'
|
6
|
+
require 'prawn'
|
7
|
+
require 'sinatra/prawn'
|
8
|
+
require 'sinatra/jbuilder'
|
9
|
+
require 'json'
|
10
|
+
|
11
|
+
# Ruby requires
|
12
|
+
require 'pathname'
|
13
|
+
|
14
|
+
# Internal requires
|
15
|
+
require 'songbooks/folder'
|
16
|
+
|
17
|
+
def self.root
|
18
|
+
Pathname.new(File.dirname(__dir__))
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.font_file(name)
|
22
|
+
root.join('app', 'fonts', "#{name}.ttf").to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.initialize_folder(directory_path)
|
26
|
+
@folder = Songbooks::Folder.new(directory_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.folder
|
30
|
+
@folder
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'songbooks/server'
|
3
|
+
|
4
|
+
module Songbooks
|
5
|
+
class CLI < Thor
|
6
|
+
desc :start, 'Starts a songbook server'
|
7
|
+
def start(directory = '.')
|
8
|
+
Songbooks.initialize_folder(File.expand_path(directory))
|
9
|
+
puts Songbooks.folder.inspect
|
10
|
+
Songbooks::Server.run!
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Songbooks
|
2
|
+
module Components
|
3
|
+
class Chord
|
4
|
+
|
5
|
+
#----------------------------------------------------------------
|
6
|
+
# CONSTANTS
|
7
|
+
#----------------------------------------------------------------
|
8
|
+
|
9
|
+
NOTES = %w(C Db D Eb E F Gb G Ab A Bb B)
|
10
|
+
|
11
|
+
NOTE_MAPPING = [
|
12
|
+
%w(C# Db),
|
13
|
+
%w(D# Eb),
|
14
|
+
%w(E# F),
|
15
|
+
%w(Fb, E),
|
16
|
+
%w(F# Gb),
|
17
|
+
%w(G# Ab),
|
18
|
+
%w(A# Bb),
|
19
|
+
%w(B#, C)
|
20
|
+
].each_with_object({}) do |(n1, n2), h|
|
21
|
+
h[n1] = n2
|
22
|
+
h[n2] = n1
|
23
|
+
end
|
24
|
+
|
25
|
+
CHORD_REGEXP = /[A-G](?:b|#)?(?:(?:m(?:aj)?|M|aug|dim|sus)?(?:[2-7]|9|13)?)?(?:\/[A-G](?:b|#)?)?/
|
26
|
+
NOTE_REGEXP = /\A[A-G](b|#)*/
|
27
|
+
|
28
|
+
# A chord in a line has to appear either right before a space or punctuation symbol
|
29
|
+
# or be the only thing left in a string
|
30
|
+
CHORD_OCCURRENCE_REGEXP = /\A(#{CHORD_REGEXP})(?:\s+|[\.,\|])|\A(#{CHORD_REGEXP})\z/
|
31
|
+
|
32
|
+
#----------------------------------------------------------------
|
33
|
+
# Parsing
|
34
|
+
#----------------------------------------------------------------
|
35
|
+
|
36
|
+
#
|
37
|
+
# Tries to parse the beginning of the given text as a chord
|
38
|
+
#
|
39
|
+
# If the text starts with a chord, this part of the string is automatically deleted.
|
40
|
+
#
|
41
|
+
# @return [Chord, NilClass] a chord object if the given text started with a valid chord
|
42
|
+
# or +nil+ otherwise.
|
43
|
+
#
|
44
|
+
def self.munch_chord(text)
|
45
|
+
if (match = text.match(CHORD_OCCURRENCE_REGEXP))
|
46
|
+
chord_name = match[1..-1].compact.first
|
47
|
+
text[0, chord_name.length] = ''
|
48
|
+
return Chord.new(chord_name)
|
49
|
+
end
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.starts_with_chord?(text)
|
54
|
+
CHORD_OCCURRENCE_REGEXP === text
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(chord_name, transposed_by: 0)
|
58
|
+
@chord_name = chord_name
|
59
|
+
@transposed_by = transposed_by
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_reader :transposed_by
|
63
|
+
|
64
|
+
def name
|
65
|
+
@chord_name
|
66
|
+
end
|
67
|
+
|
68
|
+
def base_note
|
69
|
+
name.match(NOTE_REGEXP)[0]
|
70
|
+
end
|
71
|
+
|
72
|
+
def transpose!(count)
|
73
|
+
note = base_note
|
74
|
+
note = NOTE_MAPPING[base_note] unless NOTES.include?(base_note)
|
75
|
+
index = NOTES.index(note)
|
76
|
+
transposed_note = NOTES[(index + number) % NOTES.size]
|
77
|
+
@transposed_by = count
|
78
|
+
@chord_name.sub!(note, transposed_note)
|
79
|
+
end
|
80
|
+
|
81
|
+
def transpose(count)
|
82
|
+
dup.transpose!(count)
|
83
|
+
end
|
84
|
+
|
85
|
+
def ==(other_chord)
|
86
|
+
name == other_chord.name
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_s
|
90
|
+
name
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_str
|
94
|
+
name
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|