diff --git a/src/markov_ketju.py b/src/markov_ketju.py index d7407a7..4ee0c65 100644 --- a/src/markov_ketju.py +++ b/src/markov_ketju.py @@ -17,7 +17,7 @@ class MarkovKetju: self._trie = trie self._aste = aste - self._menneet_tilat = deque() + self.menneet_tilat = deque() def kasittele_opetusdata(self, opetusdata: list): """Lukee opetusdata Markovin ketjun omaan trie-rakenteeseen. @@ -43,24 +43,24 @@ class MarkovKetju: """ if len(alkuosa) < self._aste: raise ValueError("Liian lyhyt alkuosa!") - self._menneet_tilat = deque([alkuosa[i] for i in range(0, self._aste)]) + self.menneet_tilat = deque([alkuosa[i] for i in range(0, self._aste)]) def seuraava(self): """Antaa seuraavan merkin muilla funktioilla annettujen opetusdata ja alkuosan perusteella. Päivittää alkuosan automaattisesti seuraavaa askelta varten """ - if len(self._menneet_tilat) < self._aste: + if len(self.menneet_tilat) < self._aste: raise ValueError("Markovin ketjulle ei ole asetettu sopivaa alkuosaa!") - todennakoisyydet, merkit = self._trie.etsi_seuraavat(self._menneet_tilat) + todennakoisyydet, merkit = self._trie.etsi_seuraavat(self.menneet_tilat) if todennakoisyydet and merkit: #Valitsee merkeistä yhden, valinta painotetaan triessä olevien todennäköisyyksien avulla seuraava = choices(merkit, weights=todennakoisyydet)[0] #Päivittää alkuosan seuraavaa iteraatiota varten - self._menneet_tilat.popleft() - self._menneet_tilat.append(seuraava) + self.menneet_tilat.popleft() + self.menneet_tilat.append(seuraava) return seuraava diff --git a/src/midi_kasittelija.py b/src/midi_kasittelija.py index 1c65c6d..78e8b5c 100644 --- a/src/midi_kasittelija.py +++ b/src/midi_kasittelija.py @@ -18,7 +18,7 @@ savellaji_arvot = { "Cb":1, } -def LueMidi(tiedostopolku): +def lue_midi(tiedostopolku): midi = mido.MidiFile(tiedostopolku) tulos = [] for raita in midi.tracks: @@ -41,11 +41,11 @@ def LueMidi(tiedostopolku): return tulos -def KirjoitaMidi(tiedostopolku, nuotit): +def kirjoita_midi(tiedostopolku, nuotit, tempo=120): midi = mido.MidiFile() track = mido.MidiTrack() - track.append(mido.MetaMessage("set_tempo", tempo=mido.bpm2tempo(120), time=0)) + track.append(mido.MetaMessage("set_tempo", tempo=mido.bpm2tempo(tempo), time=0)) track.append(mido.MetaMessage("time_signature")) for nuotti in nuotit: diff --git a/src/musiikki_generaattori.py b/src/musiikki_generaattori.py new file mode 100644 index 0000000..9d33bbc --- /dev/null +++ b/src/musiikki_generaattori.py @@ -0,0 +1,98 @@ +from collections import deque +from glob import glob +from markov_ketju import MarkovKetju +from midi_kasittelija import lue_midi, kirjoita_midi +from trie import Trie + + +class MusiikkiGeneraattori: + def __init__(self): + self._ketju = None + self._nuotit = [] + self._opetusdata = [] + + self._nuotti_midiksi = { + "C":0, + "C#":1, + "Db":1, + "D":2, + "D#":3, + "Eb":3, + "E":4, + "F":5, + "F#":6, + "Gb":6, + "G":7, + "G#":8, + "Ab":8, + "A":9, + "A#":10, + "Bb":10, + "B":11} + + def lue_opetusdata(self, polku): + tiedostot = glob(polku) + self._opetusdata = [] + + for tiedosto in tiedostot: + try: + for x in lue_midi(tiedosto): + self._opetusdata.append(x) + except: + continue + + def valmistele_ketju(self, alkuosa, aste=1): + alkuosa = self._nuotit_midiksi(alkuosa) + + if len(alkuosa) < aste: + trie = Trie() + + for jono in self._opetusdata: + if len(jono) > aste: + alkio = deque() + + for i in range(0, len(jono)): + if len(alkio) > aste: + alkio.popleft() + alkio.append(jono[i]) + for j in range(-len(alkio), 0): + trie.lisaa([alkio[x] for x in range(j, 0)]) + + alkuosa = [x for x in alkuosa] + while len(alkuosa) < aste: + self._ketju = MarkovKetju(len(alkuosa), trie) + self._ketju.aseta_alkuosa(alkuosa) + alkuosa += [self._ketju.seuraava()] + + self._ketju = MarkovKetju(aste, trie) + self._ketju.aseta_alkuosa(alkuosa) + + else: + self._ketju = MarkovKetju(aste) + self._ketju.kasittele_opetusdata(self._opetusdata) + self._ketju.aseta_alkuosa(alkuosa) + + def generoi_nuotteja(self, n): + self._nuotit = list(self._ketju.menneet_tilat) + nuotteja = n - len(self._nuotit) + for i in range(nuotteja): + # Tunnistaa jos edellinen Markovin ketjun iteraatio palautti None + if not self._nuotit[-1]: + break + self._nuotit.append(self._ketju.seuraava()) + + return len(self._nuotit) + + def kirjoita_midi(self, tiedostopolku, tempo=120): + kirjoita_midi(tiedostopolku, self._nuotit, tempo) + + def _nuotit_midiksi(self, nuotit: str): + nuotit = nuotit.split(" ") + midi = [] + for nuotti in nuotit: + arvo = (int(nuotti[-1])+1) * 12 + arvo += self._nuotti_midiksi[nuotti[:-1]] + midi.append(arvo) + return midi + +musiikki_generaattori = MusiikkiGeneraattori() diff --git a/src/tests/midi_kasittelija_test.py b/src/tests/midi_kasittelija_test.py index a91ae0e..04ae371 100644 --- a/src/tests/midi_kasittelija_test.py +++ b/src/tests/midi_kasittelija_test.py @@ -1,6 +1,6 @@ import os import unittest -from midi_kasittelija import LueMidi, KirjoitaMidi +from midi_kasittelija import lue_midi, kirjoita_midi class TestMidiKasittelija(unittest.TestCase): def test_molemmat(self): @@ -9,6 +9,6 @@ class TestMidiKasittelija(unittest.TestCase): os.remove(testi_polku) nuotit = [60, 62, 64] - KirjoitaMidi(testi_polku, nuotit) - luetut_nuotit = LueMidi(testi_polku)[0] + kirjoita_midi(testi_polku, nuotit) + luetut_nuotit = lue_midi(testi_polku)[0] self.assertEqual(nuotit, luetut_nuotit)