Autor Thema: python  (Gelesen 3713 mal)

rJazz

  • Gast
python
« 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?

fugenkomponist

  • Gast
Re: python
« Antwort #1 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?
« Letzte Änderung: Freitag, 25. September 2015, 23:32 von fugenkomponist »

rJazz

  • Gast
Re: python
« Antwort #2 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
}

fugenkomponist

  • Gast
Re: python
« Antwort #3 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.
« Letzte Änderung: Samstag, 26. September 2015, 10:46 von fugenkomponist »

harm6

  • Gast
Re: python
« Antwort #4 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


Gruß,
  Harm

rJazz

  • Gast
Re: python
« Antwort #5 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?





fugenkomponist

  • Gast
Re: python
« Antwort #6 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 (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)
« Letzte Änderung: Sonntag, 27. September 2015, 10:01 von fugenkomponist »

rJazz

  • Gast
Re: python
« Antwort #7 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


harm6

  • Gast
Re: python
« Antwort #8 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
« Letzte Änderung: Sonntag, 27. September 2015, 01:50 von harm6 »

fugenkomponist

  • Gast
Re: python
« Antwort #9 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 ;)

rJazz

  • Gast
Re: python
« Antwort #10 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?

fugenkomponist

  • Gast
Re: python
« Antwort #11 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
  • test.ly in die Abhängigkeiten von test.py.out aufnehmen und dann
  • python aus der LilyPond-Datei lesen lassen. Z. B. könntest du spezielle Kommentare (meinetwegen beginnend mit „%%%PY“) im Code unterbringen, um die relevanten Stellen zu markieren.
« Letzte Änderung: Dienstag, 6. Oktober 2015, 21:03 von fugenkomponist »

harm6

  • Gast
Re: python
« Antwort #12 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
[...]

;)

fugenkomponist

  • Gast
Re: python
« Antwort #13 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 ;)

harm6

  • Gast
Re: python
« Antwort #14 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