Toteutettu korkeamman tason funktioita
This commit is contained in:
parent
83152ddc4a
commit
65a8d0abfd
4 changed files with 110 additions and 12 deletions
|
@ -17,7 +17,7 @@ class MarkovKetju:
|
||||||
|
|
||||||
self._trie = trie
|
self._trie = trie
|
||||||
self._aste = aste
|
self._aste = aste
|
||||||
self._menneet_tilat = deque()
|
self.menneet_tilat = deque()
|
||||||
|
|
||||||
def kasittele_opetusdata(self, opetusdata: list):
|
def kasittele_opetusdata(self, opetusdata: list):
|
||||||
"""Lukee opetusdata Markovin ketjun omaan trie-rakenteeseen.
|
"""Lukee opetusdata Markovin ketjun omaan trie-rakenteeseen.
|
||||||
|
@ -43,24 +43,24 @@ class MarkovKetju:
|
||||||
"""
|
"""
|
||||||
if len(alkuosa) < self._aste:
|
if len(alkuosa) < self._aste:
|
||||||
raise ValueError("Liian lyhyt alkuosa!")
|
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):
|
def seuraava(self):
|
||||||
"""Antaa seuraavan merkin muilla funktioilla annettujen opetusdata ja alkuosan
|
"""Antaa seuraavan merkin muilla funktioilla annettujen opetusdata ja alkuosan
|
||||||
perusteella. Päivittää alkuosan automaattisesti seuraavaa askelta varten
|
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!")
|
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:
|
if todennakoisyydet and merkit:
|
||||||
#Valitsee merkeistä yhden, valinta painotetaan triessä olevien todennäköisyyksien avulla
|
#Valitsee merkeistä yhden, valinta painotetaan triessä olevien todennäköisyyksien avulla
|
||||||
seuraava = choices(merkit, weights=todennakoisyydet)[0]
|
seuraava = choices(merkit, weights=todennakoisyydet)[0]
|
||||||
|
|
||||||
#Päivittää alkuosan seuraavaa iteraatiota varten
|
#Päivittää alkuosan seuraavaa iteraatiota varten
|
||||||
self._menneet_tilat.popleft()
|
self.menneet_tilat.popleft()
|
||||||
self._menneet_tilat.append(seuraava)
|
self.menneet_tilat.append(seuraava)
|
||||||
|
|
||||||
return seuraava
|
return seuraava
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ savellaji_arvot = {
|
||||||
"Cb":1,
|
"Cb":1,
|
||||||
}
|
}
|
||||||
|
|
||||||
def LueMidi(tiedostopolku):
|
def lue_midi(tiedostopolku):
|
||||||
midi = mido.MidiFile(tiedostopolku)
|
midi = mido.MidiFile(tiedostopolku)
|
||||||
tulos = []
|
tulos = []
|
||||||
for raita in midi.tracks:
|
for raita in midi.tracks:
|
||||||
|
@ -41,11 +41,11 @@ def LueMidi(tiedostopolku):
|
||||||
|
|
||||||
return tulos
|
return tulos
|
||||||
|
|
||||||
def KirjoitaMidi(tiedostopolku, nuotit):
|
def kirjoita_midi(tiedostopolku, nuotit, tempo=120):
|
||||||
midi = mido.MidiFile()
|
midi = mido.MidiFile()
|
||||||
track = mido.MidiTrack()
|
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"))
|
track.append(mido.MetaMessage("time_signature"))
|
||||||
|
|
||||||
for nuotti in nuotit:
|
for nuotti in nuotit:
|
||||||
|
|
98
src/musiikki_generaattori.py
Normal file
98
src/musiikki_generaattori.py
Normal file
|
@ -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()
|
|
@ -1,6 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
from midi_kasittelija import LueMidi, KirjoitaMidi
|
from midi_kasittelija import lue_midi, kirjoita_midi
|
||||||
|
|
||||||
class TestMidiKasittelija(unittest.TestCase):
|
class TestMidiKasittelija(unittest.TestCase):
|
||||||
def test_molemmat(self):
|
def test_molemmat(self):
|
||||||
|
@ -9,6 +9,6 @@ class TestMidiKasittelija(unittest.TestCase):
|
||||||
os.remove(testi_polku)
|
os.remove(testi_polku)
|
||||||
|
|
||||||
nuotit = [60, 62, 64]
|
nuotit = [60, 62, 64]
|
||||||
KirjoitaMidi(testi_polku, nuotit)
|
kirjoita_midi(testi_polku, nuotit)
|
||||||
luetut_nuotit = LueMidi(testi_polku)[0]
|
luetut_nuotit = lue_midi(testi_polku)[0]
|
||||||
self.assertEqual(nuotit, luetut_nuotit)
|
self.assertEqual(nuotit, luetut_nuotit)
|
||||||
|
|
Reference in a new issue