Benutzer-Werkzeuge

Webseiten-Werkzeuge


ws1415:projekte_im_wintersemester_2014_15:bildzutoncode

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen gezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
ws1415:projekte_im_wintersemester_2014_15:bildzutoncode [2014/11/27 18:09]
mr.lazy
ws1415:projekte_im_wintersemester_2014_15:bildzutoncode [2016/05/10 14:46] (aktuell)
Zeile 2: Zeile 2:
  
 ====== Code ====== ====== Code ======
-==== BILDINFO ====+ 
 +==== BILDINFO ====  
 +//ALT//
 <code python> <code python>
 #-*- coding: utf-8 -*- #-*- coding: utf-8 -*-
Zeile 37: Zeile 39:
 ==== FREQUENZEN ==== ==== FREQUENZEN ====
 <code python> <code python>
-w={} +#-*- coding: utf-8 -*- 
-w["​c"​]=[261.626] +from __future__ import division 
-w["​d"​]=[293.665] +frequenzen={} 
-w["​e"​]=[329.628] +frequenzen["​c"​]=[261.626] 
-w["​f"​]=[349.228] +frequenzen["​d"​]=[293.665] 
-w["​g"​]=[391.995] +frequenzen["​e"​]=[329.628] 
-w["​a"​]=[440.000] +frequenzen["​f"​]=[349.228] 
-w["​h"​]=[493.883]+frequenzen["​g"​]=[391.995] 
 +frequenzen["​a"​]=[440.000] 
 +frequenzen["​h"​]=[493.883
 +intervalle={} 
 +intervalle['​sekunde'​]=[9/​8] 
 +intervalle['​gterz'​]=[5/​4] #große terz 
 +intervalle['​quarte'​]=[4/​3] 
 +intervalle['​quinte'​]=[3/​2] 
 +intervalle['​gsexte'​]=[5/​3] #große sexte 
 +intervalle['​gseptime'​]=[15/​8] #große septime 
 +intervalle['​oktave'​]=[2]
 def getFreq(note):​ def getFreq(note):​
- return float(w[note][0])+ return float(frequenzen[note][0]) 
 +def getRatio(intervall):​ 
 + return float(intervalle[intervall][0])
 </​code>​ </​code>​
 +==== Bild ====
 +<code python>
 +from __future__ import division
 +from PIL import Image
 +import random
 +class Bild():
 + def __init__(self,​ filename):
 + self.image = Image.open(filename).convert('​RGBA'​)
 + self.pixels = self.picInfo()
 + def picInfo(self):​
 + '''​Gibt eine Liste der Pixel als Tupel zurück.'''​
 + pixels = list(self.image.getdata())
 + return pixels
 + def average(self):​
 + '''​Gibt den Durchschnitt über alle Pixel zurück.'''​
 + s = 0  #speichert die Summe
 + for tupel in self.pixels:​
 + for num in tupel:
 + s += num
 + return s/​(len(self.pixels)*len(self.pixels[0]))
 + def getMax(self):​
 + '''​Gibt eine Liste mit einem Eintrag für jeden Pixelzurück,​ wo der überwiegenden Farbanteil und die dazugehörige Komponente gespeichert wird.
 + Wenn es mehrere gleiche Werte gibt, wird einer zufällig gezogen'''​
 + maxList = []
 + for pixel in self.pixels:​
 + pmax = max(pixel[:​-1]) #​Das Maximum der Farbanteile
 + maxl = [] #Alle Farbanteile,​ die diesem Maximalwert entsprechen.
 + for p in enumerate(pixel[:​-1]):​
 + if p[1] == pmax:
 + maxl.append(p)
 + maxList.append(random.choice(maxl)) #​Gib einen zufälligen Wert aller Maximalwerte zurück.
 + return maxList
 +</​code>​
 +==== Sound ====
 +<code python>
 +#-*- coding: utf-8 -*-
 +from __future__ import division
 +import schallwerkzeuge as swz
 +import numpy as np
 +class Sound():
 + def __init__(self,​ sounds):
 + if len(sounds) > 0:
 + sound = np.zeros(len(sounds[0]))
 + for s in sounds:
 + sound += s/​len(sounds)
 + self.sound = sound
 + else:
 + self.sound = np.array([])
 + def play(self):
 + '''​Spielt die Sounddatei ab.'''​
 + swz.playsnd(self.sound,​ swz.RATE)
 + def save(self, filename):
 + '''​Speichert den Sound in einer .wav-datei'''​
 + swz.wavwrite(filename,​ self.sound)
 + def appendSound(self,​ sounds):
 + '''​Fügt am Ende des Sounds weitere sounds hinzu.'''​
 + sound = np.zeros(sounds[0].size)
 + for s in sounds:
 + sound += s/​len(sounds)
 + self.sound = np.append(np.append(self.sound,​np.zeros(300)),​ sound)
 + def addSound(self,​ sounds):
 + '''​Mischt den bestehenden Sound mit anderen.'''​
 + sound = np.zeros(len(sounds[0]))
 + for s in sounds:
 + sound += s(len(sounds) + 1)
 + sound += self.sound/​(len(sounds) + 1)
 + self.sound = sound
 + def length(self):​
 + '''​Gibt die Länge des Sounds in Sekunden zurück'''​
 + return self.sound.size/​swz.RATE
 +</​code>​
 +==== Schallwerkzeuge ==== 
 +//Von Stefan bereitgestellt//​
 +<code python>
 +#​!/​usr/​bin/​python
 +# coding=utf8
 +## Verhalten von / wie in Python 3    a/b  = float
 +
 +from __future__ import division
 +from math import pi
 +
 +### Audio-Module
 +
 +import pyaudio
 +from scipy.io import wavfile
 +
 +###  Fourier, Numerik
 +
 +import numpy.fft as FFT
 +import numpy as np
 +
 +
 +##############​
 +
 +# Graphik
 +
 +import matplotlib.pyplot as plt
 +
 +
 +###
 +
 +CHANNELS=1
 +RATE=44100
 +DAUER=1
 +ANZAHL=DAUER*RATE # ANZAHL der Frames,
 +GRENZE=0.02*RATE
 +
 +# globale Variablen, schlechter Programmierstil,​ hier aber ganz
 +# praktisch: Nach Aufnahme oder Lesen werden diese Variablen ​
 +# entsprechend gesetzt.
 +
 +
 +def recordsnd(filename,​ time):
 + '''​
 + Nimmt eine  time  Sekunden lange Schallsequenz
 + auf (mit der Samplerate 48000 Hz) auf und speichert,
 + speichert sie in der Datei filename und gibt die
 + Aufnahme als numpy-array mit Werten zwischen -1 und 1 zurück
 + '''​
 + global ANZAHL
 + global RATE
 + global DAUER
 + global CHANNELS ​
 +
 + DAUER=time
 + ANZAHL=int(RATE*DAUER)
 +
 +
 + p = pyaudio.PyAudio()
 + raw_input("​Aufnehmen ​ "​+str(time)+ " Sekunden): Eingabetaste drücken..."​)
 +
 + stream = p.open(format =pyaudio.paInt16 ,
 +                channels = CHANNELS,
 +                 rate = RATE,
 +                 input = True,
 +                 frames_per_buffer = 1024)#​ANZAHL)
 +
 +
 + yy=stream.read(ANZAHL)
 + y=np.fromstring(yy,​dtype=np.short)
 + if CHANNELS==2:​
 + y=y.reshape((y.shape[0]//​2,​2))
 +
 + print("​Aufgenomen:​ " + str(ANZAHL) + " Frames"​)
 + stream.close()
 + p.terminate()
 + yy=np.array(y,​dtype='​d'​)/​32768.0
 + if filename!=None:​
 + wavwrite(filename,​ yy)
 +
 + return yy
 +
 +
 +def wavwrite(filename,​ y):
 + '''​Schreibt ein wav-File aus einem numpy-array'''​
 + wavfile.write(filename,​ RATE, np.array(y*2**15,​ dtype=int))
 +
 +
 +def wavread(filename):​
 + '''​Liest ein wav-File in ein numpy-Array'''​
 + global ANZAHL
 + global RATE
 + global DAUER
 + global CHANNELS ​
 +
 + RATE,​y=wavfile.read(filename)
 + ANZAHL=y.shape[0]
 + if len(y.shape)==2:​
 + CHANNELS=y.shape[1]
 + else:
 + CHANNELS=1
 + DAUER=ANZAHL/​RATE
 +
 + return np.array(y,​dtype=np.float)/​2**15
 +
 +
 +
 +
 +
 +def playsnd(y, r):
 + '''​
 + spielt das numpy-array (bzw. Vektor) y als Klang 
 + mit der Samplerate r  [Hz] ab.
 + '''​
 + #​testkommentar
 + def callback(in_data,​ frame_count,​ time_info, status):
 + data = (32767*klanggen.generate(frame_count/​RATE)).astype(np.int16)
 + return (data.copy(),​ pyaudio.paContinue)
 +
 +
 + p = pyaudio.PyAudio()
 +
 + CHUNK=2**12
 +
 + stream = p.open(format =pyaudio.paInt16 ,
 + channels = CHANNELS,
 + rate = r,
 + output = True,
 + frames_per_buffer=CHUNK)
 + #​callback=callback)#​ANZAHL)
 +
 +
 + yy=np.array(y.flatten()*2**15,​dtype=np.short)
 + for i in range(0,​len(yy),​CHUNK):​
 + stream.write(yy[i:​i+CHUNK].tostring(order='​F'​))
 + stream.close()
 + p.terminate()
 +
 +
 +
 +def inspectsnd(y):​
 + '''​Zeigt einen Plot von y'''​
 +
 + n=y.shape[0]
 + fig=plt.Figure(figsize=(6,​4),​dpi=100)
 + ax=fig.add_subplot(111)
 + x=np.arange(0,​n,​1)/​np.float32(RATE)
 + plt.plot(x,​y)
 +
 + plt.show()
 +
 +
 +
 +def inspectspec(y):​
 + '''​zeigt einen Plot des DFT-Spektrums von y'''​
 + global RATE
 +
 + #  Vorbereitungen,​ um ein Fenster zum Plotten zu kriegen
 + plt.figure(1)
 +
 + n=len(y)
 + window=np.blackman(n)
 + sumw=sum(window*window)
 +
 + A=FFT.fft(y*window) ​
 + B2=(A*np.conjugate(A)).real
 + sumw*=2.0 ​  
 + sumw/​=1/​np.float(RATE) ​ # sample rate
 + B2=B2/sumw
 +
 +
 + x=np.arange(0,​n/​2,​1)/​np.float(n)*RATE
 +
 +
 + eps=1e-8
 + plt.plot(x,​ np.log10(B2[0:​n/​2]+eps))
 + plt.show()
 +
 +def sinewave(f,​r,​d):​
 + '''​erzeugt die Daten einer Sinusschwingung mit Frequenz f, Dauer d
 + und Samplerate r'''​
 + global RATE
 + global DAUER
 + global ANZAHL
 +
 + RATE=r
 + DAUER=d
 + ANZAHL=DAUER*RATE
 + y=np.zeros(r*d)
 + lastzero = 0
 + for i in range(0,​int(r*d)):​
 + y[i]=np.sin(2*pi*f*i/​float(r))*window(i,​r*d)
 + return y
 +def window(i, l, grenze=0.01):​
 + '''​Lässt die Sinuswelle an den Rändern gegen 0 laufen, damit das "​Klacken"​ beim Zusammenfügen von Bildern nicht entsteht.''' ​
 + global RATE
 + global GRENZE
 + GRENZE = grenze*RATE
 + m = min(i, l-i)
 + if (m < GRENZE):
 + return (m/​GRENZE)**(2/​3)
 + else:
 + return 1
 +</​code>​
 +==== Dreiklaenge ====
 +Testklasse, die aus beliebigen Bildern eine Folge von Dreiklängen erzeugt.
 +<code python>
 +#-*- coding: utf-8 -*-
 +from __future__ import division
 +import frequenzen as freq
 +from bild import Bild
 +import schallwerkzeuge as swz
 +from sound import Sound
 +
 +pic = Bild('​allfarben.png'​)
 +win = Bild('​ico-win.png'​)
 +
 +def dreiKlang (i,​grundton,​l): ​ #l=1 Tonlänge
 +    '''​erzeugt einen Dreiklang. Dabei repräsentiert 0 die Tonika, 1 die Subdominante und 2 die Dominante.'''​
 +    toene = 3 #Anzahl der Töne des Dreiklangs
 +    gfreq = freq.getFreq(grundton) #Frequenz des Grundtons
 +    grundton = swz.sinewave(gfreq,​swz.RATE,​ l)
 +    ​
 +    if i == 0: #Tonika
 +
 +        terz = swz.sinewave(gfreq*freq.getRatio('​gterz'​),​ swz.RATE, l)
 +        quinte = swz.sinewave(gfreq*freq.getRatio('​quinte'​),​ swz.RATE, l)
 +
 +        return Sound([grundton,​ terz, quinte])
 +
 +    elif i == 1: #​Subdominante
 +
 +        quarte = swz.sinewave(gfreq*freq.getRatio('​quarte'​),​ swz.RATE, l)
 +        sexte = swz.sinewave(gfreq*freq.getRatio('​gsexte'​),​ swz.RATE, l)
 +
 +        return Sound([grundton,​ quarte, sexte])
 +    elif i == 2: #Dominante
 +
 +        sekunde = swz.sinewave(gfreq*freq.getRatio('​sekunde'​),​ swz.RATE, l)
 +        quinte = swz.sinewave(gfreq*freq.getRatio('​quinte'​),​ swz.RATE, l)
 +        septime = swz.sinewave(gfreq*freq.getRatio('​gseptime'​),​ swz.RATE, l)
 +        ​
 +        return Sound([sekunde,​ quinte, septime])
 +
 +def playSound(bild,​ l=1):
 +    s = Sound([])
 +    for maxi in bild.getMax():​
 +        #​dreiKlang(maxi[0],​ '​c'​).play()
 +        s.appendSound([dreiKlang(maxi[0],​ '​c',​ l).sound], swz.GRENZE)
 +        #print s.length()
 +        #print s.sound
 +    s.play()
 +</​code>​
 +==== Soundv2 ====
 +//Nur überarbeitete Strukturen//​
 +<code python>
 +def appendSoundP(self,​ sounds):
 + '''​Fügt am Ende des Sounds weitere sounds hinzu.'''​
 + sound = np.zeros(sounds[0].size)
 + for s in sounds:
 + sound += s/​len(sounds)
 + self.sound = np.append(self.sound,​ sound)
 + def appendSound(self,​ sounds, grenze):
 + if (self.sound.size-grenze < 0 or sounds[0].size-grenze < 0):
 + print 'zu kurz.'
 + self.appendSoundP(sounds)
 + return
 + print 'lang genug.'​
 + sound2 = self.addSounds(sounds)
 + soundganz = np.append(np.append(self.sound[:​self.sound.size-grenze],​ np.zeros(grenze)),​sound2[grenze:​])
 + soundsum = np.zeros(soundganz.size)
 + for i in range(int(grenze)):​
 + soundsum[self.sound.size-grenze+i] = (self.sound[self.sound.size-grenze+i] + sound2[i])
 + soundganz += soundsum
 + self.sound = soundganz
 + def technoAppend(self,​ sounds, grenze):
 + '''​Klingt einfach nur lustig'''​
 + if (self.sound.size-grenze < 0 or sounds[0].size-grenze < 0):
 + print 'zu kurz.'
 + self.appendSoundP(sounds)
 + return
 + print 'lang genug.'​
 + sound2 = self.addSounds(sounds)
 + soundganz = np.append(self.sound,​ sound2[grenze:​])
 + soundmult = np.zeros(soundganz.size)
 + soundmult.fill(1)
 + for i in range(int(grenze)):​
 + soundganz[self.sound.size-grenze+i] += sound2[i]
 + soundmult[self.sound.size-grenze+i] = soundganz[self.sound.size+i]-1
 + soundganz -= soundmult
 + self.sound = soundganz
 +</​code>​
 +==== Bildv2 ====
 +//Nur überarbeitete Strukturen//​
 +<code python>
 +    def pixList(self):​
 +            '''​Teilt Pixels in Bildspalten auf'''​
 +            (width,​height)=self.image.size
 +            plraster=[]
 +            for i in range (width):
 +                hl=self.pixels[i::​width]
 +                plraster.append(hl)
 +            return plraster
 +</​code>​
 +==== Farbräume ====
 +<code python>
 +
 +    def farbraume (self, z=2): #z Anzahl der Teilbereiche pro Farbkomponente
 +        '''​Wenn man sich den RGB-Farbraum als Würfel vorstellt, so kann man diesen in kleinere Würfel zerlegen. Diese Methode zerlegt den Würfel in z*z*z kleine Würfel und ordnet jedem Pixel den Würfel zu, in dem er liegt.'''​
 +        farbraume = []
 +        for i in range (z):
 +            for j in range (z):
 +                for k in range (z):
 +                    farbraume.append((i,​j,​k))
 +
 +        konvpixel = []
 +        for pixel in self.pixels:​
 +            r = pixel[0]
 +            g = pixel[1]
 +            b = pixel[2]
 +            for i in range(z): ​         ​
 +                if (256/z)*i <= r and r < (256*(i+1))/​z:​
 +                    rl = i #rot
 +                if (256/z)*i <= g and g < (256*(i+1))/​z:​
 +                    gl = i #grün
 +                if (256/z)*i <= b and b < (256*(i+1))/​z:​
 +                    bl = i #blau
 +            konvpixel.append((rl,​gl,​bl))
 +        return [farbraume.index(tupel) for tupel in konvpixel]
 +</​code>​
 +==== Synthesizer ====
 +<code python>
 +def tune(f, d, r=44100, mode='​sinus'​):​
 + vol = 1
 + waves = []
 + waves.append(sinewave(f,​ r, d))
 + if mode == '​organ':​
 + while vol >= 0.1:
 + vol *= 0.7
 + waves.append(sinewave(f*freq.getRatio('​quinte'​),​ r, d, vol=vol/​2))
 + f *= freq.getRatio('​oktave'​)
 + waves.append(sinewave(f,​ r, d, vol = vol))
 + if '#'​ in mode: #bei #​a-z1,​b-z2,​c-z3,​d-z4,​g-z5,​t-z6,​e-z7 wird der a-te, b-te, ..., e-te oberton hinzugefügt mit z1, z2,..., z7 % lautstärke
 + toene = mode[1:​].split(','​)
 + for ton in toene:
 + waves.append(sinewave(f*2**int(ton[:​ton.index('​-'​)]),​ r, d, vol=int(ton[ton.index('​-'​):​])/​100))
 +
 + return wave
 +</​code>​
 +==== MARKOV ====
 +<code python>
 +    from __future__ import division
 +    from bild import Bild
 +    import matplotlib.pyplot as plt
 +    import numpy as np
 +    from sound import *
 +    import frequenzen as freq
 +    import zufallszahlen as zahlen
 +    import rhythm
 +    ​
 +    def normMatrix(matrix):​
 +            '''​normiert die Spalten der Matrix, so dass die Summe aller elemente einer Spalte genau 1 ergibt, das Verhaeltnis zwischen den Spalten aber                                                   noch erhalten bleibt.'''​
 +        matrixcopy = []
 +        for col in matrix:
 +            colcopy = []
 +            nenner = sum(col)
 +            if nenner != 0:
 +                for element in col:
 +                    colcopy.append(element/​nenner)
 +                matrixcopy.append(colcopy)
 +            else:
 +                colcopy = [1/len(col) for x in range(len(col))]
 +                print colcopy
 +        return matrixcopy
 +    def step(matrix,​ vec, step = 1):
 +        '''​berechnet den Vector der Notenwahrscheinlichkeit nach step Noten'''​
 +        for i in range(step):​
 +            retvec = []
 +            for y in range(len(matrix[0])):​
 +                retvec.append(sum([matrix[x][y]*vec[x] for x in range(len(matrix))]))
 +            vec = retvec[:]
 +        return vec
 +    def nextTune(matrix,​ vec, val):
 +        '''​val muss zwischen 0 und 1 liegen. Dann berechnet diese Methode den naechsten Ton, der gespielt wird.'''​
 +        element = 0
 +        probvec = step(matrix,​ vec)
 +        print sum(probvec),​ " >= ", val
 +        while sum(probvec[:​element+1]) < val:
 +            element += 1
 +            if (element == len(probvec)-1):​
 +                return element
 +        #print '​Tonhoehe berechnet.'​
 +        return element
 +    def generateMelodie(pic,​ nums, tunes = 24):
 +        '''​nums muss eine Liste mit zahlen zwischen 0 und 1 sein'''​
 +        matrix = pic.transformImage(tunes,​ tunes)
 +        matrix = normMatrix(matrix)
 +        melodie = []
 +        vec = genVector(9,​ tunes)
 +        for num in nums:
 +            melodie.append(nextTune(matrix,​ vec, num))
 +            vec = genVector(melodie[-1])
 +            print 'Ton wurde der Melodie hinzugefuegt.'​
 +        #print '​Melodie erzeugt.'​
 +        return melodie
 +    def generateTune(pic,​ notes = 240, tunes = 24, length = 1, mode = '​sinus'​):​
 +        '''​generiert ein Sound object, mit einer durch markov ketten erstellte melodie'''​
 +        nums = zahlen.zufallsZahl(pic,​ notes)
 +        print nums
 +        print '​Zufallszahlen generiert.'​
 +        melodie = generateMelodie(pic,​ nums, tunes)
 +        print '​Melodie erzeugt.'​
 +        rhythmus = rhythm.generateRhythm(pic,​ len(nums), length, 4)
 +        print '​Rhythmus erzeugt.'​
 +        tonlage = 1
 +        s = Sound(np.array([]))
 +        for i in range(len(melodie)):​
 +            ton = melodie[i]
 +            laenge = rhythmus[i]
 +            tune = createTune(freq.getFreq(freq.buchstaben[ton%8][0])*2**(tonlage-ton//​8),​ laenge, mode = mode)
 +            #print max(tune)
 +            s.appendSound([tune],​ swz.GRENZE)
 +        print '​Fertig.'​
 +        return s
 +    def genVector(eins,​ length = 24):
 +        '''​generiert einen Vector der Laenge length mit einer 1 an der stelle eins'''​
 +        vec = [0 for i in range(length)]
 +        vec[eins] = 1
 +        return vec
 +    def genRandList(length,​ maximum):
 +        return [(np.random.random_integers(maximum+1)-1)/​maximum for x in range(length)]
 +</​code> ​   ​
 +==== Zufallszahlen ====
 +Zufallszahlen für Markov-ketten
 +<code python>
 +# -*- coding: cp1252 -*-
 +#​Zufallszahl für Markov-Kette
 +
 +#-*- coding: utf-8 -*-
 +from __future__ import division
 +from PIL import Image
 +import random
 +from bild import Bild
 +import numpy as np
 +
 +def spaltenBreite(bild,​ spalten = 240):
 +    '''​Spaltenbreite der Pixel die zusammengefasst wird!'''​
 +    n = bild.bBreite()
 +    b = int((n-(n%spalten))/​spalten)
 +    if b == 0:
 +        return 1
 +    return b 
 +
 +def spalten(bild,​ spaltenum = 240):
 +    spalten = []
 +    pic = bild.rasterList(bild.farbraume(4))
 +    sBreite = spaltenBreite(bild,​ spaltenum)
 +    ende = bild.bBreite()-bild.bBreite()%sBreite
 +    if ende == 0:
 +        ende = bild.bBreite()
 +    for i in range(0, ende, sBreite):
 +        spalte = []
 +        for x in range(sBreite):​
 +            if (i+x < len(pic)):
 +                spalte += pic[i+x]
 +        spalten.append(spalte)  ​
 +    return spalten
 +
 +def zufallsZahl(bild,​ spaltenum = 240):
 +    n = bild.bBreite()
 +    b = spaltenBreite(bild,​ spaltenum)
 +    l = int(bild.bHohe())
 +    r = b * l
 +
 +    spalter = []
 +    spaltenliste = spalten(bild,​ spaltenum)
 +    ​
 +    for spalte in spaltenliste:​
 +        spalter.append(sum(spalte)/​len(spalte)) ​       ​
 +
 +    zZahl = []
 +    for spalte in range(len(spalter)):​
 +        varianz = 0
 +        for y in range(r):
 +            x1 = spalter[spalte]
 +            x2 = spaltenliste[spalte][y]
 +            varianz += 1/r*(x1 - x2)**2
 +        zZahl.append(varianz)
 +    maximum = max(zZahl)
 +    return [num/​maximum for num in zZahl]
 +</​code>  ​
ws1415/projekte_im_wintersemester_2014_15/bildzutoncode.1417108169.txt.gz · Zuletzt geändert: 2016/05/10 14:46 (Externe Bearbeitung)