Manon, Theresa
Wir moechten ein Programm schreiben, das uns nach einem gewissen Schema ein Lied komponiert. Die Grundlage soll die Jazz-Theorie bilden. Der Bediener kann aussuchen, mit welchem Akkord begonnen wird. Der Plan ist, eine Melodie, aber auch einen Impro-Teil zu schreiben. Jedes Mal wird ein neues Stück komponiert.
Wir haben uns entschieden, erstmal das Blues Akkord-Schema zu benutzen, damit wir uns auf die Melodiestimme konzentrieren können. Das Bluesschema besteht aus zwölf Takten und sieht so aus:
vier Takte Tonika, zwei Takte Subdominante, zwei Takte Tonika ein Takt Dominante, ein Takt Subdominante, zwei Takte Tonika, in C-dur wäre das also
CCCC FFCC GFCC
Außerdem haben wir eine Cloud eingerichtet und versucht das Modul midi zu installieren, um Töne abspielen zu können.
Wir haben das Modul sequencer, das PyAudio benutzt, installiert und ein bisschen ausprobiert. Mithilfe von sequencer können wir einen Ton abspielen.
from sequencer import * def einzelnen_ton_abspielen(): tonhoehe=0 tondauer=4 sound=Sound() sound.klanggen=EasySequencer(6,1) threading.Thread(target=sound.play).start() sound.klanggen.add_note_easy(0,tonhoehe,tondauer,0.5) time.sleep(tondauer*VIERTELNOTE) time.sleep(1) sound.outStream.close() einzelnen_ton_abspielen()
Von einem Ton ausgehend wollen wir nun eine Melodie erzeugen. Melodien bestehen aus Tonschritten und Tonsprüngen. Unsere Funktion soll, nachdem sie einen Anfangston abgespielt hat, entweder den Ton abspielen, der um einen Ganztonschritt über dem Anfangston liegt oder den Ton, der einen Ganztonschritt bzw. zwei Halbtonschritte unter ihm liegt. Das heißt der Zufall soll entscheiden, ob als nächstes ein höherer oder ein tieferer Ton gespielt wird. Dazu bedienen wir uns dem Modul random. Nachdem der Anfangston abgespielt wurde, wird in der while-Schleife die zufällig gewählte Größe i bestimmt und damit auch der folgende Ton bestimmt und abgespielt.
def tonschritt_abspielen(): tonhoehe=1 #Anfangston tondauer=1 sound=Sound() sound.klanggen=EasySequencer(6,1) threading.Thread(target=sound.play).start() time.sleep(0) k=0 while k<=2: sound.klanggen.add_note_easy(0,tonhoehe,tondauer,0.3) time.sleep(tondauer*VIERTELNOTE) i=random.randint(0,1) if i==0: tonhoehe=tonhoehe+2 else: tonhoehe=tonhoehe-2 k=k+tondauer time.sleep(1) sound.outStream.close() tonschritt_abspielen()
Für unsere Literaturrecherche hat uns Stefan einige Links über algorithmisches Komponieren geschickt und ein Buch bestellt. Hoffentlich können wir mithilfe des Buchs oder anderer Quellen mehr über die Merkmale von Bluesmelodien erfahren.Außerdem hat er uns auf ein aktuelles Forschungsprojekt namens Jazzomat hingewiesen, welches -so wie wir auch- Jazzmelodien untersucht.
Unser Melodieprogramm wurde nun aufgeteilt in zwei Funktionen: die erste Funktion kreiert die Melodie und speichert sie als eine Liste von Tönen. Diese Funktion enthält das Argument „Viertelnoten“. Wenn man also die Funktion aufruft, dann muss man bestimmen wie lang die Melodie in Viertelnoten sein soll. Die Längen der einzelnen Töne werden in unserer Funktion zufällig bestimmt mit dem Befehl random.randint(). Die tondauer kann die Werte 1 bis 8 annehmen.
from sequencer import * import random def create_melody(Viertelnoten): melodietoene=[] k=0 while k<Viertelnoten: tonhoehe=0 tondauer=random.randint(1,8)#z ist ein zufaellig gewaehlter Wert zwischen 1 und 8 tonlautstaerke=0.05 z=random.randint(0,100) if z<=5 and z>=0: tonhoehe=tonhoehe elif z>15 and z<=30: tonhoehe=tonhoehe+2 elif z>30 and z<=35: tonhoehe=tonhoehe+4 elif z>35 and z<=60: tonhoehe=tonhoehe+5 elif z>60 and z<=85: tonhoehe=tonhoehe+7 elif z>85 and z<=90: tonhoehe=tonhoehe+9 elif z>90 and z<=95: tonhoehe=tonhoehe+11 else: tonhoehe=tonhoehe+12 tonlautstaerke=0 if k+tondauer>Viertelnoten: tondauer=Viertelnoten-k tonhoehe=0 k=k+tondauer melodietoene.append((tonhoehe,tondauer,tonlautstaerke)) return melodietoene
Die zweite Funktion play_melody spielt die Liste der Melodietöne ab.
def play_melody(liste): sound=Sound() sound.klanggen=EasySequencer(600,1) threading.Thread(target=sound.play).start() for x in liste: print x[0],x[1],x[2] sound.klanggen.add_note_easy(0,x[0],x[1],x[2]) time.sleep(x[1]*VIERTELNOTE) melodie1=create_melody(10000) play_melody(melodie1)
Wir sind noch unzufrieden mit dem Klang unserer Soundausgabe. Es wäre schön, wenn wir bald ein Saxophon oder ein Klavier nachahmen könnten. Um Jazzmelodien zu erzeugen, haben wir versucht typische Merkmale von Jazzmelodien aus z. B. dem folgenden Tonbeispiel der Jazzdatenbank Jazzomat herauszuhören. In dem Beispiel hört man viele Pausen und die Töne sind eher kurz. {{:ws1516:artpepper_anthropology_prefinal.mid|}}
Die Tondauer im Melodieprogramm haben wir jetzt auf 1 und 2 beschränkt. Damit klingt es schon viel aufgeweckter und bluesiger!!
tondauer=random.randint(1,2)
Inzwischen haben wir ein Programm, welches uns die fertigen Dreiklänge für die Akkorde ausgibt.
from __future__ import division from music21 import * from music21.midi.realtime import StreamPlayer from music21.midi import MidiFile grundton=raw_input('Geben Sie einen Grundton ein ') grundton=grundton.upper() l=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'] m=0 while m<=len(l): if grundton==l[m]: print m break else: m=m+1 ## Erste Stimme part1=stream.Part() notes=[] ton1=l[m] notes_raw=[(ton1,4)] for i,v in enumerate(notes_raw): n = note.Note(v[0]) n.duration.quarterLength = v[1] n.volume.velocity=70 # das ist die Lautstärke 0.. 127 notes.append(n) part1.append(notes) part1.insert(instrument.Piano()) ## Zweite Stimme part2=stream.Part() notes=[] m=m+4 print m ton2=l[m] notes_raw=[(ton2,4)] for i,v in enumerate(notes_raw): n = note.Note(v[0]) n.duration.quarterLength = v[1] n.volume.velocity=100 notes.append(n) part2.append(notes) part2.insert(instrument.Piano()) ## Dritte Stimme part3=stream.Part() notes=[] m=m+3 print m ton3=l[m] notes_raw=[(ton3,4)] for i,v in enumerate(notes_raw): n = note.Note(v[0]) n.duration.quarterLength = v[1] n.volume.velocity=100 notes.append(n) part3.append(notes) part3.insert(instrument.Piano()) ## Beide Stimmen in einem 'Stream' stream=stream.Stream([part1,part2,part3]) ## Tempo setzen: metronome=tempo.MetronomeMark("allegro", 240, note.Note(type='quarter')) stream.insert(metronome) ## Abspielen (benötigt PyGame und einen midi-Synthesizer) player=StreamPlayer(stream) player.play()
Hier ein Programm, das die Begleitung für die 12 Takte des Blusschemas abspielt.
def Begleitung(grundton): Akkorde(grundton) Akkorde(grundton) Akkorde(grundton) Akkorde(grundton) l=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B','C','C#','D','D#','E','F','F#'] m=0 while m<=len(l): if grundton==l[m]: print l[m+5] break else: m=m+1 stufevier=l[m+5] Akkorde(stufevier) Akkorde(stufevier) Akkorde(grundton) Akkorde(grundton) l=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B','C','C#','D','D#','E','F','F#'] m=0 while m<=len(l): if grundton==l[m]: print l[m+7] break else: m=m+1 stufefuenf=l[m+7] Akkorde(stufefuenf) Akkorde(stufevier) Akkorde(grundton) Akkorde(grundton) grundton=raw_input('Geben Sie einen Grundton ein ') grundton=grundton.upper() Begleitung(grundton)
Ins Melodieprogramm konnten wir jetzt noch Pausen einfügen. Eine Pause hat genau wie ein Ton eine festgelegte Dauer. Der Unterschied ist aber, dass sie lautlos ist. Deswegen haben wir die tonhoehe=1000 eingefügt. Tritt diese tonhoehe nämlich auf, dann ist die tonlautstärke=0.
Die Melodie, die beim Ausführen der create_melody-Funktion entsteht, können wir jetzt mithilfe von music21 von einem Saxophon abspielen lassen.
import threading, time from sequencer_music21 import Sound21 as Sound from sequencer_music21 import Music21Sequencer as Sequencer from sequencer_music21 import * viertelnote=0.25 grundton=220 # Frequenz des kleinen a def test(l): sound=Sound() # das Objekt, das sich um die Tonausgabe kümmert instruments=['instrument.AltoSaxophone()'] sound.klanggen=Sequencer(6000,1,instruments=instruments) anfang=0.0 for ton in l: ende=anfang+ton[1]*viertelnote sound.klanggen.add_note(0,grundton*HALBTON**ton[0],anfang, ende,0.3) sound.klanggen.add_note(1,grundton*2*HALBTON**ton[0],anfang, ende, 0.3) anfang=ende threading.Thread(target=sound.play).start() # Tonausgabe gestartet time.sleep(1) jetzt=time.time() while jetzt+ende+1>time.time(): pass l=create_melody(16) test(l)
Ab jetzt wollen wir versuchen unsere beiden Programme zu kombinieren.
Dafür haben wir drei Klassen definiert: Komposition, Dreiklang und Melodie. In der Klasse Komposition sollen Objekte der Klasse Melodie mit Objekten der Klasse Dreiklang kombiniert werden, sodass eine Jazz-Komposition entsteht.
class Dreiklang: def __init__(self): self.l=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B','C','C#','D','D#','E','F','F#'] self.noten=[] def create_dreiklang(self,grundton): . . .
class Melodie: def __init__(self): self.tonliste=[] self.melodielaenge=0 def create_melody(self,Viertelnoten): k=0 while k<Viertelnoten: ton=0 j=random.randint(1,8) i=random.randint(0,100) if i<=20 and i>=0: ton=ton elif i>20 and i<=40: . . .
Dabei ergibt sich für uns das Problem, dass die Melodie und die Dreiklänge unterschiedlich abgespielt werden. Dies wollen wir nun vereinheitlichen.
Wir haben beschlossen, dass Theresa sich um die Zusammenführung unserer beiden Programme in einer Klasse Komposition kümmert, während Manon ab jetzt die Dokumentation in der Wiki übernimmt. Die Kompositionsklasse schuat folgendermaßen aus:
from __future__ import division from music21 import * from music21.midi.realtime import StreamPlayer from music21.midi import MidiFile import dreiklang import melodie class Komposition: def __init__(self): self.akkorde=[] self.melodie=[] def add_akkord(self,m): self.akkorde.append(dreiklang.Dreiklang()) self.akkorde[-1].create_dreiklang(m) def play_dreiklang(self,m): stream2=stream.Stream(self.akkorde[m].dreiklang) metronome=tempo.MetronomeMark("allegro", 240, note.Note(type='quarter')) stream2.insert(metronome) player=StreamPlayer(stream2) player.play() def play_begleitung(self,o): play_dreiklang(o) play_dreiklang(o) play_dreiklang(o) play_dreiklang(o) l=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B','C','C#','D','D#','E','F','F#'] p=0 while p<=len(l): if o==l[p]: print l[o+5] break else: o=o+1 stufevier=l[o+5] play_dreiklang(stufevier) play_dreiklang(stufevier) play_dreiklang(o) play_dreiklang(o) l=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B','C','C#','D','D#','E','F','F#'] p=0 while p<=len(l): if o==l[p]: print l[o+7] break else: o=o+1 stufefuenf=l[o+7] play_dreiklang(stufefuenf) play_dreiklang(stufevier) play_dreiklang(o) play_dreiklang(o) def add_ton(self): self.melodie.append(melodie.Melodie()) self.melodie.create_melody() def play_melodie(self): sound=Sound() instruments=['instrument.AltoSaxophone()'] sound.klanggen=Sequencer(6000,1,instruments=instruments) anfang=0.0 for ton in self.melodie: ende=anfang+ton[1]*viertelnote sound.klanggen.add_note(0,grundton*HALBTON**ton[0],anfang, ende,0.3) #sound.klanggen.add_note(1,grundton*2*HALBTON**ton[0],anfang, ende, 0.3) anfang=ende threading.Thread(target=sound.play).start() time.sleep(1) jetzt=time.time() while jetzt+ende+1>time.time(): pass
Momentan kann aber die Begleitung nur sehr holprig abgespielt werden, da jedes Mal die Funktion neu aufgerufen wird.
Dieses Problem haben wir jetzt lösen können -es wird jetzt eine Liste angelegt, die dann abgespielt wird- und erhalten ein Programm, das uns Jazzmelodien auf dem Blusschema improvisiert.
from __future__ import division from music21 import * from music21.midi.realtime import StreamPlayer from music21.midi import MidiFile import dreiklang as v import melodie as u class Komposition: def __init__(self): m=u.Melodie() self.tonliste=m.create_melodie(48) self.akkorde1=[] self.akkorde2=[] self.akkorde3=[] self.l=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B','C','C#','D','D#','E','F','F#'] def create_bluesschema(self,grundton): grundton=grundton.upper() p=self.l.index(grundton) z=v.Dreiklang() stufevier=self.l[p+5] stufefuenf=self.l[p+7] self.akkorde1.append(z.create_dreiklang1(grundton)) self.akkorde1.append(z.create_dreiklang1(grundton)) self.akkorde1.append(z.create_dreiklang1(grundton)) self.akkorde1.append(z.create_dreiklang1(grundton)) self.akkorde1.append(z.create_dreiklang1(stufevier)) self.akkorde1.append(z.create_dreiklang1(stufevier)) self.akkorde1.append(z.create_dreiklang1(grundton)) self.akkorde1.append(z.create_dreiklang1(grundton)) self.akkorde1.append(z.create_dreiklang1(stufefuenf)) self.akkorde1.append(z.create_dreiklang1(stufevier)) self.akkorde1.append(z.create_dreiklang1(grundton)) self.akkorde1.append(z.create_dreiklang1(grundton)) #print grundton self.akkorde2.append(z.create_dreiklang2(grundton)) self.akkorde2.append(z.create_dreiklang2(grundton)) self.akkorde2.append(z.create_dreiklang2(grundton)) self.akkorde2.append(z.create_dreiklang2(grundton)) self.akkorde2.append(z.create_dreiklang2(stufevier)) self.akkorde2.append(z.create_dreiklang2(stufevier)) self.akkorde2.append(z.create_dreiklang2(grundton)) self.akkorde2.append(z.create_dreiklang2(grundton)) self.akkorde2.append(z.create_dreiklang2(stufefuenf)) self.akkorde2.append(z.create_dreiklang2(stufevier)) self.akkorde2.append(z.create_dreiklang2(grundton)) self.akkorde2.append(z.create_dreiklang2(grundton)) #print grundton self.akkorde3.append(z.create_dreiklang3(grundton)) self.akkorde3.append(z.create_dreiklang3(grundton)) self.akkorde3.append(z.create_dreiklang3(grundton)) self.akkorde3.append(z.create_dreiklang3(grundton)) self.akkorde3.append(z.create_dreiklang3(stufevier)) self.akkorde3.append(z.create_dreiklang3(stufevier)) self.akkorde3.append(z.create_dreiklang3(grundton)) self.akkorde3.append(z.create_dreiklang3(grundton)) self.akkorde3.append(z.create_dreiklang3(stufefuenf)) self.akkorde3.append(z.create_dreiklang3(stufevier)) self.akkorde3.append(z.create_dreiklang3(grundton)) self.akkorde3.append(z.create_dreiklang3(grundton)) #print grundton def play_melodie(self): sound=Sound() instruments=['instrument.AltoSaxophone()'] sound.klanggen=Sequencer(6000,1,instruments=instruments) anfang=0.0 for ton in self.tonliste: ende=anfang+ton[1]*viertelnote sound.klanggen.add_note(0,grundton*HALBTON**ton[0],anfang, ende,0.3) #sound.klanggen.add_note(1,grundton*2*HALBTON**ton[0],anfang, ende, 0.3) anfang=ende threading.Thread(target=sound.play).start() time.sleep(1) jetzt=time.time() while jetzt+ende+1>time.time(): pass def melodie_und_begleitung(self): part4=stream.Part() notes=[] for i,v in enumerate(self.tonliste): n = note.Note(v[0]) n.duration.quarterLength = v[1] n.volume.velocity=70 notes.append(n) part4.append(notes) part1=stream.Part() part1.append(self.akkorde1) part2=stream.Part() part2.append(self.akkorde2) part3=stream.Part() part3.append(self.akkorde3) fertig=[part1, part2, part3, part4] stream2=stream.Stream(fertig) metronome=tempo.MetronomeMark("allegro", 240, note.Note(type='quarter')) stream2.insert(metronome) player=StreamPlayer(stream2) player.play()
Leider treten vereinzelt störende, sehr tiefe Töne auf, die wir noch nicht vermeiden können. Im folgenden Beispiel haben wir die Wahrscheinlichkeit dieser deutlich erhöht. tiefe_toene.mid
Die entstehenden Kompositionen können wir jetzt als Midi-Dateien und als Textdateien speichern. Auch die tiefen Töne können wir nach längerem Herumprobieren vermeiden. Das Programm improvisiert nun über 12 Takte eine Melodie auf dem Bluesschema. Hier eine Kostprobe von unserem Programm:kostprobe_blues_improvisation.mid
Im Großen und Ganzen sind wir zufrieden mit unserem Ergebnis. Wir haben wie geplant ein Programm geschrieben, das uns immer wieder neue bluesige Stücke komponiert, wenn auch noch sehr einfach gehalten. Uns beiden macht das Projekt aber immer noch großen Spaß und wir sind dabei, es immer weiter zu verbessern. Beispielsweise ist die Dynamik noch gar nicht beachtet und auch die Harmonien sind noch sehr simpel. Außerdem versuchen wir eine Möglichkeit zu finden, in der Begleitung einen Rhythmus zu finden, der dann in der Melodie aufgegriffen und verändert werden kann. Wenn wir irgendwann das Projekt als abgeschlossen betrachten, werden wir noch eine Homepage erstellen, auf der das Programm abgespielt werden kann.