josuelmet commited on
Commit
c7042e3
1 Parent(s): 3ea2913

Upload _Decompressor.py

Browse files
Files changed (1) hide show
  1. _Decompressor.py +252 -0
_Decompressor.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ Imports
3
+ '''
4
+ import guitarpro
5
+ from guitarpro import *
6
+ import numpy as np
7
+ import os
8
+ import pickle
9
+ import re
10
+ from tqdm import tqdm
11
+
12
+ from keras.utils import np_utils
13
+
14
+
15
+ '''
16
+ Constants
17
+ '''
18
+ # PITCH[i] = the pitch associated with midi note number i.
19
+ # For example, PITCH[69] = 'A4'
20
+ PITCH = {val : str(GuitarString(number=0, value=val)) for val in range(128)}
21
+ # MIDI[string] = the midi number associated with the note described by string.
22
+ # For example, MIDI['A4'] = 69.
23
+ MIDI = {str(GuitarString(number=0, value=val)) : val for val in range(128)}
24
+
25
+
26
+
27
+ '''
28
+ get_strings function
29
+ '''
30
+ def get_strings(chord, tuning, as_fingerings=True):
31
+
32
+ lowest_string = len(tuning) # Bass has 4 strings, while metal guitars can have 6-8 strings.
33
+
34
+ if as_fingerings:
35
+ # NEW CODE:
36
+ # Represent the tuning as the number of semitones between the open strings and the lowest string.
37
+ # i.e., relative_tuning[lowest_string] = 0
38
+ relative_tuning = {k : v - tuning[lowest_string] for k, v in tuning.items()}
39
+
40
+ chord_parts = chord.split('_')
41
+
42
+
43
+ if as_fingerings:
44
+ # NEW CODE:
45
+ root_value = int(chord_parts[0])
46
+ else:
47
+ # NEW CODE:
48
+ root_value = MIDI[chord_parts[0]]
49
+
50
+
51
+ if as_fingerings:
52
+ # NEW CODE:
53
+ if root_value < 0:
54
+ print(f'!!!!! error !!!!!\t {root_value} < 0')
55
+ else:
56
+ # OLD CODE:
57
+ if root_value < tuning[lowest_string]:
58
+ print(f'!!!!! error !!!!!\t {root_value} < {tuning[lowest_string]}')
59
+
60
+ # Using the tuning, get a list of all possible fret positions for the root note.
61
+ if as_fingerings:
62
+ # NEW CODE:
63
+ tuning_values = np.array(list(relative_tuning.values()))
64
+ else:
65
+ # OLD CODE:
66
+ tuning_values = np.array(list(tuning.values()))
67
+
68
+ fingerings = root_value - tuning_values
69
+
70
+
71
+ # + 1 because tuning[] is 1-indexed.
72
+ string = np.where(fingerings >= 0, fingerings, np.inf).argmin() + 1
73
+ fret = fingerings[string-1]
74
+
75
+ # If we are just playing a single note, then the function can just return what it has now.
76
+ if len(chord_parts) == 1:
77
+ return [(fret, string)]
78
+
79
+ # If the chord requires a very high pitch, lower its fingering to the second-highest string,
80
+ # so as to save the highest string for the other part of the chord.
81
+ if string == 1:
82
+ string = 2
83
+ fret = fingerings[string-1]
84
+
85
+ if chord_parts[1] == '5':
86
+ upper_value = root_value + 7 # perfect fifth
87
+ elif chord_parts[1] == 'dim5':
88
+ upper_value = root_value + 6 # tritone
89
+ elif chord_parts[1] == '4':
90
+ upper_value = root_value + 5
91
+ else:
92
+ upper_value = root_value + 5 # in case of an error, assume that the upper value is a perfect 4th above the root.
93
+
94
+
95
+ if as_fingerings:
96
+ # NEW CODE:
97
+ upper_fret = upper_value - relative_tuning[string-1]
98
+ else:
99
+ # OLD CODE:
100
+ upper_fret = upper_value - tuning[string-1]
101
+
102
+
103
+ if upper_fret < 0:
104
+ # There are some rare cases where the chord cannot be played given a tuning.
105
+ # For example, a tritone or a perfect 4th with root C2 in a drop-C guitar.
106
+ # In that case, just return the root note.
107
+ return [(fret, string)]
108
+
109
+ return [(fret, string), (upper_fret, string-1)]
110
+
111
+
112
+
113
+
114
+ class SongWriter:
115
+
116
+ def __init__(self, initialTempo):
117
+ self.song = guitarpro.models.Song(tempo=initialTempo)
118
+ self.song.tracks = [] # Initialize song.tracks to an empty list.
119
+
120
+ self.currentTempo = initialTempo
121
+
122
+ '''
123
+ decompress_track function
124
+ '''
125
+ def decompress_track(self, track, tuning, tempo=None, instrument=None, name=None, as_fingerings=True):
126
+
127
+ # Instantiate the Midi Channel / Instrument for this track,
128
+ # making sure to avoid channel 9 (the drum channel)
129
+ channel = MidiChannel(channel = len(self.song.tracks) + (len(self.song.tracks) >= 9),
130
+ effectChannel = len(self.song.tracks) + (len(self.song.tracks) >= 9))
131
+ if instrument is not None:
132
+ channel.instrument = instrument
133
+
134
+ # Instantiate the name for this track
135
+ if name is None:
136
+ name = f'Track {len(self.song.tracks) + 1}'
137
+
138
+ # Pre-format the name to avoid any characters that the tab file format doesn't like.
139
+ # Keep only spaces and alphanumeric characters.
140
+ name = name.split('(')[0]
141
+ name = re.sub(r' \W+', '', name)
142
+
143
+ # Instantiate the actual Track itself
144
+ self.song.tracks.append(Track(song=self.song, name=name, channel=channel))
145
+
146
+ # Calculate the dict key corresponding to the lowest-tuned string.
147
+ lowest_string = len(tuning)
148
+
149
+ # Set the guitar tuning for the instrument.
150
+ self.song.tracks[-1].strings = [GuitarString(number=num, value=val) for num, val in tuning.items()]
151
+
152
+
153
+
154
+
155
+ # The first measureHeader and measure are already added by default. Let's remove them to make things more standard.
156
+ if len(self.song.tracks) == 1:
157
+ self.song.measureHeaders = []
158
+ self.song.tracks[0].measures = []
159
+
160
+ startingTrackIdx = len(self.song.measureHeaders)
161
+
162
+ for i in range(startingTrackIdx, startingTrackIdx+len(track)):
163
+ start = guitarpro.Duration.quarterTime * (1 + i*6)
164
+
165
+ self.song.addMeasureHeader(MeasureHeader(number=i+1, start=start))
166
+
167
+ # Add new measure to every existing track.
168
+ for existing_track in self.song.tracks:
169
+ existing_track.measures.append( Measure(existing_track, self.song.measureHeaders[i]) )
170
+
171
+
172
+
173
+
174
+
175
+ for m_i, measure in enumerate(self.song.tracks[-1].measures):
176
+
177
+ if m_i < startingTrackIdx:
178
+ continue # Skip tracks that have already been written to.
179
+
180
+ # "beats" starts off as an empy array [].
181
+ voice = measure.voices[0]
182
+ beats = voice.beats
183
+
184
+ #print(m_i - startingTrackIdx)
185
+ # For the m_i-th measure, get the indices b_i and the beats track_beat of the compressed song.
186
+ for b_i, track_beat in enumerate(track[m_i - startingTrackIdx]):
187
+
188
+ #print(f'\t{b_i}')
189
+
190
+ # If a tempo change is needed:
191
+ if tempo is not None and tempo != self.currentTempo:
192
+ # Implement the tempo change, then update the current tempo.
193
+ effect = BeatEffect(mixTableChange=MixTableChange(tempo=MixTableItem(value=tempo), hideTempo=False))
194
+ self.currentTempo = tempo
195
+ else:
196
+ effect = BeatEffect()
197
+
198
+
199
+ chord = track_beat[0]
200
+ duration = Duration(value=int(track_beat[1]), isDotted=bool(track_beat[2]))
201
+
202
+ # since "beats" is empty, we can append Beat objects to it.
203
+ beats.append(Beat(voice, duration=duration, effect=effect))
204
+ if chord == 'rest':
205
+ beats[b_i].status = guitarpro.BeatStatus.rest
206
+
207
+ elif chord == 'tied':
208
+ # If this tied note is the first beat in its measure:
209
+ if b_i == 0:
210
+ # If this tied note is the first beat in the first measure:
211
+ if m_i == 0:
212
+ # Designate this beat as a rest, then move on to the next beat.
213
+ beats[b_i].status = guitarpro.BeatStatus.rest
214
+ continue
215
+ else:
216
+ # Get the last Beat object from the previous Measure.
217
+ previous_beats = self.song.tracks[-1].measures[m_i-1].voices[0].beats
218
+ if len(previous_beats) == 0:
219
+ beats[b_i].status = guitarpro.BeatStatus.rest
220
+ continue
221
+ previous_beat = previous_beats[-1]
222
+ else:
223
+ # Get the previous Beat object from the current Measure.
224
+ previous_beat = beats[b_i-1]
225
+
226
+ for note in previous_beat.notes:
227
+ beats[b_i].notes.append(Note(beat=beats[b_i], value=note.value, string=note.string, type=NoteType.tie))
228
+
229
+
230
+
231
+
232
+ elif chord == 'dead':
233
+ beats[b_i].notes.append(Note(beat=beats[b_i], value=0, string=lowest_string, type=NoteType.dead))
234
+ beats[b_i].notes.append(Note(beat=beats[b_i], value=0, string=lowest_string-1, type=NoteType.dead))
235
+ beats[b_i].notes.append(Note(beat=beats[b_i], value=0, string=lowest_string-2, type=NoteType.dead))
236
+
237
+ else:
238
+ for fret, string in get_strings(chord, tuning, as_fingerings):
239
+ noteEffect = NoteEffect(palmMute=track_beat[3])
240
+ beats[b_i].notes.append(Note(beat=beats[b_i], value=fret, string=string,
241
+ type=NoteType.normal, effect=noteEffect))
242
+
243
+
244
+ #print('\t\t', chord, '\t', duration)
245
+
246
+
247
+ # Lastly, return the song so that it can be saved to a .gp5 file.
248
+ # return new_song
249
+
250
+
251
+ def write(self, filename):
252
+ guitarpro.write(self.song, filename)