Autor Thema: mit scheme: Noten in Pausen umwandeln?  (Gelesen 2113 mal)

rJazz

  • Member
mit scheme: Noten in Pausen umwandeln?
« am: Dienstag, 6. Oktober 2015, 20:43 »
Hallo!

Ich würde gern eine (meiner ersten)
Scheme-Funktionen entwickeln.

Die Idee ist, Füllpausen zu verwenden, die
genauso lang sind, wie musikalische Motive
und damit unbenutzte Stimmen zu füllen.

(das steht in Verbindung zu dem Thema hier:
https://liarchiv.joonet.de/index.php?topic=2085.0)
ist aber nochmal eine Idee für eine andere Strukturierung.

Es soll dabei eine Funktion \toRest alle Noten
in Pausen umwandeln.

Im Beispiel unten würde das dazu führen,
dass ich die beiden Stimmn abwechselnd das
Moiv spielen lasse, indem ich den Ablauf
kopieren und durch die Funktion \toRest
die nicht spielenden Stimmen einfach
durch Pausen füllen kann.

Den Rahmen für die Funktion habe ich angelegt,
konnte aber noch nicht herausfinden,
wie ich die Noten in einer Schleife durchlaufe
und durch Pausen ersetze.

Ist das mit einem einfachen Befehl machbar?


toRest =

#(define-music-function
     (note)
     (ly:music?)
   #{
      #note
     
   #})


motiv = \relative c'{c4 d e f }

staffA = \new Staff{
  \toRest \motiv 
  \motiv 
}

staffB = \new Staff{
   \toRest \motiv
   \motiv
   
}


\score {
<<
 \staffA
 \staffB

>>

\layout { }
 \midi {   
    \tempo 4 = 115
  }
}

harm6

  • Member
Re: mit scheme: Noten in Pausen umwandeln?
« Antwort #1 am: Dienstag, 6. Oktober 2015, 22:48 »
Ich bin nicht ganz sicher, ob ich Dich richtig verstanden habe, aber vielleicht eine der folgenden Definitionen (toRest funktioniert nur wenn das Motiv ganze Takte lang ist).

\version "2.19.28"

toRest =
#(define-music-function (mus) (ly:music?)
   (mmrest-of-length mus))
   
   
toRestII =
#(define-music-function (mus) (ly:music?)
   (music-map
     (lambda (m)
       (if (music-is-of-type? m 'note-event)
           #{ r $(ly:music-property m 'duration) #}
           m))
     mus))


motiv = \relative c' { c4 d e f }

staffA = \new Staff {
  \toRestII
  %\toRest
  \motiv
  \motiv
}

staffB = \new Staff {
   \motiv
   \toRestII
   %\toRest
   \motiv
}


\score {
  <<
    \staffA
    \staffB
  >>
 
  \layout { }
  %\midi {   
  %  \tempo 4 = 115
  %}
}

Und bitte gib immer Deine Lily-Version an.

Gruß,
  Harm

fugenkomponist

  • Member
Re: mit scheme: Noten in Pausen umwandeln?
« Antwort #2 am: Mittwoch, 7. Oktober 2015, 07:14 »
Da rJazz neu ist in Scheme, erkläre ich kurz:
toRest =
#(define-music-function (mus) (ly:music?)
   (mmrest-of-length mus))
mmrest-of-length ist eine Funktion (zu finden in der Datei scm/music-functions.scm), die tut, was ihr Name sagt. Nämlich eine Mehrtaktpause (R) zurückgeben, die so lang ist wie die übergebene Musik mus (man übergibt also Musik, keine Länge; für letzteres kann man einfach (make-music 'MultiMeasureRest …) verwenden).
Zitat
toRestII =
#(define-music-function (mus) (ly:music?)
   (music-map
     (lambda (m)
       (if (music-is-of-type? m 'note-event)
           #{ r $(ly:music-property m 'duration) #}
           m))
     mus))
music-map macht das, was du wolltest: Es ist wendet eine Funktion auf alle Bestandteile eines Musik-Ausdrucks an. Vom Denken her ist es eigentlich etwas anders, aber im Prinzip ist das die „Schleife“, die du wolltest. (Im Endeffekt wird da vermutlich ne Tiefensuche/Backtracking über den Ausdruck laufen. Für Listen gibts die Funktion map, die ist vermutlich endrekursiv definiert, also auch nicht wesentlich anders als ne Schleife.)

Die Funktion, die auf die Teilausdrücke von mus angewendet wird, hat in diesem Fall keinen Namen, sondern ist ein λ-Ausdruck (lambda (arg1 arg2 …) ret), wobei ret in diesem Fall der Ausdruck (if …) ist.

Du kannst dir einen Musik-Ausdruck (z. B. staffA) mal per \void \displayMusic \staffA ausgeben lassen (\void verhindert nur, dass der Staff ausgegeben wird wie wenn du \staffA einfach so hingeschrieben hättest), dann wirst du sehen, dass der aus wesentlich mehr als nur Noten besteht (z. B. SequentialMusic, RelativeOctaveMusic). Da music-map über alle Teilausdrücke läuft, müssen natürlich alle Nicht-Noten in Ruhe gelassen werden.

harm6

  • Member
Re: mit scheme: Noten in Pausen umwandeln?
« Antwort #3 am: Mittwoch, 7. Oktober 2015, 14:44 »
Zitat von: fugenkomponist
Da rJazz neu ist in Scheme, erkläre ich kurz: [...]

Gute Analyse. Ich hatte ja mal angefangen posts über scheme/guile-basics zu schreiben. Sollte man vielleicht fortführen...

Hier noch eine Beobachtung:

\version "2.19.28"

toRestII =
#(define-music-function (mus) (ly:music?)
   (music-map
     (lambda (m)
       (if (music-is-of-type? m 'note-event)
           #{ r $(ly:music-property m 'duration) #}
           m))
     mus))
     
\displayMusic
\toRestII \relative c' { c4 <d f> e f }

ergibt:

Zitat
(make-music
  'RelativeOctaveMusic
  'element
  (make-music
    'SequentialMusic
    'elements
    (list (make-music
            'RestEvent
            'duration
            (ly:make-duration 2))
          (make-music
            'EventChord
            'elements
            (list (make-music
                    'RestEvent
                    'duration
                    (ly:make-duration 2))
                  (make-music
                    'RestEvent
                    'duration
                    (ly:make-duration 2))))
          (make-music
            'RestEvent
            'duration
            (ly:make-duration 2))
          (make-music
            'RestEvent
            'duration
            (ly:make-duration 2)))))

Interessant ist der EventChord, der nur aus Pausen besteht.

Tatsächlich kompiliert in einem .ly-file:

$(make-music
   'EventChord
   'elements
   (list (make-music
           'RestEvent
           'duration
           (ly:make-duration 2))
         (make-music
           'RestEvent
           'duration
           (ly:make-duration 2))))

Nicht aber:

{ < r r >2 }

Interessant ...

Gruß,
  Harm

fugenkomponist

  • Member
Re: mit scheme: Noten in Pausen umwandeln?
« Antwort #4 am: Donnerstag, 8. Oktober 2015, 15:50 »
Anscheinend erlaubt die LilyPond-Eingabesprache (mit anderen Worten: der Parser) nicht alles, was in der Datenstruktur abbildbar ist. Es gibt auch einfachere Beispiele, ich kenne z. B. keine Möglichkeit,
\displayMusic {
  #(make-music
  'NoteEvent
  'duration (ly:make-duration 0 0)
  'pitch (ly:make-pitch 0 0 0)
  'meineEigeneProperty "foo")
}
ohne explizites Hinschreiben in Scheme (oder als Ergebnis einer Musikfunktion) in LilyPond darzustellen ;) Aber wann braucht man mal Akkorde von Pausen? Mehrere Pausen gleichzeitig sind ja eigentlich immer der Polyphonie geschuldet, also keine Akkorde.

rJazz

  • Member
Re: mit scheme: Noten in Pausen umwandeln?
« Antwort #5 am: Donnerstag, 8. Oktober 2015, 22:07 »
Ja, erstklassig.
Die Grundlagen habe ich mal verstanden (also Anwenden einer Funktion auf jedes Element mit music-map),
die Details noch nicht alle.

Damit konnte ja die explizite Schleife geschickt umgangen werden.

Es stellt sich mir jetzt noch die Anschlussfrage für toRestII:
dort wird ja für jedes Element eine Pause erzeugt (so wie ich es vorgsechlagen und gedacht hatte).
Jetzt wäre es im Anschluss natürlich sinnvoll, die Pausen zusammenzufassen,
also im Bespiel statt vier Viertelpausen eine ganztaktige  Pause einzügen.
das heißt: die Noten von vorn bis hinten durchgehen, und innerhalb der Taktgrenzen
solange Pausen zusammenzufassen, bis der Takt zuende ist oder etwas anderes
als eine Pause kommt (also klingende Note).

Das wird wahrscheinlich nicht ohne Schleife gehen, oder?
Falls es überhaupt geht, die Takte einzeln zu durchlaufen.

fugenkomponist

  • Member
Re: mit scheme: Noten in Pausen umwandeln?
« Antwort #6 am: Freitag, 9. Oktober 2015, 00:10 »
Die Grundlagen habe ich mal verstanden (also Anwenden einer Funktion auf jedes Element mit music-map),
die Details noch nicht alle.
Frag ruhig, wenns was konkretes gibt. Ich hab meine Antwort bewusst knapp gehalten, aber ich kann eigentlich zu allen Teilen noch weiter ausführen ;)
Zitat
Damit konnte ja die explizite Schleife geschickt umgangen werden.
Kleines Mißverständnis: Es gibt gar keine Schleifen in funktionalen Programmiersprachen, nur Rekursion. Allerdings hat Scheme sogenannte named-let-Ausdrücke, die ein einfach handzuhabendes Äquivalent darstellen. Und bewiesenermaßen lässt sich mit Rekursion alles machen, was mit Schleifen geht und umgekehrt, das stellt also keine Einschränkung dar.
Zitat
Das wird wahrscheinlich nicht ohne Schleife gehen, oder?
Falls es überhaupt geht, die Takte einzeln zu durchlaufen.
Das geht schon; die Schwierigkeit liegt in diesem Fall darin, die Taktgrenzen zu finden. Dafür musst du auf die Kontexteigenschaften 'measureLength und 'measurePosition des aktuellen Kontextes zugreifen. Da hab ich wenig Erfahrung mit, mit Funktionen wie \applyContext ist das aber grundsätzlich möglich. Wenn du das aber erstmal hast, kannst du auch gleich auf die Version mit mmrest-of-length zurückgreifen. Vorgehensweise wäre dann:
• Die Länge der Pause rauskriegen per ly:music-length.
• Mit measureLength und measurePosition rausfinden, wo im Takt du bist und wie lang ein Takt ist.
• Den aktuellen Takt bis zum Ende auffüllen, dann eine Mehrtaktpause und evtl. noch den übrigen Teiltakt mit Pausen füllen.
Funktioniert so nur so lange, wie es keine Taktwechsel gibt. Ansonsten musst du halt von Takt zu Takt springen (und eintaktige Pausen verwenden; das geht wiederum nur, solange nicht mit \partial und \set measureLength/Position rumgespielt wird). Hat leider den Nachteil, dass \compressFullBarRests dann mehrere Takte nicht mehr zusammenfasst.

rJazz

  • Member
Re: mit scheme: Noten in Pausen umwandeln?
« Antwort #7 am: Freitag, 9. Oktober 2015, 11:26 »
Das heißt, also vom Ersetzen der einzelnen
Noten ganz wegggehen, die  Gesamtlänge
der Phrase ermtelln und dann die neue
Phrase taktweise mit Pausen auffüllen wie
von fugenkomponist beschrieben?

Dann wäre noch die Frage wie geht das,
die Gesamtlänge zu ermitteln:
lässt sich ly:music-length auch auf die gesamte
Phrase, und nich nur auf einzelne Noten anwenden?)


fugenkomponist

  • Member
Re: mit scheme: Noten in Pausen umwandeln?
« Antwort #8 am: Freitag, 9. Oktober 2015, 12:20 »
Ja, so hatte ich das gedacht. Und ja, das geht:\version "2.19.28"

music = {
  c4 d8 e f g a b c
}

#(display
  (ly:music-length music))

% oder direkt

#(display
  (ly:music-length
   #{
     c4 d8 e f g a b c
   #}))
Beide Male wird #<Mom 9/8> ausgegeben. Mit Werten vom Typ ly:moment? lässt sich auch rechnen, dafür gibts ly:moment-add & Co.