MORTM 1.2__tar.gz → 2.0b3__tar.gz
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.
- {mortm-1.2 → mortm-2.0b3}/MORTM.egg-info/PKG-INFO +1 -1
- {mortm-1.2 → mortm-2.0b3}/MORTM.egg-info/SOURCES.txt +1 -1
- {mortm-1.2 → mortm-2.0b3}/PKG-INFO +1 -1
- {mortm-1.2 → mortm-2.0b3}/mortm/convert.py +83 -101
- mortm-2.0b3/mortm/datasets.py +36 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/de_convert.py +12 -6
- mortm-2.0b3/mortm/mortm.py +398 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/progress.py +2 -3
- {mortm-1.2 → mortm-2.0b3}/mortm/reinforcement.py +2 -2
- {mortm-1.2 → mortm-2.0b3}/mortm/rpr.py +2 -71
- mortm-2.0b3/mortm/token.py +296 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/tokenizer.py +21 -20
- {mortm-1.2 → mortm-2.0b3}/mortm/train.py +65 -45
- {mortm-1.2 → mortm-2.0b3}/setup.py +1 -1
- mortm-1.2/mortm/aya_node.py +0 -271
- mortm-1.2/mortm/datasets.py +0 -114
- mortm-1.2/mortm/mortm.py +0 -352
- {mortm-1.2 → mortm-2.0b3}/MORTM.egg-info/dependency_links.txt +0 -0
- {mortm-1.2 → mortm-2.0b3}/MORTM.egg-info/top_level.txt +0 -0
- {mortm-1.2 → mortm-2.0b3}/README.md +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/PositionalEncoding.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/RelativePositionalRepresentations.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/__init__.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/constants.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/eval.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/generate.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/gmail_messanger.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/loss.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/mask.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/messager.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/mortm/noam.py +0 -0
- {mortm-1.2 → mortm-2.0b3}/setup.cfg +0 -0
|
@@ -7,7 +7,6 @@ MORTM.egg-info/top_level.txt
|
|
|
7
7
|
mortm/PositionalEncoding.py
|
|
8
8
|
mortm/RelativePositionalRepresentations.py
|
|
9
9
|
mortm/__init__.py
|
|
10
|
-
mortm/aya_node.py
|
|
11
10
|
mortm/constants.py
|
|
12
11
|
mortm/convert.py
|
|
13
12
|
mortm/datasets.py
|
|
@@ -23,5 +22,6 @@ mortm/noam.py
|
|
|
23
22
|
mortm/progress.py
|
|
24
23
|
mortm/reinforcement.py
|
|
25
24
|
mortm/rpr.py
|
|
25
|
+
mortm/token.py
|
|
26
26
|
mortm/tokenizer.py
|
|
27
27
|
mortm/train.py
|
|
@@ -2,12 +2,12 @@ from typing import List, Any
|
|
|
2
2
|
|
|
3
3
|
import mido
|
|
4
4
|
import numpy as np
|
|
5
|
+
from numpy import ndarray
|
|
5
6
|
from AGSM.convert import ConvTempo
|
|
6
7
|
from pretty_midi.pretty_midi import PrettyMIDI, Instrument, Note, TimeSignature
|
|
7
8
|
from abc import abstractmethod, ABC
|
|
8
9
|
from typing import TypeVar, Generic
|
|
9
|
-
from . import
|
|
10
|
-
from .aya_node import Token
|
|
10
|
+
from .token import Token
|
|
11
11
|
from .tokenizer import Tokenizer
|
|
12
12
|
|
|
13
13
|
T = TypeVar("T")
|
|
@@ -19,10 +19,9 @@ class _AbstractMidiToAyaNode(ABC):
|
|
|
19
19
|
self.file_name = file_name
|
|
20
20
|
self.is_error = False
|
|
21
21
|
self.instance = instance
|
|
22
|
-
self.token_converter: List[Token] = tokenizer.
|
|
22
|
+
self.token_converter: List[Token] = tokenizer.music_token_list
|
|
23
23
|
self.error_reason: str = "不明なエラー"
|
|
24
24
|
self.tokenizer = tokenizer
|
|
25
|
-
|
|
26
25
|
if midi_data is not None:
|
|
27
26
|
self.midi_data: PrettyMIDI = midi_data
|
|
28
27
|
else:
|
|
@@ -99,10 +98,10 @@ class _AbstractMidiToAyaNode(ABC):
|
|
|
99
98
|
def convert(self):
|
|
100
99
|
pass
|
|
101
100
|
|
|
102
|
-
class
|
|
101
|
+
class MIDIToSequence(_AbstractMidiToAyaNode):
|
|
103
102
|
|
|
104
103
|
def __init__(self, tokenizer: Tokenizer, directory: str, file_name: str, program_list, midi_data=None):
|
|
105
|
-
super().__init__(
|
|
104
|
+
super().__init__(MIDIToSequence, tokenizer, directory, file_name, program_list, midi_data)
|
|
106
105
|
self.aya_node = [0]
|
|
107
106
|
|
|
108
107
|
def convert(self):
|
|
@@ -134,7 +133,6 @@ class MidiToAyaNode(_AbstractMidiToAyaNode):
|
|
|
134
133
|
inst: Instrument = inst
|
|
135
134
|
if not inst.is_drum and inst.program in self.program_list:
|
|
136
135
|
aya_node_inst = self.ct_aya_node(inst)
|
|
137
|
-
|
|
138
136
|
self.aya_node = self.aya_node + aya_node_inst
|
|
139
137
|
program_count += 1
|
|
140
138
|
|
|
@@ -159,8 +157,7 @@ class MidiToAyaNode(_AbstractMidiToAyaNode):
|
|
|
159
157
|
aya_node_inst = []
|
|
160
158
|
back_note = None
|
|
161
159
|
|
|
162
|
-
|
|
163
|
-
split_count: int = 1
|
|
160
|
+
clip_count = 0
|
|
164
161
|
|
|
165
162
|
sorted_notes = sorted(inst.notes, key=lambda notes: notes.start)
|
|
166
163
|
|
|
@@ -168,36 +165,31 @@ class MidiToAyaNode(_AbstractMidiToAyaNode):
|
|
|
168
165
|
note: Note = note
|
|
169
166
|
|
|
170
167
|
tempo = self.get_tempo(note.start)
|
|
171
|
-
measure_time = (60 / tempo) * 4
|
|
172
|
-
|
|
173
|
-
if clip_time >= measure_time * 8 * split_count:
|
|
174
|
-
aya_node_inst = self.marge_clip(clip, aya_node_inst)
|
|
175
|
-
|
|
176
|
-
clip = np.array([], dtype=int)
|
|
177
|
-
back_note = None
|
|
178
|
-
split_count += 1
|
|
179
|
-
|
|
180
|
-
if back_note is None:
|
|
181
|
-
clip = np.append(clip, self.tokenizer.get(constants.START_SEQ_TOKEN))
|
|
182
168
|
|
|
183
169
|
for conv in self.token_converter:
|
|
184
170
|
conv: Token = conv
|
|
185
171
|
|
|
186
|
-
token = conv(back_notes=back_note, note=note, tempo=tempo)
|
|
172
|
+
token = conv(inst=inst, back_notes=back_note, note=note, tempo=tempo)
|
|
173
|
+
|
|
187
174
|
if token is not None:
|
|
175
|
+
if conv.token_type == "<SME>":
|
|
176
|
+
clip_count += 1
|
|
177
|
+
if clip_count >= 8:
|
|
178
|
+
aya_node_inst = self.marge_clip(clip, aya_node_inst)
|
|
179
|
+
clip = np.array([], dtype=int)
|
|
180
|
+
back_note = None
|
|
181
|
+
clip_count = 0
|
|
182
|
+
|
|
188
183
|
token_id = self.tokenizer.get(token)
|
|
189
184
|
clip = np.append(clip, token_id)
|
|
190
185
|
|
|
191
186
|
back_note = note
|
|
192
|
-
clip_time = note.start
|
|
193
187
|
|
|
194
|
-
if len(clip) >
|
|
195
|
-
clip = np.append(clip, self.tokenizer.get(constants.END_SEQ_TOKEN))
|
|
188
|
+
if len(clip) > 4:
|
|
196
189
|
aya_node_inst = self.marge_clip(clip, aya_node_inst)
|
|
197
190
|
return aya_node_inst
|
|
198
191
|
|
|
199
192
|
def marge_clip(self, clip, aya_node_inst):
|
|
200
|
-
#clip = np.append(clip, self.tokenizer.get(constants.END_SEQ_TOKEN))
|
|
201
193
|
aya_node_inst.append(clip)
|
|
202
194
|
|
|
203
195
|
return aya_node_inst
|
|
@@ -214,34 +206,28 @@ class MidiToAyaNode(_AbstractMidiToAyaNode):
|
|
|
214
206
|
else:
|
|
215
207
|
return False, self.error_reason
|
|
216
208
|
|
|
217
|
-
def ct_tempo(self):
|
|
218
|
-
try:
|
|
219
|
-
ct = ConvTempo(directory=self.directory + "/" + self.file_name, midi_data=self.midi_data, change_tempo=120)
|
|
220
|
-
ct.convert()
|
|
221
|
-
self.midi_data = ct.midi_data
|
|
222
|
-
except OSError or IndexError or ValueError or mido.midifiles.meta.KeySignatureError or EOFError or KeyError or ZeroDivisionError:
|
|
223
|
-
self.error_reason = f"{self.directory}/{self.file_name}でテンポ変換中にエラーが発生。処理を中断します。"
|
|
224
|
-
self.is_error = True
|
|
225
209
|
|
|
210
|
+
class MidiToSequece(_AbstractMidiToAyaNode):
|
|
211
|
+
def __init__(self, tokenizer: Tokenizer, directory: str, file_name: str, program_list):
|
|
212
|
+
super().__init__(MidiToSequece, tokenizer, directory, file_name, program_list)
|
|
213
|
+
self.sequence_dict: dict = dict()
|
|
214
|
+
self.sequence_count = 0
|
|
215
|
+
self.context = 8
|
|
226
216
|
|
|
227
|
-
|
|
217
|
+
def add_sequence(self, seq_pare: dict):
|
|
218
|
+
self.sequence_dict[f'array_{self.sequence_count}'] = seq_pare
|
|
219
|
+
self.sequence_count += 1
|
|
228
220
|
|
|
229
221
|
def save(self, save_directory: str) -> [bool, str]:
|
|
230
222
|
if not self.is_error:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
np.savez(save_directory + "/" + self.file_name, **array_dict)
|
|
235
|
-
return True, "処理が正常に終了しました。"
|
|
223
|
+
if len(self.sequence_dict) > 2:
|
|
224
|
+
np.savez(save_directory + self.file_name, **self.sequence_dict)
|
|
225
|
+
return True, "正常に終了しました。"
|
|
236
226
|
else:
|
|
237
227
|
return False, "オブジェクトが何らかの理由で見つかりませんでした。"
|
|
238
228
|
else:
|
|
239
229
|
return False, self.error_reason
|
|
240
230
|
|
|
241
|
-
def __init__(self, tokenizer: Tokenizer, directory: str, file_name: str, program_list, midi_data=None):
|
|
242
|
-
super().__init__(MidiToAyaNode_TGT, tokenizer, directory, file_name, program_list, midi_data)
|
|
243
|
-
self.aya_node: List[dict] = []
|
|
244
|
-
|
|
245
231
|
def convert(self):
|
|
246
232
|
if not self.is_error:
|
|
247
233
|
program_count = 0
|
|
@@ -249,76 +235,72 @@ class MidiToAyaNode_TGT(_AbstractMidiToAyaNode):
|
|
|
249
235
|
for inst in self.midi_data.instruments:
|
|
250
236
|
inst: Instrument = inst
|
|
251
237
|
if not inst.is_drum and inst.program in self.program_list:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
self.aya_node = self.aya_node + aya_node_inst
|
|
238
|
+
self.ct_aya_node(inst)
|
|
255
239
|
program_count += 1
|
|
256
240
|
|
|
257
241
|
if program_count == 0:
|
|
258
242
|
self.is_error = True
|
|
259
243
|
self.error_reason = f"{self.directory}/{self.file_name}に、欲しい楽器がありませんでした。"
|
|
260
244
|
|
|
261
|
-
def ct_aya_node(self, inst: Instrument)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
clip = [<START>, S, P, V, D, H, S, P, V, D, H ...<END>]
|
|
266
|
-
さらに60秒ごとにスプリットし、以下のような配列構造を作る。
|
|
267
|
-
aya_node_inst = [clip_1, clip_2 ... clip_n]
|
|
268
|
-
|
|
269
|
-
よって、二次元配列のndarrayを返す。
|
|
270
|
-
:param inst: インストゥルメント
|
|
271
|
-
:return: 60秒にクリッピングされた旋律の配列(2次元)
|
|
272
|
-
"""
|
|
273
|
-
|
|
274
|
-
clip = np.array([], dtype=int)
|
|
275
|
-
clip_dict: dict = dict()
|
|
276
|
-
aya_node_inst = np.array([], dtype=object)
|
|
277
|
-
is_seq = True
|
|
278
|
-
back_note = None
|
|
279
|
-
|
|
280
|
-
clip_time: float = 0
|
|
281
|
-
split_count: int = 1
|
|
282
|
-
|
|
245
|
+
def ct_aya_node(self, inst: Instrument):
|
|
246
|
+
back_note: Note | None = None
|
|
247
|
+
key = np.array([], dtype=int)
|
|
248
|
+
value = np.array([], dtype=int)
|
|
283
249
|
sorted_notes = sorted(inst.notes, key=lambda notes: notes.start)
|
|
284
250
|
|
|
285
251
|
for note in sorted_notes:
|
|
286
252
|
note: Note = note
|
|
287
253
|
|
|
288
|
-
tempo = self.get_tempo(note.start)
|
|
289
|
-
measure_time = (60 / tempo) * 4
|
|
290
|
-
|
|
291
|
-
if clip_time >= measure_time * 4 * split_count:
|
|
292
|
-
if is_seq:
|
|
293
|
-
clip_dict['src'] = clip.tolist()
|
|
294
|
-
else:
|
|
295
|
-
clip_dict['tgt'] = clip.tolist()
|
|
296
|
-
aya_node_inst = np.append(aya_node_inst, clip_dict)
|
|
297
|
-
|
|
298
|
-
clip_dict = dict()
|
|
299
|
-
clip = np.array([], dtype=int)
|
|
300
|
-
is_seq = not is_seq
|
|
301
|
-
split_count += 1
|
|
302
|
-
|
|
303
|
-
if back_note is None:
|
|
304
|
-
clip = np.append(clip, self.tokenizer.get(constants.START_SEQ_TOKEN))
|
|
305
|
-
|
|
306
254
|
for conv in self.token_converter:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
token = conv(back_notes=back_note, note=note, tempo=tempo)
|
|
255
|
+
token: str = conv(inst=inst, back_notes=back_note, note=note, tempo=self.get_tempo(note.start))
|
|
310
256
|
if token is not None:
|
|
311
|
-
|
|
312
|
-
|
|
257
|
+
if token == "<BLANK>":
|
|
258
|
+
token_id = self.tokenizer.get(token)
|
|
259
|
+
value = np.append(value, token_id)
|
|
260
|
+
self.step(key, value)
|
|
261
|
+
key = np.array([], dtype=int)
|
|
262
|
+
value = np.array([self.tokenizer.get("<SME>")], dtype=int)
|
|
263
|
+
back_note = None
|
|
264
|
+
elif token == "<SME>":
|
|
265
|
+
new_key, new_value = self.step(key, value)
|
|
266
|
+
key = new_key
|
|
267
|
+
value = new_value
|
|
268
|
+
token_id = self.tokenizer.get(token)
|
|
269
|
+
value = np.append(value, token_id)
|
|
270
|
+
else:
|
|
271
|
+
token_id = self.tokenizer.get(token)
|
|
272
|
+
value = np.append(value, token_id)
|
|
313
273
|
|
|
314
274
|
back_note = note
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
275
|
+
if len(value) >= 10:
|
|
276
|
+
self.step(key, value)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def step(self, key:ndarray, value:ndarray):
|
|
280
|
+
if np.isin(1, value):
|
|
281
|
+
key = value
|
|
282
|
+
value = np.array([], dtype=int)
|
|
283
|
+
elif np.sum(key==3) > self.context - 1:
|
|
284
|
+
new_value = np.append(value, self.tokenizer.get("<ESEQ>"))
|
|
285
|
+
new_value = np.insert(new_value, 0, self.tokenizer.get("<CONTINUE>"))
|
|
286
|
+
clip = {
|
|
287
|
+
'key': key.copy(),
|
|
288
|
+
'value': new_value.copy()
|
|
289
|
+
}
|
|
290
|
+
self.add_sequence(clip)
|
|
291
|
+
indices = np.where(key==3)[0][1]
|
|
292
|
+
key = key[indices:]
|
|
293
|
+
key = np.concatenate((key, value))
|
|
294
|
+
value = np.array([], dtype=int)
|
|
295
|
+
else:
|
|
296
|
+
new_value = np.append(value, self.tokenizer.get("<ESEQ>"))
|
|
297
|
+
new_value = np.insert(new_value, 0, self.tokenizer.get("<CONTINUE>"))
|
|
298
|
+
clip = {
|
|
299
|
+
'key': key.copy(),
|
|
300
|
+
'value': new_value.copy()
|
|
301
|
+
}
|
|
302
|
+
self.add_sequence(clip)
|
|
303
|
+
key = np.concatenate((key, value))
|
|
304
|
+
value = np.array([], dtype=int)
|
|
305
|
+
|
|
306
|
+
return key, value
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Tokenizerで変換したシーケンスを全て保管します。
|
|
3
|
+
'''
|
|
4
|
+
import random
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
import torch
|
|
8
|
+
from torch.utils.data import Dataset
|
|
9
|
+
import numpy as np
|
|
10
|
+
from .progress import LearningProgress
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MORTM_DataSets(Dataset):
|
|
14
|
+
def __init__(self, progress: LearningProgress, positional_length):
|
|
15
|
+
self.key: list = list()
|
|
16
|
+
self.value: list = list()
|
|
17
|
+
self.progress = progress
|
|
18
|
+
self.positional_length = positional_length
|
|
19
|
+
|
|
20
|
+
def __len__(self):
|
|
21
|
+
return len(self.key)
|
|
22
|
+
|
|
23
|
+
def __getitem__(self, item):
|
|
24
|
+
return (torch.tensor(self.key[item], dtype=torch.long, device=self.progress.get_device()),
|
|
25
|
+
torch.tensor(self.value[item], dtype=torch.long, device=self.progress.get_device()))
|
|
26
|
+
|
|
27
|
+
def add_data(self, music_seq: np.ndarray):
|
|
28
|
+
suc_count = 0
|
|
29
|
+
for i in range(len(music_seq) - 1):
|
|
30
|
+
seq = music_seq[f'array_{i + 1}'].tolist()
|
|
31
|
+
if (100 < len(seq['key']) < self.positional_length) or 4 in seq['key']:
|
|
32
|
+
self.key.append(seq['key'].tolist())
|
|
33
|
+
self.value.append(seq['value'].tolist())
|
|
34
|
+
suc_count += 1
|
|
35
|
+
|
|
36
|
+
return suc_count
|
|
@@ -3,27 +3,33 @@ from pretty_midi import Instrument, Note, PrettyMIDI
|
|
|
3
3
|
from typing import List
|
|
4
4
|
|
|
5
5
|
from .tokenizer import Tokenizer, PITCH_TYPE, START_TYPE, SHIFT_TYPE, VELOCITY_TYPE, DURATION_TYPE
|
|
6
|
-
from .
|
|
6
|
+
from .token import Token, ShiftTimeContainer
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def ct_token_to_midi(tokenizer: Tokenizer, seq: Tensor, save_directory:str, program=1, tempo=120):
|
|
10
12
|
seq = seq[1:]
|
|
11
13
|
midi = PrettyMIDI()
|
|
12
14
|
inst: Instrument = Instrument(program=program)
|
|
13
15
|
note = Note(pitch=0, velocity=100, start=0, end=0)
|
|
14
16
|
back_note = None
|
|
15
|
-
token_converter_list = tokenizer.
|
|
17
|
+
token_converter_list = tokenizer.music_token_list
|
|
18
|
+
container = ShiftTimeContainer(0, tempo)
|
|
16
19
|
for token_id in seq:
|
|
17
20
|
token = tokenizer.rev_get(token_id.item())
|
|
18
|
-
if token_id ==
|
|
21
|
+
if token_id == 392:
|
|
19
22
|
break
|
|
23
|
+
if token_id == 3:
|
|
24
|
+
container.shift()
|
|
25
|
+
|
|
20
26
|
for con in token_converter_list:
|
|
21
|
-
token_type = con(token=token, back_notes=back_note, note=note)
|
|
27
|
+
token_type = con(token=token, back_notes=back_note, note=note, container=container, tempo=tempo)
|
|
22
28
|
if token_type == DURATION_TYPE:
|
|
23
29
|
inst.notes.append(note)
|
|
24
30
|
back_note = note
|
|
25
31
|
note = Note(pitch=0, velocity=100, start=0, end=0)
|
|
26
32
|
midi.instruments.append(inst)
|
|
27
|
-
|
|
33
|
+
print(inst.notes)
|
|
28
34
|
midi.write(save_directory)
|
|
29
35
|
return midi
|