Zufällig generierte Midi.tracks

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
artificalstupidity
User
Beiträge: 1
Registriert: Montag 10. April 2023, 21:06

Moin,

ich arbeite aktuell an einem Skript, welches zufällige didi.tracks erstellt.

Der aktuellste Code würde denke ich den Rahmen sprengen.
Deshalb habe ich ersteinmal den Code gepostet mit dem die ersten kleinen Tracks entstanden sind bzw. Notenansammlungen ^^
Musik kann man das ganze noch nicht nennen :mrgreen:

Das aktuelle skript welches für das Forum zu groß wäre spuckt u.A. folgenden Track aus:
https://www.youtube.com/watch?v=V0tqtYkB--s

Code: Alles auswählen

import random
from midiutil import MIDIFile
import subprocess


track = 0
channel = 0
time = 0
duration = 1
tempo = 280  # in BPM
volume = 100


mf = MIDIFile(1)
mf.addTempo(track, time, tempo)
mf.addProgramChange(track, channel, time, 19)

notes = { "llf":10 ,"llllf":120,  'b': 125,  'c': 115,  'bb': 125,  'cc': 115, 'oi':200, 'e': 105, 'f': 110 }
#very high piano 105,110
#clingding >110
#10 deep sound

# generate random piano track
for i in range(16000):
    #if(random.randint(0,99) > 50):
      #  tempo+=20
     #   mf.addTempo(track, time, tempo )
    #elif (tempo > 20) : tempo -=20
  #  if(random.randint(0,10) > 1):
    extramp = random.randint(0,10)
    if(extramp == 3):

        pitch = random.choice(list(notes.values()))
        mf.addNote(track, channel, pitch, time, duration, volume)
        time+=0.25

        mf.addNote(track, channel, pitch, time, duration, volume)
        time += 0.25
        if(random.randint(0,1) == 1):
            pitch = random.choice(list(notes.values()))

        mf.addNote(track, channel, pitch, time, duration, volume)
        time += 0.25
        mf.addNote(track, channel, pitch, time, duration, volume)
        time+=0.25

        continue
    elif(extramp == 2):
        pitch = random.choice(list(notes.values()))
        mf.addNote(track, channel, pitch, time, duration, volume)
        time+=0.5
        mf.addNote(track, channel, pitch, time, duration, volume)
        time += 0.5
        continue
    pitch = random.choice(list(notes.values()))
    mf.addNote(track, channel, pitch, time, duration, volume)
    time += 1
    #else:

    #    pitch = random.choice(list(notes.values()))
     #   mf.addNote(track, channel, pitch, time, duration/3, volume)
    #    time += 0.3
     #   pitch = random.choice(list(notes.values()))
    #    mf.addNote(track, channel, pitch, time, duration / 3, volume)
     #   time += 0.3
     #   pitch = random.choice(list(notes.values()))
     #   mf.addNote(track, channel, pitch, time, duration / 3, volume)
    #    time += 0.3

num = int((open("num.txt", "r").readlines())[0])
open("num.txt", "w").writelines(str(num + 1))


with open("random_piano_track" + "_" + str(num) +".mid", "wb") as output_file:
    mf.writeFile(output_file)
LG und einen schönen Restabend :)
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@artificalstupidity: Anmerkungen zum Quelltext: `subprocess` wird importiert, aber nicht verwendet.

Da ist auskommentierter Code der so aussieht als wäre jede Zeile einzeln auskommentiert worden. Brauchbare Texteditoren haben dafür eine Funktion, das man mehrere Zeilen markiert und die dann alle auf einmal auskommentiert, oder auch wieder einkommentiert.

Bei ``if``/``elif`` & Co sind unnötige Klammern um die Bedingung und das ist ausserdem noch so geschrieben, als wären diese Schlüsselworte Funktionen, also ohne Leerzeichen zwischen Schlüsselwort und der öffnenden Klammer.

`track`, `channel`, `duration`, `tempo`, und `volume` scheinen Konstanten zu sein. Das würde man dadurch kennzeichnen, das die Namen KOMPLETT_GROSS geschrieben werden.

`notes` schein auch konstant zu sein, zudem werden aus dem Wörterbuch im gesamten Code nie die Schlüssel, sondern immer nur die Werte benutzt, das könnte also eine Liste mit den Werten sein.

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen. Bei `mf` kann man noch raten, aber warum `extramp` so heisst, erschliesst sich mir nicht.

``continue`` sollte man vermeiden, weil das ein unbedingter Sprung ist, den man nicht an der Einrückung des Quelltextes ablesen kann. Das ist unübersichtlich und erschwert die Weiterententwicklung. Zum Beispiel wenn man Code aus der Schleife in eigene Funktionen herausziehen möchte, oder wenn man Code hinzufügen möchte, der am Ende von jedem Schleifendurchlauf ausgeführt werden soll. Im Vorliegenden Fall ist das doch einfach ein ganz normales ``if``/``elif``/``else``-Konstrukt bei dem überhaupt gar kein ``continue`` benötigt wird.

Die drei Fälle haben genug ähnlichen Code beim schreiben der Noten, dass man da eine Funktion herausziehen könnte.

Dateien die man öffnet, sollte man auch wieder schliessen. Beim schreiben der MIDI-Datei wird das ja auch mit Hilfe der ``with``-Anweisung gemacht, aber bei der Datei mit den Zähler nicht.

Statt mit `readlines()` alle Zeilen zu lesen und dann mit dem Index 0 nur auf die erste zuzugreifen ist unnötig umständlich. Man kann da mit `next()` einfach die erste Zeile lesen.

Beim schreiben der Zahl ist die `writelines()`-Methode das falsche Mittel. Da wird ja nur *eine* Zahl in Form von *einer* Zeichenkette geschrieben. Das wäre dann einfach nur `write()`. `writelines()` funktioniert zufällig auch, weil Zeichenketten iterierbar über die einzelnen Zeichen sind, was bedeutet das `writelines()` dann jedes Zeichen einzeln schreibt.

Textdateien sollte man immer explizit mit Angabe der Kodierung der Datei öffnen. Und am besten auch jede Zeile mit einem Zeilenendezeichen abschliessen.

Das zusammenstückeln von Zeichenketten und Werten mittels ``+`` und `str()` ist eher BASIC als Python. Dafür gibt es die `format()`-Methode auf Zeichenketten und f-Zeichenkettenliterale.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import random

from midiutil import MIDIFile

COUNTER_FILENAME = "num.txt"

TRACK = 0
CHANNEL = 0
DURATION = 1
TEMPO = 280  # in BPM
VOLUME = 100

NOTES = [10, 120, 125, 115, 125, 115, 200, 105, 110]


def add_notes(midi_file, time, pitch, time_delta, count):
    for _ in range(count):
        midi_file.addNote(TRACK, CHANNEL, pitch, time, DURATION, VOLUME)
        time += time_delta
    return time


def main():
    time = 0
    midi_file = MIDIFile(1)
    midi_file.addTempo(TRACK, time, TEMPO)
    midi_file.addProgramChange(TRACK, CHANNEL, time, 19)
    for _ in range(16_000):
        random_number = random.randint(0, 10)
        if random_number == 3:
            pitch = random.choice(NOTES)
            time = add_notes(midi_file, time, pitch, 0.25, 2)

            if random.randint(0, 1):
                pitch = random.choice(NOTES)

            time = add_notes(midi_file, time, pitch, 0.25, 2)

        elif random_number == 2:
            pitch = random.choice(NOTES)
            time = add_notes(midi_file, time, pitch, 0.5, 2)

        else:
            pitch = random.choice(NOTES)
            time = add_notes(midi_file, time, pitch, 1, 1)

    with open(COUNTER_FILENAME, "r", encoding="ascii") as lines:
        number = int(next(lines)) + 1
    with open(COUNTER_FILENAME, "w", encoding="ascii") as file:
        file.write(f"{number}\n")

    with open(f"random_piano_track_{number}.mid", "wb") as midi_file:
        midi_file.writeFile(midi_file)


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Cynob
User
Beiträge: 15
Registriert: Montag 11. Dezember 2017, 19:41

Hallo und guten Morgen @artificalstupidity

Danke für deine Inspiration! Du machst das schon richtig ;)
Antworten