for-each: Funktion auf die Liste anwenden, aber alte Liste behalten

Begonnen von Multimax, Samstag, 9. Juni 2018, 17:13

« vorheriges - nächstes »

Multimax

Hallo,


seit drei Stunden versuche ich aus einer music-function die eine for-each-Schleife enthält, eine Variable auszugeben. Es soll hierbei nicht die die Liste geändert werden, sondern lediglich eine Kopie der geänderten Liste erstellt werden, die ich später in einer Variable aufrufen kann.

In dem Beispiel erstellt die funktion namens klammer einen Hairpin um eine Notengruppe. Dieses Ergebnis soll wie gesagt aber nicht auf die Liste angewendet werden, sondern in einem separaten Dynamic-Staff ausgegeben werden.

Leider bekomme ich diese gewünschte Variable da nicht raus. Bei meinen Versuchen bis jetzt war entweder die Variable unbekannt, oder es wurde "Bad define placement" konstatiert :(

Nach meinem Verständnis sollte am ende der Funktion, dort wo mus ausgegeben wird einfach eine Variable definiert werden. Dies ist aber anscheinend nicht so einfach möglich.

Für einen Tip wäre ich sehr dankbar.

\version "2.18.2"



klammer=
#(define-music-function (parser location mus)(ly:music?)
   (let ((elts (extract-typed-music mus '(rhythmic-event event-chord))))
    (for-each
      (lambda (sel)
        (let ((m (sel elts)))
          (if (music-is-of-type? m 'event-chord)
              (set! (ly:music-property m 'elements)
                    (cons (make-music 'CrescendoEvent
                                      'span-direction (sel '(-1 1)))
                          (ly:music-property m 'elements)))
              (set! (ly:music-property m 'articulations)
                    (cons (make-music 'CrescendoEvent
                                      'span-direction (sel '(-1 1)))
                          (ly:music-property m 'articulations))))))
      (list last first)))
   mus ;hier nicht mus ausgeben, sondern in Variable speichern???
   )


 




melodie = {c4 \klammer{d e f} b1}

<<
{\melodie} 

{\new Dynamics {}}

>>

harm6

Hallo,

Du hast ein sehr gut codierte Fuktion, sie macht genau das was ich aus dem Code heraus lese. Offensichtlich ist es aber nicht was Du willst.

Nun ist mir aber nicht klar was Du eigentlich willst.

Kannst Du mal (völlig lösgelöst vom aktuellen Coding) in Worten beschreiben was Dir vorschwebt, am besten begleitet von einem pseudo-Code, wie es angewendet werden soll, sowie welches Ergebnis angestoßen werden soll!?

Gruß,
  Harm

Multimax

Hallo,

Du hast ein sehr gut codierte Fuktion

Die ist ja auch von Dir ;D . Hab ich aus einem älteren Beitrag im Forum oder im Archiv und leicht modifiziert

Was will ich erreichen:
Die Funktion setzt ein Crescendo unter die Notengruppe.  Wenn ich die Höhe noch auf Null setze, erhalte ich einen Strich. Diesen Strich brauche ich für die Umsetzung einer Tabulaturschrift für steirische Hamornika.

Der Strich soll aber in einem Extra Staff (new dynamics) darunter erzeugt werden. Ich möchte ja vielleicht noch "echte" dynamics hinzufügen, will also da noch alle Möglichkeiten offen haben: (dynamicsUp, Hairpins to barline... etc) (siehe Bild im Anhang).

Normalerweise nimmt man ja music-map bzw. for-each, wenn man Musik verändern möchte. Als Eingang geht dort eine Liste von Musik ein, Eine Prozedur wird auf die Liste angewendet und die geänderte Musik wieder ausgegeben und zwar direkt dort, wo die funktion im Notentext aufgerufen wurde.

In meinem Fall möchte ich mir die geänderte Liste aber erstmal nur merken und die ursprüngliche Liste unangetastet lassen. Die geänderte Liste rufe ich in einem parallelen dynamic-Staff nochmal auf und dort wird sie dann ausgegeben.

Pseudocode:


ändereMusik=
#(define-music-function (parser location mus)(ly:music?)

setze ein Crescendo unter die Noten

speichere die geänderte Musik ab in Variable MusikAnders
)
 

melodie = {c4 \ändereMusik{d e f} b1}

<<
{\melodie}  %musik ohne Crescendo

{\new Dynamics {\MusikAnders}} %paralleler dynamic-Staff mit Crescendo

>>


Hoffentlich war das einigermaßen verständlich. Vielen Dank für Deine Mühe,

Viele Grüße
Multimax

harm6

Zitat von: Multimax am Sonntag, 10. Juni 2018, 10:40
Du hast ein sehr gut codierte Fuktion

Die ist ja auch von Dir ;D . Hab ich aus einem älteren Beitrag im Forum oder im Archiv und leicht modifiziert
lol
Aber wenn ich so versuche mich zu erinnern, dann ist dieses coding von David Kastrup, ich hab's bestenfalls adaptiert.

Zitat
Was will ich erreichen:
Die Funktion setzt ein Crescendo unter die Notengruppe.  Wenn ich die Höhe noch auf Null setze, erhalte ich einen Strich. Diesen Strich brauche ich für die Umsetzung einer Tabulaturschrift für steirische Hamornika.
Ich kenn mich mit dem Instrument nicht aus.
Aber wenn es ein Strich sein soll, wäre ein TextSpanner nicht die bessere Wahl?

Zitat
Der Strich soll aber in einem Extra Staff (new dynamics) darunter erzeugt werden. Ich möchte ja vielleicht noch "echte" dynamics hinzufügen, will also da noch alle Möglichkeiten offen haben: (dynamicsUp, Hairpins to barline... etc) (siehe Bild im Anhang).
Verstanden

Zitat
Normalerweise nimmt man ja music-map bzw. for-each, wenn man Musik verändern möchte. Als Eingang geht dort eine Liste von Musik ein, Eine Prozedur wird auf die Liste angewendet und die geänderte Musik wieder ausgegeben und zwar direkt dort, wo die funktion im Notentext aufgerufen wurde.
`for-each' ist eine von guiles proceduren um eine procedure auf jedes Elenemt einer Liste anzuwenden. Andere Beispiel wären map, filter, remove und andere.
`music-map' hingegen ist eine LilyPond-eigene procedure, um über eine Musik beinhaltende Liste zu iterieren.

Zitat
In meinem Fall möchte ich mir die geänderte Liste aber erstmal nur merken und die ursprüngliche Liste unangetastet lassen. Die geänderte Liste rufe ich in einem parallelen dynamic-Staff nochmal auf und dort wird sie dann ausgegeben.

Pseudocode:


ändereMusik=
#(define-music-function (parser location mus)(ly:music?)

setze ein Crescendo unter die Noten

speichere die geänderte Musik ab in Variable MusikAnders
)
 

melodie = {c4 \ändereMusik{d e f} b1}

<<
{\melodie}  %musik ohne Crescendo

{\new Dynamics {\MusikAnders}} %paralleler dynamic-Staff mit Crescendo

>>



Also möchtest Du eigentlich eine Variable on-the-fly definieren.
Eine Musikfunktion muß aber Musik ausgeben. Diese Musik soll erstmal unverändert sein (hier wäre ly:music-deep-copy hilfreich um mit einer Kopie zuarbeiten, ohne das Original zu verändern).
Jegliche simple Definition in einer Musikfunktion ist nur lokal, also im "Wirkungsbereich" dieser Funktion abrufbar.
Um hier weiter zu kommen könnte man ly:parser-define! verwenden.
Arnold hat das mal gemacht:
https://lilypondforum.de/index.php/topic,195.msg1315.html#msg1315

Auf Dein Beispiel angewendet führt das zu:

\version "2.18.2"

klammer=
#(define-music-function (parser location sym mus)(symbol? ly:music?)
   (let* ((my-mus (ly:music-deep-copy mus))
          (elts (extract-typed-music my-mus '(rhythmic-event event-chord))))
    (for-each
      (lambda (sel)
        (let ((m (sel elts)))
          (if (music-is-of-type? m 'event-chord)
              (set! (ly:music-property m 'elements)
                    (cons (make-music 'CrescendoEvent
                                      'span-direction (sel '(-1 1)))
                          (ly:music-property m 'elements)))
              (set! (ly:music-property m 'articulations)
                    (cons (make-music 'CrescendoEvent
                                      'span-direction (sel '(-1 1)))
                          (ly:music-property m 'articulations))))))
      (list last first))
    (ly:parser-define! sym my-mus))
   mus)

melodie = { c4 \klammer #'foo { d e f } b1 }

<<
  \melodie
 
  \new Dynamics { s4 \foo }
 
  \new Staff { r4 \foo }
>>


Allerdings behagt mir nicht, daß zusätzlich zum CrescendoEvent auch alles andere mitgenommen wird. In einem Dynamics-context überflüssig.

Lass mich mal drüber nachdenken.


Gruß,
  Harm

Multimax

Hi,

vielen Dank für die schnelle Antwort.

ZitatIch kenn mich mit dem Instrument nicht aus.
Es ist ein diatonisches instrument, das beim Drücken und Ziehen des Balges andere Töne erzeugt. Analog: Mundharmonika. Der Strich symbolisiert den Balgdruck.

ZitatAber wenn es ein Strich sein soll, wäre ein TextSpanner nicht die bessere Wahl?
Das könnte gut sein. Denn ich bräuchte den Strich auch für Abschnitte, die aus einer einzelnen Note bestehen. Dort ist ja kein Crescendo möglich. Dafür wollte ich im Nachgang noch eine Adaption vornehmen, die in diesen Fällen eine parallele Stimme z.B. {s4 s4} generiert, über deren erstes und letztes Element dann das Crescendo realisiert wird.

ZitatAllerdings behagt mir nicht, daß zusätzlich zum CrescendoEvent auch alles andere mitgenommen wird. In einem Dynamics-context überflüssig.
Das gefällt mir ebensowenig. Deshalb hätte ich, nach der Lösung des Grundproblems mit der Variablen noch eine Erweiterung versucht hinzuzufügen, die zusätzlich alles in Pausen umwandelt.

Den Code von Dir kann ich leider nicht ausführen: "Wrong Number of Arguments..." bei ly:parser-define.
Ich werde jetzt erstmal versuchen, den Fehler zu finden und das Beispiel zu verstehen.
Oder sollte das etwa ein Versionsproblem sein?

harm6

ZitatDen Code von Dir kann ich leider nicht ausführen: "Wrong Number of Arguments..." bei ly:parser-define.
Ich werde jetzt erstmal versuchen, den Fehler zu finden und das Beispiel zu verstehen.
Oder sollte das etwa ein Versionsproblem sein?

Ja.
In 2.18.2 muß es
(ly:parser-define! parser sym my-mus)
heißen.

Sorry,
  Harm

harm6

Zitatich bräuchte den Strich auch für Abschnitte, die aus einer einzelnen Note bestehen. Dort ist ja kein Crescendo möglich. Dafür wollte ich im Nachgang noch eine Adaption vornehmen, die in diesen Fällen eine parallele Stimme z.B. {s4 s4} generiert, über deren erstes und letztes Element dann das Crescendo realisiert wird.

Würde auch eine programmatische Adaption von
{ s1\> <>\! s1 }
gehen?

Zitat
Deshalb hätte ich, nach der Lösung des Grundproblems mit der Variablen noch eine Erweiterung versucht hinzuzufügen, die zusätzlich alles in Pausen umwandelt.

Hier eine Version die alles einzeln in SkipEvents umwandelt.


\version "2.18.2"

#(define (make-skip-event dur)
  (make-music 'SkipEvent
              'duration dur))

klammer=
#(define-music-function (parser location sym mus)(symbol? ly:music?)

  (define (mus->skip m)
    (cond ((eq? (ly:music-property m 'name)
                'EventChord)
           (make-skip-event (duration-of-note m)))
          ((memq (ly:music-property m 'name)
                 '(NoteEvent RestEvent MultiMeasureRestMusic))
           (make-skip-event (ly:music-property m 'duration)))
          ;; else is #f otherwise map-some-music would stop.
          (else #f)))
       
  (let* ((my-mus
           (map-some-music mus->skip (ly:music-deep-copy mus)))
         (elts (ly:music-property my-mus 'elements)))
         
    (for-each
      (lambda (sel)
        (let ((m (sel elts)))
          (set! (ly:music-property m 'articulations)
                (cons (make-music 'CrescendoEvent
                                  'span-direction (sel '(-1 1)))
                      (ly:music-property m 'articulations)))))
      (list last first))
 
    (ly:parser-define! parser sym my-mus))

   mus)

melodie = { c4 \klammer #'foo { d r2 e4 f <g b>2 \break a1 R1 <> } b1 }

<<
  \melodie

  \new Dynamics { s4 \foo }
 
  \new Staff { r4 \foo }
>>


Anstatt alles einzeln umzuwandeln könnte man mal prüfen, ob es nicht hinreichend wäre ein einzelnes SkipEvent mit der Dauer des music-arguments auszugeben, und das <>\! am Schluß hinzuzufügen.

Gruß,
  Harm






Multimax

Super, vielen Dank auch für die Pausen-Funktion.

Ist zwar optisch genauso, aber der Dynamics-Staff ist dann nicht mit unnötigem Zeugs gefüllt.

Würde auch eine programmatische Adaption von
{ s1\> <>\! s1 }
gehen?


Ich bin gerade überrascht. Ich schreibe seit gut drei oder vier Jahren noten mit Lilypond. Aber
<>
ist mir bis jetzt noch nicht begegnet. Was ist das für ein Befehl?

Multimax


Multimax

#9
Ich sehe gerade: so ganz funktioniert es noch nicht. Beim mehrmaligen Aufrufen müsste ich jetzt im Dynamics-Staff entsprechende Pausen einfügen.
Zitat\version "2.18.2"

#(define (make-skip-event dur)
  (make-music 'SkipEvent
              'duration dur))

klammer=
#(define-music-function (parser location sym mus)(symbol? ly:music?)

  (define (mus->skip m)
    (cond ((eq? (ly:music-property m 'name)
                'EventChord)
           (make-skip-event (duration-of-note m)))
          ((memq (ly:music-property m 'name)
                 '(NoteEvent RestEvent MultiMeasureRestMusic))
           (make-skip-event (ly:music-property m 'duration)))
          ;; else is #f otherwise map-some-music would stop.
          (else #f)))
       
  (let* ((my-mus
           (map-some-music mus->skip (ly:music-deep-copy mus)))
         (elts (ly:music-property my-mus 'elements)))
         
    (for-each
      (lambda (sel)
        (let ((m (sel elts)))
          (set! (ly:music-property m 'articulations)
                (cons (make-music 'CrescendoEvent
                                  'span-direction (sel '(-1 1)))
                      (ly:music-property m 'articulations)))))
      (list last first))

    (ly:parser-define! parser sym my-mus))

   mus)

melodie = {\klammer #'foo { c4 d } c d e f g c d e f g \klammer #'foo {c4 e} c' c' }

<<
  \melodie

  \new Dynamics { \foo \foo }

  \new Staff { \foo \foo}
>>

Das sollte aber kein Problem sein. Für die Abschnitte wo kein Strich gezeichnet wird (da arbeitet man mit dem Balg in Zugrichtung) wird sowieso eine Funktion aufgerufen. Diese schreibt dann einfach Pausen ohne Dynamics.

EDIT:
Leider ist doch noch der Wurm drin: Das Problem ist bei foo: Wenn ich klammer mehrmals aufrufe, kann ich mit foo im Dynamics-Staff immer nur den jeweils letzten erzeugten Strich abrufen. Vielleicht hätte ich das anfangs explit erwähnen sollen, dass sich Passagen mit und ohne Strich abwechseln.

harm6

Zitat
Leider ist doch noch der Wurm drin: Das Problem ist bei foo: Wenn ich klammer mehrmals aufrufe, kann ich mit foo im Dynamics-Staff immer nur den jeweils letzten erzeugten Strich abrufen.

Deshalb das `sym'-Argument.
melodie = {\klammer #'foo { c4 d } c d e f g c d e f g \klammer #'buzz {c4 e} c' c' }

<<
  \melodie

  \new Dynamics { \foo \buzz }

  \new Staff { \foo \buzz}
>>


Gruß,
  Harm



Multimax

#11
Ja, das wäre natürlich so möglich, jedes Mal neue Argumente mitzugeben.

Andererseits wäre das unpraktikabel, denn ich arbeite mit meinem Griffschriftalgorithmus immer wechselnd mit den Befehlen "\druck" und "\zug" (wobei die Unterstrich-Funktion nur eine von vielen ist, die dadurch aufgerufen wird. (hier habe ich das erläutert https://lilypondforum.de/index.php/topic,319.0.html

Und das relativ häufig in einem einem Musikstück (siehe Bild). Da wäre es natürlich wünschenswert, wenn ich dort nicht händisch foos und buzzes etc. mitgeben muss, und dann im Dynamic-Staff ebenfalls nochmal gesondert aufrufen muss.

Ich werde also versuchen:

* entweder eine Alterierung des foo einzubauen (foo1 foo2 foo3...) die automatisch einen numerischen Index mitführt und dann per Funktion im Dynamics-Staff ebenfalls automatisch  foo1 bis foon hintereinander schreibt,

* oder gleich die Pausen, versehen mit den Strichen jedes Mal im Sinne von append in einer Liste aneinanderzuhängen.

Viele Grüße

Multimax

Manuela

Multimax,

du scheinst dich ja sehr gut mit der Steirischen Harmonika auszukennen.

Ich habe (auf Anregung) eines Bekannten eine Möglichkeit einer Griffschriftnotation entwickelt, die allerdings keine Automatismen enthält wie sie dir vorschweben.

Vielleicht kannst du ja trotzdem irgendetwas davon brauchen: Idee für eine Griffschriftnotation für die Steirische Harmonika
Danke für eure Hilfe
viele Grüße
-- Manuela

Manuela

Danke für eure Hilfe
viele Grüße
-- Manuela

Multimax

Hi Manuela,

ich bin zwar Anfänger auf dem Instrument, aber so langsam krieg ich auch was zustande.

Im Musiker-Board habe ich für jemanden eine Griffschrift geschrieben und habe das testweise mit einem Capellascript und Capella-Testversion gemacht. Das war so ermüdend, dass ich dann das ganze Web nach Lilypond-Umsetzungen durchsucht habe.
Deinen Link kenne ich natürlich, und von Dir hab ich auch die Sache mit den Hairpins für die Drucklinie  ;)

Jedenfalls wollte ich mal sehen, ob man das alles nicht automatisieren kann. Bei den Tönen klappt ja schon alles. Jetzt fehlt eigentlich nur noch, die Linieneingabe etwas komfortabler zu gestalten. Für den Rest, also Bass-Seite, wollte ich dann aus Deinem und anderen Links ideen holen.