Musiktheorie einmal praktisch

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
barrio
User
Beiträge: 30
Registriert: Dienstag 26. Februar 2013, 13:15

Hi zusammen,

für alle die sich für musiktheoretische Zusammenhänge interessieren, gibt es ein schönes kleines und recht neues Projekt auf Github namens musthe, auf welches ich durch python weekly aufmerksam wurde:

https://github.com/falziots/musthe

Es befindet sich noch in der absoluten Anfangsphase, ist daher sehr überschaubar und somit auch für noch nicht so fortgeschrittene Pythonianer geeignet. Grundkenntnisse über OOP & unittests in Python sowie Umgang mit git wären allerdings schon hilfreich.

Implementiert sind bislang nur Klassen für Noten, Intervalle, die gängigsten Skalen sowie ein paar elementare Akkorde. Sehr schön zum Ausprobieren ist die __add__-Methode der Notenklasse: Wenn man ein Intervall addiert, soll die neue Note mit korrekten Vorzeichen berechnet werden (ist leider noch etwas buggy :-)).

Wenn man das weiterspinnt sind da perspektivisch interessante Sachen wie harmonische Analyse von Stücken etc. denkbar. Wer sich bereits ausgefeilte Projekte ansehen möchte, sollte mal einen Blick auf music21 oder mingus werfen, die sind allerdings schon echt komplex, insbesondere ersters.

Ich und v. a. Gonzales, der das Projekt leitet würden sich über alle freuen, die Lust haben mitzumachen.

Viele Grüße
Barrios
> (...(lambda...(it-schemes-i-must-be-jailed-in-braces? code)...))))))))))))))))))))))))))
#t
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@barrio
Habs mal kurz überflogen und will mich nicht an der Codequalität aufhängen. Weil Du harmonische Analyse angesprochen hast, habe ich eher ein paar Anmerkungen zur Vorgehensweise, wie Ihr Intervalle berechnet:

Für eine funktionsharmonisch korrekte Intervallbehandlung (und damit dann auch alle Mehrklänge als zusammengesetzte Intervalle) könnt Ihr nicht nur über die Halbtöne gehen. Intervalle beziehen sich immer auf die Basisabstände in der CDEGFAH-Reihe. So führt z.B. Eure kleine Terz auf As:

Code: Alles auswählen

        # Fixing Issue #7: Note('Ab')+Interval('m3') --> Exception
        if self.tone == 'A' and self.accidental.startswith('b') and interval.number == 3 and interval.semitones == 3:
            new_note_tone = 'B'
        else:
            new_note_tone = 'CDEFGABCDEFGABCDEFGAB'[_old_tone+interval.number-1]
zum falschen Ton H, korrekt wäre Ces. (Implizite enharmonische Verwechslung ist gerade bei Intervallbetrachtungen nicht erlaubt. Hinzu kommt, dass H und Ces nur in bestimmten temperierten Stimmungen gleich klingen!)

Korrekte Vorgehensweise bei "addieren" von Intervallen wäre, den alterierten Ton auf den Basiston zu reduzieren (ohne Vorzeichen betrachten), den auf das Basisintervall reduzierte Abstand (groß/klein/übermässig/vermindert raus) zum Basiston zu addieren. Die Summe dessen modulo 7 (für 7 Basistöne) ergibt den Zielton. Anschliessend den Basiston um Alterationen korrigieren und den Zielton gemäss den Halbtönen des Intervalls anpassen/alterieren, z.B:

Ges + übermässige Septime
Ges --> Basiston G (b gemerkt)
G + 7 - 1 (-1, da die Prime auf den Ton selbst zeigt) modulo 7 --> F
G wieder zu Ges (+1 Halbton)
Halbtonkorrektur (Ges-F => 11, fehlt ein Halbton) F --> Fis

Damit erhaltet Ihr nach der klassischen Harmonielehre immer korrekte Intervalle, welche die Funktion eines Intervalls stärker wichtet als die enharmonische Verwechslung zur Vorzeichenreduktion. Das gewährleistet, dass man bei komplexeren Akkorden den funktionsharmonischen Kontext noch erkennen kann, und aus einem As-Moll Dreiklang nicht ein As-H-Dis werden kann. Die Enharmonik in der klassischen Harmonielehre erlaubt durchaus Verwechslungen zur Vereinfachung, nur werden die Basisintervallabstände nicht angefasst. Aus As-Ces-Es darf z.B. Gis-H-Dis werden. Oder auch Fisisis-Aisis-Cisisis ;)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@barrio
Hier mal etwas Code zur Anregung:

Code: Alles auswählen

import re

BASETONES = 'cdefgah'

# overcommitted semitones for index within one octave
SEMI_MISSES = {0: 0, 1: 0, 2: 0, 3: 1, 4: 1, 5: 1, 6: 1}

# interval identifier => (base tone steps, semitone steps)
INTERVALS = {
    'P1': (1, 0), 'A1': (1, 1),
    'd2': (2, 0), 'm2': (2, 1), 'M2': (2, 2), 'A2': (2, 3),
    'd3': (3, 2), 'm3': (3, 3), 'M3': (3, 4), 'A3': (3, 5),
    'd4': (4, 4), 'P4': (4, 5),
    # etc.
}


class Note(object):
    _rex = re.compile('^([cdefgah])([b]*|[#]*)?([\d])$')

    def __init__(self, notestring, semi=0):
        parsed = re.search(self._rex, notestring)
        if not parsed:
            raise Exception("wrong note string '%s'" % notestring)
        self.base, self.sig, self.octave = parsed.groups()
        self.octave = int(self.octave)
        self.add_semitones(semi)

    def __str__(self):
        return "Note('%s%s%d')" % (self.base, self.sig or '', self.octave)

    @property
    def semitones(self):
        """Get semitones from alteration."""
        if self.sig:
            semitones = len(self.sig)
            if 'b' in self.sig:
                return -semitones
            return semitones
        return 0

    def add_semitones(self, semi):
        """Add semitones to note."""
        semi += self.semitones
        self.sig = None if not semi else (
            ('b' if semi < 0 else '#') * abs(semi))


def transpose(note, interval):
    """Return a new note transposed by interval."""
    if interval.startswith('-'):
        # find lower base tone
        interval = interval[1:]
        base, semitones = INTERVALS[interval]
        note_index = BASETONES.index(note.base)
        octave, pos = divmod(note_index - (base - 1), 7)
        res = Note('%s%s' % (BASETONES[pos], octave + note.octave))
        # add semitone difference to signature
        res.add_semitones(note.semitones - transpose(res, interval).semitones)
        return res
    base, semitones = INTERVALS[interval]
    note_index = BASETONES.index(note.base)
    octave, pos = divmod(note_index + (base - 1), 7)
    # calculate overcommitted semitones for whole interval (adds 2 per octave)
    correction = SEMI_MISSES[pos] - SEMI_MISSES[note_index] + 2 * octave
    return Note('%s%s' % (BASETONES[pos], octave + note.octave),
                note.semitones + semitones - (base - 1) * 2 + correction)

if __name__ == '__main__':
    # quick and dirty sorting
    order = ['d', 'm', 'P', 'M', 'A']
    f = lambda x, y: ((int(x[-1:]) - int(y[-1:])) * 10 +
                      order.index(x[-2:-1]) - order.index(y[-2:-1]))
    # some tests
    note = Note('c1')
    for interval in sorted(INTERVALS.keys(), cmp=f):
        print note, interval, transpose(note, interval)
    for interval in sorted(INTERVALS.keys(), cmp=f):
        print note, '-'+interval, transpose(note, '-'+interval)
Die abschliessende Halbtonkorrektur ist doch schwieriger als ich anfangs annahm. Vielleicht kannst Du was damit anfangen. (Achtung: habs nicht sonderlich getestet, es ist gut möglich, dass es nicht für alle Intervalle und Startpositionen richtig funktioniert.)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

jerch hat geschrieben:

Code: Alles auswählen

# overcommitted semitones for index within one octave
SEMI_MISSES = {0: 0, 1: 0, 2: 0, 3: 1, 4: 1, 5: 1, 6: 1}
Hier hätte es aber auch eine Liste getan. ;)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Zugegeben. Sowas passiert halt, wenn mans nicht überarbeitet. Die Datenstruktur hat eine kleine Evolution hingelegt - von singulären "Nurhalbton"-Einträgen über zusätzliche für die absteigenden Intervalle. Übrig geblieben ist - nunja, ein Wörterbuch, welches fortlaufende lückenlose Indices auf Werte mappt. Sehr praktisch, so ein Wörterbuch. :P
barrio
User
Beiträge: 30
Registriert: Dienstag 26. Februar 2013, 13:15

@ jerch

Vielen Dank für Deine sehr differenzierten Anregungen! Du scheinst ja sowohl in Python als auch Musiktheoie ziemlich versiert zu sein und wärst damit sicherlich eine große Bereicherung als Contributor für das Projekt!

Ich habe mir ehrlich gesagt an der Intervallberechnung auch schon etwas die Zähne ausgebissen und werde mir jetzt Deinen Code mal genauer zu gemüte führen. Meine Intention war bereits, pragmatisch die zulässigen Tonarten auf den Quintenzirkel (erster Kreis) zu begrenzen, um die Fehlerquellen zu minimieren und dabei erstmal von dem heutzutage in westlichen Kulturkreisen üblichen gleichschwebenden Tonsystem auszugehen, damit u. a. enharmonisch verwechselte Tonarten wie z. B. Fis- und Ges-Dur auch akustisch identisch sind.

Würde mich freuen, wenn wir uns auf Github wiederbegegnen und gemeinsam die Köpfe rauchen lassen :D ...

Viele Grüße
barrio
> (...(lambda...(it-schemes-i-must-be-jailed-in-braces? code)...))))))))))))))))))))))))))
#t
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@barrio:
Leider hab ich keine Zeit, an dem Projekt aktiv mitzuwirken, bin aber gern bereit, bei konkreten Fragestellungen meinen Senf in die Waagschale zu werfen.

Folgende Tipps dazu hab ich noch:
Ich bin mir nicht sicher, ob Ihr mit Eurer derzeitigen Modellierung von Intervallen und Akkorden glücklich werdet. Ich würde Akkorde wahrscheinlich eher über Intervallschichtungen "herleiten", was dann z.B. auch Betrachtungen zu Umkehrungen oder enge/weite Lage recht einfach macht. Selbst die Jazzharmonik verwendet dieses Prinzip noch sehr stark (Ausnahmen sind Quart- und Quintakkorde, die mit Umkehrungen ihre typische Wirkung verlieren). Damit seid Ihr in jedem Falle für die westlichen Skalen und Harmonievorstellungen gut aufgestellt.

Generell könnten in der Modellierung zumindest Teilaspekte von UML wichtig sein, um der Überkomplexität dieses Theoriegebäudes Herr zu werden. Nichts ist ärgerlicher als wiederholtes Umschreibenmüssen von APIs, ein rein agiler Ansatz stößt imho bei einer solchen Projektgröße immer an konzeptionelle Sollbruchstellen, welche mal eben 50% des Codes in Frage stellen können. Und spätestens nach dem 3. Monkeypatch gegen die vermeintlich richtige API wirds zum Kraus, was gerade Freizeitprojekte gefährden kann, wie ich selbst mehrfach miterleben musste.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wobei eine agile Herangehensweise oftmals nützlich sein kann, um überhaupt eine Vorstellung davon zu haben, wie eine vernünftige API denn im Detail aussehen sollte. Gerade bei Themen, wo man eher wenig von anderen Softwareprojekten abgucken kann und mehr oder weniger ins Blaue entwickelt, ist es IMHO ganz gut, die Dinge recht zeitnah auszuprogrammieren. Code wegschmeißen wird man bei einem solchen Projekt ohnehin zu Genüge. Da kann man sich prinzipiell auch erst ab der zweiten oder gar dritten Iteration Gedanken um eine konsistente API machen. Aber in der Hinsicht scheiden sich vermutlich die Geister. Ich möchte jetzt auch nicht behaupten, dass umfangreiche UML-Diagramme hier der komplett falsche Ansatz wären.
Antworten