Deutsches Lilypond Forum (Archiv)

Allgemein => Fragen zu Funktionen => Thema gestartet von: rJazz am Freitag, 25. September 2015, 18:50

Titel: python
Beitrag von: rJazz am Freitag, 25. September 2015, 18:50
Kann ich (in Frescobaldi) irgendwie python-Code in eine Lilypond-Datei einfügen,
so dass dieser gesparst (und das Ergebnis in die lilypond-Datei eingefügt) wird, bevor lilypond
den Notensatz beginnt?
Titel: Re: python
Beitrag von: fugenkomponist am Freitag, 25. September 2015, 23:12
Ja, unter Snippets→Manage Snippets (oder Strg+T). Man kann LilyPond beibringen, jede neue Datei gleich mit so einem Snippet zu befüllen (Unter Bearbeiten→Einstellungen→Allgemeine Einstellungen→Beim Erstellen neuer Dokumente→Erzeuge Dokument nach einer Vorlage). So lasse ich z. B. immer gleich die aktuelle LilyPond-Version und \language "deutsch" einfügen. Aber auch bestehende Dateien lassen sich mit solchen Snippets ergänzen und bearbeiten.

Wobei … jetzt hab ich gerade nochmal genauer gelesen und bin gestolpert über
Zitat
bevor lilypond den Notensatz beginnt
Meinst du, dass der Code vor jedem Kompilieren ausgeführt werden soll? Dann wäre die Frage, ob das nicht eine Aufgabe für Scheme in LilyPond wäre. Was genau schwebt dir denn vor?
Titel: Re: python
Beitrag von: rJazz am Samstag, 26. September 2015, 00:02
Ja, vor dem Compilieren.

z.B.
{
 \myPythonFunction  {hier kommt der  python-code rein der Noten generiert und zurückgibt}
}


würde zu

{
  c e g b
}
Titel: Re: python
Beitrag von: fugenkomponist am Samstag, 26. September 2015, 10:45
Hm. In LilyPond direkt geht das nicht; es wird halt nicht mit python, sondern mit scheme erweitert. Und soweit ich weiß, sind die Frescobaldi-python-snippets zum einmaligen Ausführen gedacht (und werden eben auch nur einmal ausgeführt).

Dir bleiben also zwei Optionen:

1. Option
Lern Scheme und schreibe eine Musikfunktion. Sieht dann ungefähr so aus:
mySchemeFunction =
#(define-music-function (arg1 arg2 arg3) (typ1? typ2? typ3?)
    […Scheme-Magie…])

{
  \mySchemeFunction
}

% oder, je nach dem, was mySchemeFunction zurückgibt auch

\mySchemeFunction
Statt einer expliziten music-function und deren Aufruf kannst du auch direkt einen scheme-Ausdruck hinschreiben wie z. B.#(ly:music-transpose (ly:make-pitch 0 2 0) #{ c d e #})Dieser hier sollte z. B. den gleichen Effekt haben, als stünde da einfach{ e fis gis }(bin mir gerade nicht sicher, ob ly:music-transpose genau so heißt).

Dein Anwendungsfall (vor jedem Kompilieren ausführen) klingt so, als würdest du auf Zeit, Zufall oder andere Dateien zugreifen. All dies ist mit Scheme auch möglich.

2. Option
Schreib ein kleines Programm, welches eine Datei test.pyly einliest, die LilyPond-Code und von dir speziell gekennzeichnete Pythoncodeabschnitte enthält und eine test.ly ausgibt und danach LilyPond damit aufruft. Alles außerhalb der python-Abschnitte wird direkt so wieder ausgegeben, der Inhalt der Abschnitte wird interpretiert (gibt ja bestimmt sowas wie eval in anderen Sprachen auch in python) und dessen Ausgabe ausgegeben. Die Dateien könnten so aussehen:

test.pyly
{
  %% BEGIN PYTHON %%
  Hier deine Python-Magie
  %% END PYTHON %%
}

test.ly
{
  %% BEGIN PYTHON-OUTPUT %%
  c e g b
  %% END PYTHON-OUTPUT %%
}

Wenn du noch ein bisschen konkreter wirst, was du vorhast, kann dir hier sicher jemand (z. B. ich) sagen, wie einfach das in Scheme umzusetzen ist. Schaden kann es jedenfalls nicht, ein bisschen scheme zu lernen ;) Aber auch bei der zweiten Option könnte ich dir evtl. ein Stück weit helfen, auch wenn ich kein python kann.
Titel: Re: python
Beitrag von: harm6 am Samstag, 26. September 2015, 12:36
Da ich auch keine python-Kenntnisse habe kann ich hier nicht viel sagen.
Ich bezweifel jedoch, daß python-code in einem .ly-file ausgeführt werden kann bevor der LilyPond-parser zuschlägt.

Wie fugenkomponist schon sagte kann man aber eventuell scheme/guile nutzen.
Insofern wäre es schön Du würdest konkret posten was Dir vorschwebt.

So etwas wie:
Diese Eingabe
<what-ever-code>
soll umgewandelt werden in
<other-what-ever-code>

Darüberhinaus gibt es allerdings auch die Möglichkeit make zu benutzen, siehe:

http://www.lilypond.org/doc/v2.18/Documentation/usage/make-and-makefiles (http://www.lilypond.org/doc/v2.18/Documentation/usage/make-and-makefiles)


Gruß,
  Harm
Titel: Re: python
Beitrag von: rJazz am Samstag, 26. September 2015, 17:39
Ich werde auf jeden Fall scheme lernen, das braucht aber etwas
Zeit, weil ich keine große Erfahrung mit lisp-artigem Programmieren habe.
Aber ich habe verstanden, dass das mit lilypond am elegantesten ist.

Python brauche ich dennoch ab und zu, weil ich schon mit music21
(http://web.mit.edu/music21/doc/about/what.html)
gearbeitet habe und ein paar Skripte verwenden will mit lilypond.

Es geht natürlich auch andersherum:
ich kann mit python und music21 Noten generieren, die dann
als MIDI oder sogar lilypond-Format abspeichern.

Nur das direkte Einbetten in frescobaldi  wäre für mich die einfachste Option gewesen,
da kann ich inzwischen ja sogar direkt MIDI-Output mehrkanalig abhören.

Eine andere Idee/Frage ist noch: kann ich mit scheme Systembefehle
ausführen? Also:

-> scheme aus der frescobaldi-lilypond-Datei ruft python auf
-> python schreibt eine Textdatei
-> scheme liest die Textdatei ein?




Titel: Re: python
Beitrag von: fugenkomponist am Samstag, 26. September 2015, 19:03
Eine andere Idee/Frage ist noch: kann ich mit scheme Systembefehle
ausführen? Also:

-> scheme aus der frescobaldi-lilypond-Datei ruft python auf
-> python schreibt eine Textdatei
-> scheme liest die Textdatei ein?
Ja, das geht. In Scheme/Guile gibts die Funktion system, die hier (https://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/Processes.html) (kurz) dokumentiert ist. Und Textdateien einlesen sollte ebenfalls kein Problem sein, ich habe aber gerade keine Zeit, ins Detail zu gehen (weil ich mir die Dinge selbst erst zusammensuchen müsste).

Edit: allerdings erhältst du dann natürlich die LilyPond-Darstellung der Noten als Text wie „c4 d“, ich bin mir gerade nicht sicher, ob es möglich ist, diese dann an den LilyPond-Parser weiterzufüttern …

2. Edit: richtige Version der Guile-Dokumentation verlinkt (danke an harm)
Titel: Re: python
Beitrag von: rJazz am Samstag, 26. September 2015, 23:00
Ich habe erstmal ganz einfach  versucht:


pyMx =
#(define-music-function (parser location )
   ()

    system "python pythonTest.py"
   
  )

myMusic  =  {
  \pyMx
}


Das gibt aber auch schon einen Fehler:

/home/std/Dokumente/python/lilypond/pythonTest.ly:61:3: error: music function cannot return #"python pythonTest.py"
 
  \pyMx

Titel: Re: python
Beitrag von: harm6 am Sonntag, 27. September 2015, 01:46
Ich fürchte Du hast in python gedacht, nicht in scheme ;)

In scheme ist alles eine Liste (ist nicht die ganze Geschichte, aber für jetzt eine gute Annäherung); der erste Eintrag ist ein Operator.

Rein praktisch bedeutet das (u.a.): setze jeden Ausdruck in Klammern.

Ich habe mal folgenden Code in hello-world.py gespeichert.
print "Hello World!"

Und so in einem LilyPond-file verarbeitet:

pyMx =
#(define-music-function (parser location)()
    (system "python hello-world.py")
    ;; or:
    ;(system* "python" "hello-world.py")
   #{ cis'1 #}
  )

\pyMx

Funktioniert. (In LilyPond muß eine music-function Musik ausgeben, deshalb lasse ich #{ cis'1 #} ausgeben.)

Insoweit sollte
pyMx =
#(define-music-function (parser location)()
    (system "python pythonTest.py")
    ;; maybe need to add:
    ;#{ cis'1 #}
)

\pyMx
eigentlich klappen.

Wie weit man damit kommt ahne ich nicht. Wie gesagt, ich kann kein python.

HTH,
  Harm

P.S.
@fugenkomponist
Du hast zum manual für guile 2.0.11 verlinkt, LilyPond braucht aber 1.8, mit 2.x gibts ja nach wie vor Probleme.
(Aber das weißt Du ja eigentlich ;) )
Insoweit:

https://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/Processes.html#Processes
 (https://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/Processes.html#Processes)
Titel: Re: python
Beitrag von: fugenkomponist am Sonntag, 27. September 2015, 10:09
Funktioniert. (In LilyPond muß eine music-function Musik ausgeben, deshalb lasse ich #{ cis'1 #} ausgeben.)
Kurz off-topic: Es gibt auch andere Funktionen, z. B. void-functions u. ä. Die heißen so einfach nach dem Rückgabetyp (irgendwo sind die Funktionen (? Makros? …?) define-xxxxx-function inkl. define-syntax-function (für beliebige anzugebende Rückgabetypenprädikate) definiert). Aber in diesem Fall wird tatsächlich eine music-function gebraucht.
Zitat
Wie weit man damit kommt ahne ich nicht. Wie gesagt, ich kann kein python.
Vermutlich wird es tatsächlich schwierig, dem LilyPond-Parser die Noten zu füttern. Eine Idee: Gibts ein Scheme-Äquivalent für \include? (hab gerade nicht meinen eigenen Rechner mit dem lilypond-git hier) Dann könnte man nämlich python eine .ly- oder .ily-Datei schreiben lassen und diese einfach per include einbinden. (In diesem Fall evtl. doch eine void-function.)
Zitat
Du hast zum manual für guile 2.0.11 verlinkt, LilyPond braucht aber 1.8, mit 2.x gibts ja nach wie vor Probleme.
(Aber das weißt Du ja eigentlich ;) )
Ja, weiß ich eigentlich. Hab aber da gestern abend nicht dran gedacht. Und ich muß zugeben, daß ich oft nicht so systematisch in der Doku suche, sondern per Google und das spuckt nunmal die aktuelle Version als ersten Treffer aus ;)
Titel: Re: python
Beitrag von: rJazz am Dienstag, 6. Oktober 2015, 20:20
O.k., das ist ja noch ein weiter Weg, wenn ich
damit auch noch Parameter übergeben will und
die Ausgabe von Python strukturiert wieder
zurückführen wollte.

Wahrscheinlich ist des am ende besser, die inhaltslogik
von Python erst in Python zu schreiben und
dann Noten im lilypond-Format in eine Textdatei zu schreiben und
dann weiter zu bearbeiten, oder?
Titel: Re: python
Beitrag von: fugenkomponist am Dienstag, 6. Oktober 2015, 20:59
Ich frag mich, warum ich auf den folgenden Vorschlag nicht schon viel früher gekommen bin: Nutze make, das könnte evtl. genau das richtige für dich sein (je nach dem, was für Parameter du wann und wie übergeben willst). Falls du das nicht kennst, hier eine ganz kurze Einführung. Und falls du es kennst, hilfts ja vielleicht wemanders ;)

make ist ein Programm, welches immer wiederkehrende Aufgaben, die Dateien abhängig von anderen Dateien verändern (allen voran Kompilieren & Co.), vereinfacht. Man schreibt eine Datei namens Makefile (mit genau diesem Namen), die Anweisungen hierfür enthält. In deinem Fall könnte das Makefile z. B. folgendermaßen aussehen (dabei soll test.py deinen python-Code enthalten, test.py.out die Ausgabe und test.ly den LilyPond-Code samt \include "test.py.out"):
# das hier ist ein Kommentar

# Das Ziel test.pdf wird durch den angegebenen lilypond-Befehl neu erzeugt, wenn es älter ist als test.ly oder test.py.out (die sich also geändert haben).
# $< steht für die erste Abhängigkeit, man könnte auch schreiben „lilypond test.ly“
test.pdf: test.ly test.py.out
  lilypond $<

# Das Ziel test.py.out wird neu erzeugt, wenn test.py sich geändert hat.
# $@ steht für das Ziel selbst, man könnte auch schreiben „python test.py > test.py.out“
test.py.out: test.py
  python $< > $@

Wenn man nun „make test.pdf“ aufruft, wird test.pdf neu erzeugt, falls sich an den Abhängigkeiten (test.ly, test.py.out) etwas geändert hat. Da es das erste Ziel im Makefile ist, kann man auch make einfach ohne Argument aufrufen. Das schöne an der Sache: make schaut erstmal, ob die Abhängigkeiten nicht selbst irgendwelche Abhängigkeiten haben. Wenn du also make aufrufst und es hat sich nur test.py geändert, dann wird zunächst test.py.out aktualisiert und dann erst lilypond aufgerufen.

Makefiles können noch nen ganzen Haufen mehr schöne Dinge (z. B. alle Dateien eines bestimmten Typs gleich behandeln, sodass man nicht für jede Datei eine eigene Regel braucht). Der Umgang damit ist vermutlich schneller zu lernen als Scheme/Guile (übrigens nutzt die GNU-Implementierung von make genau wie LilyPond Guile als Erweiterungssprache).

Edit: Falls die Parameter, mit denen dein python-Code aufgerufen werden soll, von der LilyPond-Datei abhängen, kannst du
Titel: Re: python
Beitrag von: harm6 am Dienstag, 6. Oktober 2015, 22:51
Zitat von: fugenkomponist
Ich frag mich, warum ich auf den folgenden Vorschlag nicht schon viel früher gekommen bin: Nutze make [...]


[...]
Darüberhinaus gibt es allerdings auch die Möglichkeit make zu benutzen, siehe:
http://www.lilypond.org/doc/v2.18/Documentation/usage/make-and-makefiles (http://www.lilypond.org/doc/v2.18/Documentation/usage/make-and-makefiles)
[...]

;)
Titel: Re: python
Beitrag von: fugenkomponist am Mittwoch, 7. Oktober 2015, 06:45
Ich bin mir sicher, dass ich deine Antwort „damals“ gelesen habe. Aber manchmal bleibt dann offensichtlich doch nicht alles hängen, sorry … Und dann kommen einem gute, vermeintlich neue Ideen ;)
Titel: Re: python
Beitrag von: harm6 am Mittwoch, 7. Oktober 2015, 14:32
Zitat von: fugenkomponist
Ich bin mir sicher, dass ich deine Antwort „damals“ gelesen habe. Aber manchmal bleibt dann offensichtlich doch nicht alles hängen, sorry … Und dann kommen einem gute, vermeintlich neue Ideen

Dein post liefert wertvolle Zusatzinformationen.
Also kein Grund sich zu entschuldigen, aber die Bemerkung konnte ich mir dann doch nicht verkneifen. ;)

Anders sieht es aus, falls jemand meine posts nicht liest bzw sich nicht bemüht zu verstehen. Und weiterfragt, obwohl die Lösung schon gepostet ist ...
Das kommt besonders auf der engl. Liste schon mal vor, dann werd' ich doch ungehalten.

Gruß,
  Harm
Titel: Re: python
Beitrag von: barrio am Freitag, 25. März 2016, 14:22
Etwas spät, aber wenn rJazz oder andere es noch interessiert. Man kann in Python Lilypond ansteuern mit dem Modul abjad http://abjad.mbrsi.org/for_beginners/abjad_hello_world_in_a_file.html (http://abjad.mbrsi.org/for_beginners/abjad_hello_world_in_a_file.html). Allerdings schrteibt man den Code dann natürlich in ein Python *.py file. Mit
systemtools.open_last_ly()kann man sich das ly file anzeigen, welches abjad intern erstellt. :-)

Viel SPaß damit
barrio
Titel: Re: python
Beitrag von: xr am Dienstag, 5. April 2016, 06:45
Hi,

ich habe ebenfalls nach einer Möglichkeit gesucht, mit Python Code für Lilypond zu erzeugen, und bin daher auf diesen alten Thread gestoßen.

1)

Nach einigem Probieren funktioniert bei mir der folgende Code, der in "test.py" eine Datei "test.ly" erzeugt, die von Lilypond wieder eingelesen wird. (Den Code führe ich in Frescobaldi aus.)
Lilyponds Parser wartet dabei auf die Ausführung des Pythoncodes, was man auch sieht, wenn man dem Systemaufruf kein Argument mitgibt. Also: "(system "C:\\Python34\\python.exe")
Dann nämlich öffnet sich die Pythonkonsole und erst wenn die wieder geschlossen wird, führt Lilypond weiteren Code aus.

\version "2.18.2"


pyMx =
#(define-void-function (parser location)()
   (system "C:\\Python34\\python.exe C:\\Users\\Homer\\Desktop\\Lilypond\\Droste\\test.py")
  )

\pyMx


\include "test.ly"

Mit "C:\\Python34\\python.exe" kann ich auch meine eigene Pythoninstallation ansprechen. Gebe ich nur "python" an, wird die Pythoninstallation von Lilypond oder Frescobaldi geöffnet. (Version 2.4.5 - ziemlich veraltet)

2)

Abjad habe ich ebenfalls ausprobiert, es läuft bei mir allerdings nicht. (Lilypond 2.18.2, Python 2.7.11 oder 3.4 oder 3.5, Abjad 2.16, win7 und win10)
Abjad läuft, nachdem ich es gerade nochmal probiert habe, nun plötzlich doch. Leider weiß ich nicht mehr, was ich alles geändert habe.

Der Befehl "systemtools.open_last_ly()" muss bei mir allerdings lauten: "systemtools.IOManager.open_last_ly()" und erzeugt weiterhin einen Fehler:
"Der Befehl "edit" ist entweder falsch geschrieben oder
konnte nicht gefunden werden."

Grüße,
Xaver





Titel: Re: python
Beitrag von: harm6 am Mittwoch, 6. April 2016, 02:03
Zitat
Mit "C:\\Python34\\python.exe" kann ich auch meine eigene Pythoninstallation ansprechen.

Unwahrscheinlich, daß das auf meinem Linux-system funktioniert :D

Zitat
Gebe ich nur "python" an, wird die Pythoninstallation von Lilypond oder Frescobaldi geöffnet. (Version 2.4.5 - ziemlich veraltet)

Allerdings funktioniert midi2ly nicht mit neueren python-versionen, siehe diesen aktuellen thread:
http://lilypond.1069038.n5.nabble.com/midi2ly-dll-error-td189201.html (http://lilypond.1069038.n5.nabble.com/midi2ly-dll-error-td189201.html)

Gruß,
  Harm
Titel: Re: python
Beitrag von: xr am Mittwoch, 6. April 2016, 13:21
Zitat
Unwahrscheinlich, daß das auf meinem Linux-system funktioniert
Ich nutze Linux nur gelegentlich, daher bin ich da nicht sicher, aber ich dachte, man spricht ein bestimmtes Python direkt an, wenn man nicht das für das System voreingestellte nutzen will.  Etwa: "python3" statt "python"

Zitat
Allerdings funktioniert midi2ly nicht mit neueren python-versionen
Frescobaldi bzw. Lilypond nutzen ja weiterhin python 2.4. Nur mein Code wird mit meiner Python Version ausgeführt.
Titel: Re: python
Beitrag von: harm6 am Donnerstag, 7. April 2016, 01:26
Zitat von: xr
Ich nutze Linux nur gelegentlich, daher bin ich da nicht sicher, aber ich dachte, man spricht ein bestimmtes Python direkt an, wenn man nicht das für das System voreingestellte nutzen will.  Etwa: "python3" statt "python"

Yep, es sollte eigentlich ein kleiner Scherz sein, denn Dein windows-Pfad wird bei mir nicht funktionieren.
Vielleicht wäre ein Kommentar wie etwa "oder wo auch immer das gewünschte python sich befindet" sinnvoll.

Gruß,
  Harm