1
0
Fork 0

Toteuta MIDI-muunnos

This commit is contained in:
Vili Sinervä 2022-10-15 18:46:05 +03:00
parent 058bdbd518
commit 83fd8ea522
9 changed files with 82 additions and 29 deletions

BIN
dokumentaatio/muunnos1.mp3 Executable file

Binary file not shown.

BIN
dokumentaatio/muunnos2.mp3 Executable file

Binary file not shown.

View file

@ -23,7 +23,7 @@ Viikko 4: Generoin koodin nykyisellä versiolla 4:nnen asteen Markovin ketjulla
Viikko 5: Toteutin tällä viikolla manuaalisen rytmin määrittämisen. Generoin 80 lyhyen melodian yksinkertaisella kahden tahdin välein toistuvalla rytmillä. Yllätin siitä kuinka paljon yksinkertainenkin rytmi "elävöitti" generoitua melodiaa. Viikko 5: Toteutin tällä viikolla manuaalisen rytmin määrittämisen. Generoin 80 lyhyen melodian yksinkertaisella kahden tahdin välein toistuvalla rytmillä. Yllätin siitä kuinka paljon yksinkertainenkin rytmi "elävöitti" generoitua melodiaa.
Viikko 6: Testasin tällä viikolla kattavammin eri asteilla. Rajoitin asteen alle 5, sillä huomasin että tätä suuremmilla asteilla monessa tilanteessa oli vain yksi vaihtoehto seuraavalle nuotille, joka viittaa siihen että opetusdatan määrä ei ole riittävän suuri. Poistin myös tämän takia aiemmat testit. Viikko 6: Testasin tällä viikolla kattavammin eri asteilla. Rajoitin asteen alle 5, sillä huomasin että tätä suuremmilla asteilla monessa tilanteessa oli vain yksi vaihtoehto seuraavalle nuotille, joka viittaa siihen että opetusdatan määrä ei ole riittävän suuri. Poistin myös tämän takia aiemmat testit. Lisäksi toteutin mekanismin, jolla olemassaolevan MIDI tiedoston 1. raidan nuotin voidaan korvata Markovin ketjun antamilla nuoteilla. Nämä "muunnokset" löytyvät myös alta. Näissä oli käytössä 4. asteen ketju.
[Aste 1](https://github.com/ArcticCoder/markov-music-generator/blob/main/dokumentaatio/aste_1.mp3) [Aste 1](https://github.com/ArcticCoder/markov-music-generator/blob/main/dokumentaatio/aste_1.mp3)
@ -35,4 +35,8 @@ Viikko 6: Testasin tällä viikolla kattavammin eri asteilla. Rajoitin asteen al
[Aste 4, ei rytmiä](https://github.com/ArcticCoder/markov-music-generator/blob/main/dokumentaatio/aste_4_rytmiton.mp3) [Aste 4, ei rytmiä](https://github.com/ArcticCoder/markov-music-generator/blob/main/dokumentaatio/aste_4_rytmiton.mp3)
[MIDI muunnos 1](https://github.com/ArcticCoder/markov-music-generator/blob/main/dokumentaatio/muunnos1.mp3)
[MIDI muunnos 2](https://github.com/ArcticCoder/markov-music-generator/blob/main/dokumentaatio/muunnos2.mp3)
[Sattuma](https://github.com/ArcticCoder/markov-music-generator/blob/main/dokumentaatio/testi-sattuma.mp3) [Sattuma](https://github.com/ArcticCoder/markov-music-generator/blob/main/dokumentaatio/testi-sattuma.mp3)

View file

@ -15,3 +15,5 @@ https://musescore.com/user/38606/scores/6082483
https://musescore.com/user/110059/scores/980236 https://musescore.com/user/110059/scores/980236
https://musescore.com/user/36583375/scores/6502523 https://musescore.com/user/36583375/scores/6502523
https://musescore.com/r_d/scores/5449385 https://musescore.com/r_d/scores/5449385
https://musescore.com/user/285631/scores/287116
https://musescore.com/user/33617769/scores/6363495

BIN
opetusdata/muunnos1.mid Normal file

Binary file not shown.

BIN
opetusdata/muunnos2.mid Normal file

Binary file not shown.

View file

@ -45,31 +45,58 @@ def lue_midi(tiedostopolku):
return tulos return tulos
def kirjoita_midi(tiedostopolku, nuotit, tempo=120, rytmi="1/4"): def kirjoita_midi(tiedostopolku, nuotit, muunnettava_midi=None, tempo=120, rytmi="1/4"):
"""Kirjoittaa halutut nuotit valittuun MIDI-tiedostoon annetulla tempolla""" """Kirjoittaa nuotit MIDI-tiedostoon halutulla tempolla ja rytmillä.
Vaihtoehtoisesti ottaa valitun MIDI-tiedoston ja vaihtaa sävelkorkeudet
"""
midi = mido.MidiFile() midi = mido.MidiFile()
raita = mido.MidiTrack() raita = mido.MidiTrack()
rytmi = rytmi_taulukoksi(rytmi) if muunnettava_midi:
iskun_kesto = midi.ticks_per_beat muutettava = mido.MidiFile(muunnettava_midi)
rytmi = [round(x*4*iskun_kesto) for x in rytmi] muutettava_raita = muutettava.tracks[0]
raita.append(mido.MetaMessage("set_tempo", tempo=mido.bpm2tempo(tempo), time=0)) muunnokset = {}
raita.append(mido.MetaMessage("time_signature"))
raita.append(mido.Message("program_change", program=0, time=0))
rytmi_i = 0 nuotti_i = 0
for nuotti in nuotit: for viesti in muutettava_raita:
if nuotti: if viesti.type == "note_on" and viesti.velocity > 0:
raita.append(mido.Message("note_on", note=nuotti, velocity=64, time=0)) muunnokset[viesti.note] = nuotit[nuotti_i]
raita.append(mido.Message("note_on", note=nuotti, velocity=0, time=rytmi[rytmi_i])) raita.append(mido.Message("note_on",
rytmi_i = (rytmi_i + 1) % len(rytmi) note=muunnokset[viesti.note], velocity=64, time=viesti.time))
nuotti_i += 1
if nuotti_i == len(nuotit):
break
elif viesti.type == "note_off" or (viesti.type == "note_on" and viesti.velocity == 0):
raita.append(mido.Message("note_on",
note=muunnokset[viesti.note], velocity=0, time=viesti.time))
else:
raita.append(viesti)
else:
iskun_kesto = midi.ticks_per_beat
rytmi = rytmi_taulukoksi(rytmi)
rytmi = [round(x*4*iskun_kesto) for x in rytmi]
raita.append(mido.MetaMessage("set_tempo", tempo=mido.bpm2tempo(tempo), time=0))
raita.append(mido.MetaMessage("time_signature"))
raita.append(mido.Message("program_change", program=0, time=0))
rytmi_i = 0
for nuotti in nuotit:
if nuotti:
raita.append(mido.Message("note_on", note=nuotti, velocity=64, time=0))
raita.append(mido.Message("note_on", note=nuotti, velocity=0, time=rytmi[rytmi_i]))
rytmi_i = (rytmi_i + 1) % len(rytmi)
midi.tracks.append(raita) midi.tracks.append(raita)
midi.save(tiedostopolku) midi.save(tiedostopolku)
def rytmi_taulukoksi(rytmi): def rytmi_taulukoksi(rytmi):
"""Muuntaa annetun rytmin numeeriseksi taulukoksi"""
rytmi = rytmi.split("|") rytmi = rytmi.split("|")
tulos = [] tulos = []
for alkio in rytmi: for alkio in rytmi:

View file

@ -45,7 +45,7 @@ class MusiikkiGeneraattori:
for jono in lue_midi(tiedosto): for jono in lue_midi(tiedosto):
self._opetusdata.append(jono) self._opetusdata.append(jono)
# Kaikki virheet halutaan ohittaa # Kaikki virheet halutaan ohittaa
except: # pylint: disable=bare-except except Exception: # pylint: disable=broad-except
continue continue
def valmistele_ketju(self, alkuosa, aste=1): def valmistele_ketju(self, alkuosa, aste=1):
@ -95,9 +95,14 @@ class MusiikkiGeneraattori:
return len(self._nuotit) return len(self._nuotit)
def kirjoita_midi(self, tiedostopolku, tempo=120, rytmi="1/4"): def kirjoita_midi(self, tiedostopolku, muunnettava_midi=None, tempo=120, rytmi="1/4"):
"""Kirjoittaa nuotit MIDI-tiedostoon halutulla tempolla ja rytmillä""" """Kirjoittaa nuotit MIDI-tiedostoon halutulla tempolla ja rytmillä.
kirjoita_midi(tiedostopolku, self._nuotit, tempo, rytmi) Vaihtoehtoisesti ottaa valitun MIDI-tiedoston ja vaihtaa sävelkorkeudet
"""
if muunnettava_midi:
kirjoita_midi(tiedostopolku, self._nuotit, muunnettava_midi)
else:
kirjoita_midi(tiedostopolku, self._nuotit, None, tempo, rytmi)
def _nuotit_midiksi(self, nuotit: str): def _nuotit_midiksi(self, nuotit: str):
"""Muuttaa perinteiset nuottimerkinnät kuten C#4 MIDI-arvoiksi""" """Muuttaa perinteiset nuottimerkinnät kuten C#4 MIDI-arvoiksi"""

View file

@ -1,6 +1,7 @@
"""Musiikki generaattorin tekstipohjainen käyttöliittymä """Musiikki generaattorin tekstipohjainen käyttöliittymä
from ui import UI""" from ui import UI"""
from os import system, name from os import system, name
from os.path import exists
from re import search from re import search
from sys import exit as sys_exit from sys import exit as sys_exit
from musiikki_generaattori import musiikki_generaattori from musiikki_generaattori import musiikki_generaattori
@ -15,6 +16,7 @@ class UI: # pylint: disable=too-few-public-methods, too-many-instance-attributes
self._nuottien_maara = 120 self._nuottien_maara = 120
self._tempo = 120 self._tempo = 120
self._rytmi = "1/4" self._rytmi = "1/4"
self._muunnettava_midi_polku = ""
self._toiminnot = { self._toiminnot = {
"A": ("(A)pu", self._tulosta_apu, 0, "A": ("(A)pu", self._tulosta_apu, 0,
@ -25,14 +27,16 @@ class UI: # pylint: disable=too-few-public-methods, too-many-instance-attributes
"Valitse valmiin sävelmän polku"), "Valitse valmiin sävelmän polku"),
"K": ("(K)etjun aste", self._aseta_aste, 1, "K": ("(K)etjun aste", self._aseta_aste, 1,
"Aseta Markovin ketjun aste valittuun kokonaislukuun"), "Aseta Markovin ketjun aste valittuun kokonaislukuun"),
"L": ("a(L)kuosa [merkkijono]", self._aseta_alkuosa, 1,
"Aseta alkuosa. Esim. 'C4|D#5|Gb3'"),
"N": ("(N)uotteja [luku]", self._aseta_nuottien_maara, 1, "N": ("(N)uotteja [luku]", self._aseta_nuottien_maara, 1,
"Aseta sävelmän nuottien määrä valittuun kokonaislukuun"), "Aseta sävelmän nuottien määrä valittuun kokonaislukuun"),
"T": ("(T)empo [luku]", self._aseta_tempo, 1, "T": ("(T)empo [luku]", self._aseta_tempo, 1,
"Aseta tempo valittuun kokonaislukuun"), "Aseta tempo valittuun kokonaislukuun"),
"R": ("(R)ytmi [merkkijono]", self._aseta_rytmi, 1, "R": ("(R)ytmi [merkkijono]", self._aseta_rytmi, 1,
"Aseta rytmi. Esim. '1/4|1/4|1/2'"), "Aseta rytmi. Esim. '1/4|1/4|1/2'"),
"L": ("a(L)kuosa [merkkijono]", self._aseta_alkuosa, 1, "M": ("(M)uunnettava MIDI [polku]", self._aseta_muunnettava_midi, 1,
"Aseta alkuosa. Esim. 'C4|D#5|Gb3'"), "Valitse MIDI, jonka sävelkorkeudet vaihdetaan generoituihin"),
"G": ("(G)eneroi sävelmä", self._generoi_savelma, 0, "G": ("(G)eneroi sävelmä", self._generoi_savelma, 0,
"Generoi sävelmä valituilla asetuksilla"), "Generoi sävelmä valituilla asetuksilla"),
"S": ("(S)ulje", self._sulje, 0, "S": ("(S)ulje", self._sulje, 0,
@ -69,9 +73,11 @@ class UI: # pylint: disable=too-few-public-methods, too-many-instance-attributes
for i in range(1, 1+parametrien_maara): for i in range(1, 1+parametrien_maara):
parametrit.append(komento[i]) parametrit.append(komento[i])
funktio(parametrit) funktio(parametrit)
# Kaikki virheet halutaan ohittaa except IndexError:
except: # pylint: disable=bare-except self._virheet.append("ARGUMENTTIEN MÄÄRÄ VÄÄRÄ!")
self._virheet.append("KOMENNON MUOTO VÄÄRÄ!") # Kaikki muut virheet halutaan ohittaa
except Exception as exception: # pylint: disable=broad-except
self._virheet.append(f"TUNTEMATON VIRHE! {str(exception)}")
def _tyhjenna(self): def _tyhjenna(self):
# windows # windows
@ -92,12 +98,14 @@ class UI: # pylint: disable=too-few-public-methods, too-many-instance-attributes
def _tulosta_arvot(self): def _tulosta_arvot(self):
print(f"Opetusdata: '{self._opetusdata_polku}'") print(f"Opetusdata: '{self._opetusdata_polku}'")
print(f"Generoidun sävelmän polku: '{self._tulos_polku}'") print(f"Generoidun sävelmän polku: '{self._tulos_polku}'")
print()
print(f"Markovin ketjun aste: {self._aste}") print(f"Markovin ketjun aste: {self._aste}")
print(f"Alkuosa: '{self._alku}'")
print(f"Sävelmän pituus nuotteina: {self._nuottien_maara}") print(f"Sävelmän pituus nuotteina: {self._nuottien_maara}")
print()
print(f"Tempo: {self._tempo}") print(f"Tempo: {self._tempo}")
print(f"Rytmi: '{self._rytmi}'") print(f"Rytmi: '{self._rytmi}'")
print(f"Alkuosa: '{self._alku}'") print("TAI")
print(f"Muunnettava MIDI: '{self._muunnettava_midi_polku}'")
def _tulosta_apu(self, _): def _tulosta_apu(self, _):
print("OHJEET") print("OHJEET")
@ -117,7 +125,7 @@ class UI: # pylint: disable=too-few-public-methods, too-many-instance-attributes
try: try:
aste = int(aste[0]) aste = int(aste[0])
if aste < 1: if aste < 1:
raise ValueError; raise ValueError
self._aste = aste self._aste = aste
except ValueError: except ValueError:
self._virheet.append("ANNETTU ASTE EI MUUTETTAVISSA KOKONAISLUVUKSI!") self._virheet.append("ANNETTU ASTE EI MUUTETTAVISSA KOKONAISLUVUKSI!")
@ -143,7 +151,7 @@ class UI: # pylint: disable=too-few-public-methods, too-many-instance-attributes
def _aseta_rytmi(self, rytmi): def _aseta_rytmi(self, rytmi):
rytmi = rytmi[0] rytmi = rytmi[0]
if search(r"^\|?((([1-9]\/[1-9])|[1-9])\|)*(([1-9]/[1-9])|[1-9])\|?$", rytmi): if search(r"^\|?((([1-9]\/[1-9])|[1-9])\|)*(([1-9]/[1-9])|[1-9])\|?$", rytmi):
self._rytmi = rytmi self._rytmi = rytmi
else: else:
self._virheet.append("ANNETTU RYTMI EI KELPAA!") self._virheet.append("ANNETTU RYTMI EI KELPAA!")
@ -154,13 +162,20 @@ class UI: # pylint: disable=too-few-public-methods, too-many-instance-attributes
else: else:
self._virheet.append("ANNETTU ALKUOSA EI KELPAA!") self._virheet.append("ANNETTU ALKUOSA EI KELPAA!")
def _aseta_muunnettava_midi(self, polku):
self._muunnettava_midi_polku = polku[0]
def _generoi_savelma(self, _): def _generoi_savelma(self, _):
musiikki_generaattori.lue_opetusdata(self._opetusdata_polku) musiikki_generaattori.lue_opetusdata(self._opetusdata_polku)
musiikki_generaattori.valmistele_ketju(self._alku, self._aste) musiikki_generaattori.valmistele_ketju(self._alku, self._aste)
generoitu_maara = musiikki_generaattori.generoi_nuotteja(self._nuottien_maara) generoitu_maara = musiikki_generaattori.generoi_nuotteja(self._nuottien_maara)
if exists(self._muunnettava_midi_polku):
musiikki_generaattori.kirjoita_midi(self._tulos_polku, self._muunnettava_midi_polku)
else:
musiikki_generaattori.kirjoita_midi(self._tulos_polku, None, self._tempo, self._rytmi)
print(f"Generoitiin {generoitu_maara} nuottia!\n") print(f"Generoitiin {generoitu_maara} nuottia!\n")
musiikki_generaattori.kirjoita_midi(self._tulos_polku, self._tempo, self._rytmi)
def _sulje(self): def _sulje(self):
sys_exit(0) sys_exit(0)