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.
Files changed (32) hide show
  1. {mortm-1.2 → mortm-2.0b3}/MORTM.egg-info/PKG-INFO +1 -1
  2. {mortm-1.2 → mortm-2.0b3}/MORTM.egg-info/SOURCES.txt +1 -1
  3. {mortm-1.2 → mortm-2.0b3}/PKG-INFO +1 -1
  4. {mortm-1.2 → mortm-2.0b3}/mortm/convert.py +83 -101
  5. mortm-2.0b3/mortm/datasets.py +36 -0
  6. {mortm-1.2 → mortm-2.0b3}/mortm/de_convert.py +12 -6
  7. mortm-2.0b3/mortm/mortm.py +398 -0
  8. {mortm-1.2 → mortm-2.0b3}/mortm/progress.py +2 -3
  9. {mortm-1.2 → mortm-2.0b3}/mortm/reinforcement.py +2 -2
  10. {mortm-1.2 → mortm-2.0b3}/mortm/rpr.py +2 -71
  11. mortm-2.0b3/mortm/token.py +296 -0
  12. {mortm-1.2 → mortm-2.0b3}/mortm/tokenizer.py +21 -20
  13. {mortm-1.2 → mortm-2.0b3}/mortm/train.py +65 -45
  14. {mortm-1.2 → mortm-2.0b3}/setup.py +1 -1
  15. mortm-1.2/mortm/aya_node.py +0 -271
  16. mortm-1.2/mortm/datasets.py +0 -114
  17. mortm-1.2/mortm/mortm.py +0 -352
  18. {mortm-1.2 → mortm-2.0b3}/MORTM.egg-info/dependency_links.txt +0 -0
  19. {mortm-1.2 → mortm-2.0b3}/MORTM.egg-info/top_level.txt +0 -0
  20. {mortm-1.2 → mortm-2.0b3}/README.md +0 -0
  21. {mortm-1.2 → mortm-2.0b3}/mortm/PositionalEncoding.py +0 -0
  22. {mortm-1.2 → mortm-2.0b3}/mortm/RelativePositionalRepresentations.py +0 -0
  23. {mortm-1.2 → mortm-2.0b3}/mortm/__init__.py +0 -0
  24. {mortm-1.2 → mortm-2.0b3}/mortm/constants.py +0 -0
  25. {mortm-1.2 → mortm-2.0b3}/mortm/eval.py +0 -0
  26. {mortm-1.2 → mortm-2.0b3}/mortm/generate.py +0 -0
  27. {mortm-1.2 → mortm-2.0b3}/mortm/gmail_messanger.py +0 -0
  28. {mortm-1.2 → mortm-2.0b3}/mortm/loss.py +0 -0
  29. {mortm-1.2 → mortm-2.0b3}/mortm/mask.py +0 -0
  30. {mortm-1.2 → mortm-2.0b3}/mortm/messager.py +0 -0
  31. {mortm-1.2 → mortm-2.0b3}/mortm/noam.py +0 -0
  32. {mortm-1.2 → mortm-2.0b3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: MORTM
3
- Version: 1.2
3
+ Version: 2.0b3
4
4
  Summary: 音楽の旋律生成を実現したシステム
5
5
  Home-page: https://github.com/Ayato964
6
6
  Author: Nagoshi Takaaki
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: MORTM
3
- Version: 1.2
3
+ Version: 2.0b3
4
4
  Summary: 音楽の旋律生成を実現したシステム
5
5
  Home-page: https://github.com/Ayato964
6
6
  Author: Nagoshi Takaaki
@@ -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 constants
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.token_list
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 MidiToAyaNode(_AbstractMidiToAyaNode):
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__(MidiToAyaNode, tokenizer, directory, file_name, program_list, midi_data)
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
- clip_time: float = 0
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) > 50:
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
- class MidiToAyaNode_TGT(_AbstractMidiToAyaNode):
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
- array_dict = {f'array{i}': arr for i, arr in enumerate(self.aya_node)}
233
- if len(array_dict) > 1:
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
- aya_node_inst = self.ct_aya_node(inst)
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) -> list:
262
-
263
- """
264
- Instrumentsから1音ずつ取り出し、Tokenizerで変換する。
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
- conv: Token = conv
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
- token_id = self.tokenizer.get(token)
312
- clip = np.append(clip, token_id)
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
- clip_time = note.start
316
-
317
-
318
- return aya_node_inst.tolist()
319
-
320
- def marge_clip(self, clip, aya_node_inst):
321
- #clip = np.append(clip, self.tokenizer.get(constants.END_SEQ_TOKEN))
322
- aya_node_inst.append(clip)
323
-
324
- return aya_node_inst
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 .aya_node import Token
6
+ from .token import Token, ShiftTimeContainer
7
7
 
8
8
 
9
- def ct_token_to_midi(tokenizer: Tokenizer, seq: Tensor, save_directory:str, program=1):
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.token_list
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 == 2:
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