It’s karaoke-time!
Ziel dieser Aufgabe ist es, ein Programm zu schreiben, welches die Lyrics eines Songs stückweise auf der Konsole anzeigt, während im Hintergrund der Song läuft.
Special: Wenn Sie diese Aufgabe im Vorkurs 2024 lösen, erhalten Sie die Möglichkeit, Ihr fertiges Proramm zu testen! Karaokeparty! Schauen Sie dazu während der Tutorienzeiten in Raum U.039 vorbei, oder fragen Sie zur Not die Dozierenden (oder zur Not Ihre:n Tutor:in).
Für Lyrics können Sie auf Datenbanken des Open Source-Karaokeprogramms “UltraStar Deluxe” zurückgreifen. Die Datenbanken stellen für viele bekannte Songs Textdateien zur Verfügung, in denen Textschnipsel zu Zeitstempeln zugeordnet werden.
Wir empfehlen Ihnen die Datenbank usdb.eu, da Sie diese ohne Account nutzen können. Finden Sie einen Song über die Startseite oder über den Musikkatalog, und rufen Sie die Seite des Songs auf (Beispiel). Anschließend können Sie über den Download-Button mit der Option “Txt” ausschließlich die Lyrics-Datei herunterladen (Beispiel).
Nun fehlt Ihnen noch die Musik zu Ihrem Song. Diese können Sie mithilfe des Python-Tools yt-dlp
herunterladen:
Installieren Sie, sofern noch nicht geschehen, yt-dlp
mit pip install yt-dlp
(nutzen Sie dafür ggf. ein virtual environment)
Suchen Sie das Musikvideo zu Ihrem Song auf YouTube und kopieren Sie den Link des Musikvideos (Beispiel). Wenn Sie usdb.eu für die Lyrics verwendet haben, können Sie nutzen, dass das entsprechene Musikvideo bereits in der Datenbank hinterlegt ist, indem Sie bei dem Video dort auf das YouTube-Logo unten rechts klicken.
Laden Sie die Audioversion des Videos herunter, indem sie in einem Terminal yt-dlp -x "<YouTube-Link"
(mit dem YouTube-Link) ausführen. Beispiel: yt-dlp -x "https://www.youtube.com/watch?v=mNEUkkoUoIA"
Hinweis: Laden Sie niemals Videos zu anderen als persönlichen Nutzungszwecken von YouTube herunter! Informieren Sie sich ggf. selbst über geltende Regelungen!
Das Format der UltraStar-Deluxe Lyrics-Dateien ist hier dokumentiert.
[...]
#RELATIVE: No
[...]
#BPM:309,20
#GAP:7190,00
#START:0
[...]
: 0 3 5 I
: 4 3 2 don't
: 9 3 5 know
: 13 3 2 what
: 18 7 5 you've
: 27 3 7 been
: 32 7 7 to
: 40 3 9 ~ld
- 45
: 66 2 0 But
: 71 4 7 time
: 76 2 9 is
: 80 3 7 run
: 84 3 7 ning
: 89 5 5 out
- 96
: 97 4 4 No
: 102 3 5 nee
: 106 3 7 ~d
: 110 2 5 to
: 115 3 5 take
: 119 2 5 it
: 124 7 4 slo
: 132 1 2 ~w
[...]
Wichtige Zeilen (für eine erste Version des Programms, das einige Angaben ignoriert) sind die Folgenden:
#BPM:309,20
: Hier wird die Geschwindigkeit des Songs in Beats/Minute angegeben. Alle Zeitangaben von Noten sind in Beats angegeben#RELATIVE: No
: Zeitangaben sind hier absolut (relative Zeitangaben bedeuten, dass immer die Anzahl der Beats seit der letzten Note als Zeit angegeben wird, ggf. müssen die Zeitangaben in dem Fall erst von Ihnen umgerechnet werden)#GAP:7190,00
: Anzahl an Millisekunden, die vor der ersten Textzeile gewartet werden soll (es sollen also am Anfang 7190
Millisekunden des Songs laufen, bevor die Zeit für die Lyrics gezählt wird).#START:0
: Anzahl an Sekunden, die von der Audiodatei am Anfang übersprungen werden sollen. Um es sich einfacher zu machen, können Sie die Audiodatei immer von Anfang an wiedergeben, und die bei START
angebene Zeit wie die bei GAP
angebenene behandelnNoteType StartBeat Length Pitch Text
(Bsp.: : 9 3 5 know
): Eine Note von gegebenem Typ NoteType
, die bei StartBeat
beginnt, Length
Beats dauert, die Tonhöhe Pitch
hat und zu der der Text Text
gehört. NoteType
kann eines der folgenden Symbole sein: :
, *
, F
, R
, G
(in Ihrem Programm können Sie alle Typen gleich behandeln). Length
und Pitch
können Sie in Ihrem Programm ignorieren.- StartBeat
(Bsp.: - 45
): Ende einer Phrase zur Zeit StartBeat
Eine Audiodatei kann mit der Bibliothek python-vlc
wie folgt wiedergegeben werden (die Wiedergabe läuft ab diesem Zeitpunkt unabhängig von ihrem Code im Hintergrund):
import vlc
audiofile = './OneRepublic - I Ain’t Worried (From “Top Gun: Maverick”) [Official Music Video] [mNEUkkoUoIA].opus' # Pfad zu der Datei im Dateisystem
p = vlc.MediaPlayer(audiofile)
p.play()
Schreiben Sie ein Programm, welches eine Lyrics-Datei einliest und verarbeitet.
Ihr Programm soll dann die Musik starten und nacheinander Stücke des Liedtexts auf die Konsole drucken.
Zwischen zwei Textstücken müssen Sie dabei jeweils die korrekte Zeit abwarten (z. B. wartet time.sleep(wartezeit)
den Wert von wartezeit
in Sekunden).
Später(!) können Sie ihr Programm erweitern, Songtextzeilen immer komplett darzustellen, und dabei den bereits gehörten Teil rot auszudrucken. Das Programm kann dann beispielsweise so aussehen:
Wenn Sie die Lyrics-Datei wie folgt einlesen, erhalten Sie eine Liste von Strings, wobei jeder String eine Zeile aus dem Lyricsfile ist. Sie können dann anschließend jede Zeile einzeln parsen.
lyricsfile = './OneRepublic - IAintWorried-2609.txt'
lyrics = []
with open(lyricsfile, encoding="utf-8") as f:
lyrics = f.readlines()
Es bietet sich an, jetzt (neben einigen allgemeinen Angaben wie GAP
und BPM
) beim Einlesen der einzelnen Zeilen eine Liste an Events zu generieren. Diese Liste kann z. B. so aufgebaut sein, dass sie immer 2-Tupel (Beat,Text)
enthält.
Hilfreich sind folgende Funktionen:
text.split(' ')
teilt den Text text
an jedem Leerzeichen auf, und gibt eine Liste aller Teilstücke zurück' '.join(textstueckliste)
hängt alle Textstücke in der Liste textstueckliste
aneinander, und trennt sie jeweils durch ein LeerzeichenUm nicht am Ende jedes prints automatisch eine neue Zeile zu erhalten, und damit print
Befehle immer sofort auf die Konsole drucken (und Ihr Betriebssystem nicht ggf. versucht, mehrere print-Befehle vor dem Drucken zusammen zu fassen), können Sie folgende Parameter an print
übergeben:
print(text, end="", flush=True)