guitar_pro_parser 0.0.1

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 (36) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +72 -0
  6. data/Rakefile +7 -0
  7. data/guitar_pro_parser.gemspec +25 -0
  8. data/lib/guitar_pro_parser/bar.rb +29 -0
  9. data/lib/guitar_pro_parser/bar_settings.rb +74 -0
  10. data/lib/guitar_pro_parser/beat.rb +42 -0
  11. data/lib/guitar_pro_parser/channel.rb +25 -0
  12. data/lib/guitar_pro_parser/chord_diagram.rb +14 -0
  13. data/lib/guitar_pro_parser/guitar_pro_helper.rb +73 -0
  14. data/lib/guitar_pro_parser/io/input_stream.rb +110 -0
  15. data/lib/guitar_pro_parser/io/reader.rb +759 -0
  16. data/lib/guitar_pro_parser/note.rb +71 -0
  17. data/lib/guitar_pro_parser/page_setup.rb +34 -0
  18. data/lib/guitar_pro_parser/song.rb +113 -0
  19. data/lib/guitar_pro_parser/track.rb +125 -0
  20. data/lib/guitar_pro_parser/version.rb +3 -0
  21. data/lib/guitar_pro_parser.rb +25 -0
  22. data/spec/lib/guitar_pro_parser/bar_settings_spec.rb +176 -0
  23. data/spec/lib/guitar_pro_parser/beat_spec.rb +79 -0
  24. data/spec/lib/guitar_pro_parser/channel_spec.rb +44 -0
  25. data/spec/lib/guitar_pro_parser/guitar_pro_helper_spec.rb +11 -0
  26. data/spec/lib/guitar_pro_parser/io/input_stream_spec.rb +101 -0
  27. data/spec/lib/guitar_pro_parser/note_spec.rb +55 -0
  28. data/spec/lib/guitar_pro_parser/page_setup_spec.rb +26 -0
  29. data/spec/lib/guitar_pro_parser/song_spec.rb +121 -0
  30. data/spec/lib/guitar_pro_parser/track_spec.rb +211 -0
  31. data/spec/lib/guitar_pro_parser_spec.rb +51 -0
  32. data/spec/spec_helper.rb +23 -0
  33. data/spec/tabs/tab.gp4 +0 -0
  34. data/spec/tabs/tab.gp5 +0 -0
  35. data/spec/tabs/test_musical_directions.gp5 +0 -0
  36. metadata +144 -0
data/.gitignore ADDED
@@ -0,0 +1,18 @@
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
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in guitar_pro_parser.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Alexander Borovykh
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.
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # GuitarProParser
2
+
3
+ It is a gem for Ruby that allows to read Guitar Pro files.
4
+ Now it supports Guitar Pro 4 and 5 files. Version 3 should work but is not tested at all.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'guitar_pro_parser'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install guitar_pro_parser
19
+
20
+ ## Usage
21
+
22
+ Require the library:
23
+
24
+ require 'guitar_pro_parser'
25
+
26
+ Read the file:
27
+
28
+ song = GuitarProParser.read_file('path_to_file')
29
+
30
+ Now you can access song's properties like that:
31
+
32
+ # Get some attributes of the song
33
+ puts song.title
34
+ puts song.artist
35
+ puts song.bpm
36
+
37
+ # Or read notes
38
+ song.tracks.each do |track|
39
+ track.bars.each do |bar|
40
+ puts "There are #{bar.voices[:lead].count} beats in lead voice"
41
+ puts "There are #{bar.voices[:bass].count} beats in bass voice" unless bar.voices[:bass].nil?
42
+
43
+ bar.voice[:lead] do |beat|
44
+ beat.strings.each do |string, note|
45
+ puts "Play #{string} string on the #{note.fret} fret."
46
+ puts "Note's duration is #{note.duration}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ If you don't need any information about beats, notes and other music stuff you can read headers only:
53
+
54
+ song = GuitarProParser.read_headers('path_to_file')
55
+
56
+ # You'll have title, subtitle, artist, etc.
57
+ song.title # => 'Title'
58
+
59
+ # But no notes
60
+ song.tracks.first.bars # => []
61
+
62
+ All available methods and attributes could be found in the source code. :)
63
+
64
+ TODO: Write documentation.
65
+
66
+ ## Contributing
67
+
68
+ 1. Fork it
69
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
70
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
71
+ 4. Push to the branch (`git push origin my-new-feature`)
72
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'guitar_pro_parser/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "guitar_pro_parser"
8
+ spec.version = GuitarProParser::VERSION
9
+ spec.authors = ["Alexander Borovykh"]
10
+ spec.email = ["immaculate.pine@gmail.com"]
11
+ spec.description = %q{Gem for reading Guitar Pro files}
12
+ spec.summary = %q{It is a gem that allows to read Guitar Pro files. Now it supports Guitar Pro 4 and 5 files. Version 3 should work but is not tested at all.}
13
+ spec.homepage = "https://github.com/ImmaculatePine/guitar_pro_parser"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ end
@@ -0,0 +1,29 @@
1
+ module GuitarProParser
2
+
3
+ # This class represents bars as containers of notes.
4
+ #
5
+ # == Attributes
6
+ #
7
+ # All attributes are read-only
8
+ #
9
+ # * +voices+ (hash) Voices of this bar.
10
+ # Guitar Pro 5 files has :lead and :bass voices.
11
+ # Guitar Pro 4 and less files has only :lead voice.
12
+ #
13
+ #
14
+ class Bar
15
+
16
+ attr_accessor :voices
17
+
18
+ def initialize
19
+ @voices = {lead: [], bass: []}
20
+ end
21
+
22
+ # Returns selected beat of selected voice
23
+ def get_beat(number, voice = :lead)
24
+ @voices.fetch(voice).fetch(number)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,74 @@
1
+ module GuitarProParser
2
+
3
+ # This class represents settings of bars (measures).
4
+ #
5
+ # == Attributes
6
+ #
7
+ # *+new_time_signature+* (hash) Info about new time signature in format
8
+ # { numerator: 4, denominator: 4, beam_eight_notes_by_values: [2, 2, 2, 2] }
9
+ # or nil if doesn't present
10
+ #
11
+ # * +has_start_of_repeat+ (boolean) Is there start of repeat symbol?
12
+ # * +has_end_of_repeat+ (boolean) Is there end of repeat symbol?
13
+ # * +repeats_count+ (integer) Denominator of time signature if @has_end_of_repeat presents
14
+ # * +alternate_endings+ (array) Contains numbers of all alternate endings in Guitar Pro 5
15
+ # or the largest number of alternate endings in Guitar Pro 4 or less
16
+ #
17
+ # * +marker+ (hash) Info about marker in format:
18
+ # { name: "Marker name", color: [255, 0, 0] }
19
+ # or nil if doesn't present
20
+ #
21
+ # * +new_key_signature+ (hash) Info about new key signature in format:
22
+ # { key: 0, scale: :major }
23
+ # or nil if doesn't present.
24
+ # Key is encoded like this: # TODO: Convert to more readable format
25
+ # -2 Bb (bb)
26
+ # -1 F (b)
27
+ # 0 C
28
+ # 1 G (#)
29
+ # 2 D (##)
30
+ # Scale can be :major or :minor
31
+ #
32
+ # * +triplet_feel+ (symbol) Can be :no_triplet_feel, :triplet_8th or :triplet_16th
33
+ # * +double_bar+ (boolean) Is this bar double?
34
+ #
35
+ class BarSettings
36
+
37
+ attr_accessor :new_time_signature,
38
+ :has_start_of_repeat,
39
+ :has_end_of_repeat,
40
+ :repeats_count,
41
+ :alternate_endings,
42
+ :marker,
43
+ :new_key_signature,
44
+ :triplet_feel,
45
+ :double_bar
46
+
47
+ def initialize
48
+ # Initialize attributes by default values
49
+ @new_time_signature = nil
50
+ @has_start_of_repeat = false
51
+ @has_end_of_repeat = false
52
+ @repeats_count = 0
53
+ @alternate_endings = []
54
+ @marker = nil
55
+ @new_key_signature = nil
56
+ @triplet_feel = :no_triplet_feel
57
+ @double_bar = false
58
+ end
59
+
60
+ def set_new_key_signature(key, scale)
61
+ @new_key_signature = { key: key, scale: scale }
62
+ end
63
+
64
+ def set_new_time_signature(numerator, denominator, beam_eight_notes_by_values)
65
+ @new_time_signature = { numerator: numerator, denominator: denominator, beam_eight_notes_by_values: beam_eight_notes_by_values }
66
+ end
67
+
68
+ def set_marker(name, color)
69
+ @marker = { name: name, color: color }
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,42 @@
1
+ module GuitarProParser
2
+
3
+ class Beat
4
+
5
+ attr_accessor :dotted,
6
+ :mix_table,
7
+ :rest,
8
+ :duration,
9
+ :tuplet,
10
+ :chord_diagram,
11
+ :text,
12
+ :effects,
13
+ :strings,
14
+ :transpose
15
+
16
+ def initialize
17
+ # Initialize attributes by default values
18
+ @dotted = false
19
+ @mix_table = nil
20
+ @rest = nil
21
+
22
+ @duration = :eighth
23
+ @tuplet = nil
24
+ @chord_diagram = nil
25
+ @text = nil
26
+ @effects = {}
27
+ @strings = {}
28
+
29
+ @transpose = nil
30
+ end
31
+
32
+ def has_effect?(effect)
33
+ @effects.include?(effect)
34
+ end
35
+
36
+ def add_effect(effect)
37
+ @effects[effect] = nil
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,25 @@
1
+ module GuitarProParser
2
+
3
+ class Channel
4
+
5
+ attr_accessor :instrument,
6
+ :volume,
7
+ :pan,
8
+ :chorus,
9
+ :reverb,
10
+ :phaser,
11
+ :tremolo
12
+
13
+ def initialize
14
+ @instrument = 0
15
+ @volume = 13
16
+ @pan = 8
17
+ @chorus = 0
18
+ @reverb = 0
19
+ @phaser = 0
20
+ @tremolo = 0
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,14 @@
1
+ module GuitarProParser
2
+
3
+ class ChordDiagram
4
+
5
+ attr_accessor :name, :start_fret, :frets
6
+
7
+ def initialize
8
+ @name = ''
9
+ @start_fret = 0
10
+ @frets = []
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,73 @@
1
+ module GuitarProHelper
2
+
3
+ NOTES = %w(C C# D D# E F F# G G# A A# B)
4
+ VOICES = [:lead, :bass]
5
+ FINGERS = [:thumb, :index, :middle, :ring, :pinky]
6
+ BEND_TYPES = [:none, :bend, :bend_and_release, :bend_release_bend, :prebend, :prebend_and_release,
7
+ :tremolo_dip, :tremolo_dive, :tremolo_release_up, :tremolo_inverted_dip, :tremolo_return, :tremolo_release_down]
8
+ BEND_VIBRATO_TYPES = [:none, :fast, :average, :slow]
9
+ MUSICAL_DIRECTIONS = [:coda, :double_coda, :segno, :segno_segno, :fine, :da_capo,
10
+ :da_capo_al_coda, :da_capo_al_double_coda, :da_capo_al_fine,
11
+ :da_segno, :da_segno_al_coda, :da_segno_al_double_coda,
12
+ :da_segno_al_fine, :da_segno_segno, :da_segno_segno_al_coda,
13
+ :da_segno_segno_al_double_coda, :da_segno_segno_al_fine,
14
+ :da_coda, :da_double_coda]
15
+ TRIPLET_FEEL = [:no_triplet_feel, :triplet_8th, :triplet_16th]
16
+ REST_TYPES = { '0' => :empty_beat,
17
+ '2' => :rest }
18
+ DURATIONS = { '-2' => :whole,
19
+ '-1' => :half,
20
+ '0' => :quarter,
21
+ '1' => :eighth,
22
+ '2' => :sixteens,
23
+ '3' => :thirty_second,
24
+ '4' => :sixty_fourth }
25
+ STRING_EFFECTS = [:tremolo_bar, :tapping, :slapping, :popping]
26
+ STROKE_EFFECT_SPEEDS = [:none, 128, 64, 32, 16, 8, 4]
27
+ STROKE_DIRECTIONS = [:none, :up, :down]
28
+ NOTE_TYPES = [:normal, :tie, :dead]
29
+ NOTE_DYNAMICS = %w(ppp pp p mp mf f ff fff)
30
+ GRACE_NOTE_TRANSITION_TYPES = [:none, :slide, :bend, :hammer]
31
+ GRACE_NOTE_DURATIONS = { '3' => 16, '2' => 32, '1' => 64 }
32
+ TREMOLO_PICKING_SPEEDS = { '3' => 32, '2' => 16, '1' => 8 }
33
+
34
+ SLIDE_TYPES = [:no_slide, :shift_slide, :legato_slide, :slide_out_and_downwards, :slide_out_and_upwards, :slide_in_from_below, :slide_in_from_above]
35
+ MAP_SLIDE_TYPES_GP5 = { '0'=>0, '1'=>1, '2'=>2, '4'=>3, '8'=>4, '16'=>5, '32'=>6 }
36
+ MAP_SLIDE_TYPES_GP4 = { '-2'=>0, '-1'=>1, '0'=>2, '1'=>3, '2'=>4, '3'=>5, '4'=>6 }
37
+
38
+ HARMONIC_TYPES = [:none, :natural, :artificial, :tapped, :pinch, :semi]
39
+ TRILL_PERIODS = [4, 8, 16]
40
+
41
+ # Macros to create boolean instance variables' getters like this:
42
+ # attr_boolean :complete
43
+ # generates
44
+ # complete?
45
+ # method that returns @complete instance variable
46
+ def attr_boolean(*variables)
47
+ variables.each do |variable|
48
+ define_method("#{variable}?") do
49
+ instance_variable_get("@#{variable}")
50
+ end
51
+ end
52
+ end
53
+
54
+ # Converts note's digit representation to its string equivalent:
55
+ # 0 for C0, 1 for C#0, etc.
56
+ def GuitarProHelper.digit_to_note(digit)
57
+ note_index = 0
58
+ octave = 0
59
+ digit.times do |i|
60
+ note_index = note_index + 1
61
+ if note_index == NOTES.count
62
+ note_index = 0
63
+ octave = octave + 1
64
+ end
65
+ end
66
+
67
+ "#{NOTES.fetch(note_index)}#{octave.to_s}"
68
+ end
69
+
70
+
71
+ # TODO: Create helper to convert number of increments of .1dB to float for equalizers
72
+
73
+ end
@@ -0,0 +1,110 @@
1
+ module GuitarProParser
2
+
3
+ class InputStream
4
+
5
+ INTEGER_LENGTH = 4
6
+ SHORT_INTEGER_LENGTH = 2
7
+ BYTE_LENGTH = 1
8
+
9
+ attr_accessor :offset
10
+
11
+ def initialize file_path
12
+ @file_path = file_path
13
+ @offset = 0
14
+ end
15
+
16
+ # Reads unsigned integer (4 bytes)
17
+ def read_integer
18
+ value = IO.binread(@file_path, INTEGER_LENGTH, @offset).unpack('i')[0]
19
+ skip_integer
20
+ value
21
+ end
22
+
23
+ # Reads signed integer (4 bytes)
24
+ def read_signed_integer
25
+ value = IO.binread(@file_path, INTEGER_LENGTH, @offset).unpack('I')[0]
26
+ skip_integer
27
+ value
28
+ end
29
+
30
+ # Reads unsigned short integer (2 bytes)
31
+ def read_short_integer
32
+ value = IO.binread(@file_path, SHORT_INTEGER_LENGTH, @offset).unpack('S_')[0]
33
+ skip_short_integer
34
+ value
35
+ end
36
+
37
+ # Reads signed short integer (2 bytes)
38
+ def read_signed_short_integer
39
+ value = IO.binread(@file_path, SHORT_INTEGER_LENGTH, @offset).unpack('s_')[0]
40
+ skip_short_integer
41
+ value
42
+ end
43
+
44
+ # Reads unsigned byte (8 bits) (0..255)
45
+ def read_byte
46
+ value = IO.binread(@file_path, BYTE_LENGTH, @offset).unpack('C')[0]
47
+ skip_byte
48
+ value
49
+ end
50
+
51
+ # Reads signed byte (8 bits) (-127..127)
52
+ def read_signed_byte
53
+ value = IO.binread(@file_path, BYTE_LENGTH, @offset).unpack('c')[0]
54
+ skip_byte
55
+ value
56
+ end
57
+
58
+ # Reads signed byte as boolean (8 bit)
59
+ def read_boolean
60
+ !read_byte.zero?
61
+ end
62
+
63
+ # Reads string with specified length
64
+ def read_string(length)
65
+ value = IO.binread(@file_path, length, @offset)
66
+ increment_offset(length)
67
+ value
68
+ end
69
+
70
+ # Reads byte as 8-bit bitmask
71
+ def read_bitmask
72
+ bits = []
73
+ value = read_byte
74
+ value.to_s(2).each_char { |bit| bits << !bit.to_i.zero? }
75
+ bits.reverse!
76
+ bits << false while bits.count < 8
77
+ bits
78
+ end
79
+
80
+ # Reads data chunk from file.
81
+ #
82
+ # Chunk's format is:
83
+ # 4 bytes - field length (including string length byte that follows this value)
84
+ # 1 byte - string length (N)
85
+ # N bytes - string
86
+ def read_chunk
87
+ skip_integer
88
+ string_length = read_byte
89
+ read_string string_length
90
+ end
91
+
92
+ def increment_offset delta
93
+ @offset = @offset + delta
94
+ end
95
+
96
+ def skip_integer
97
+ increment_offset(INTEGER_LENGTH)
98
+ end
99
+
100
+ def skip_short_integer
101
+ increment_offset(SHORT_INTEGER_LENGTH)
102
+ end
103
+
104
+ def skip_byte
105
+ increment_offset(BYTE_LENGTH)
106
+ end
107
+
108
+ end
109
+
110
+ end