Autor Thema: \score dynamisch erzeugen  (Gelesen 4222 mal)

derburn

  • Member
\score dynamisch erzeugen
« am: Freitag, 11. März 2011, 11:20 »
Hi zusammen,

sagt mal, gibt es eigentlich eine Möglichkeit, \scores dynamisch zu generieren?
Ich denke an so was wie:

myDrumScore = #(define-music-function (parser location myMusic) (ly:music?)
    #{
       \score { <<
         \new DrumStaff { $myMusic }
       >>
         \layout {
         \context {
           \DrumStaff
             \remove Clef_engraver
             \remove Time_signature_engraver
                  \remove Bar_engraver
           } } }
    #})

Aufruf z.B.:
\myDrumScore { bda4 r16 bda8. bda4 cl4 }

So wie oben dargestellt geht es nicht: define-music-function ist sicher der falsche Kandidat und beim
Kompilieren beschwert er sich auch, wenn er neue Score anlegen soll:
"syntax error, unexpected \score".

Dass dabei einige Engraver entfernt werden, ist übrigens nur als Platzhalter für beliebige
\context - Aktionen gedacht.

Aber gibt es nicht eine Möglichkeit, eine Funktion zu definieren, die einem z.B. genau den obigen Score
anlegt und der man dann nur noch die Musik übergeben muss?

Hintergrund ist der, dass ich zur Notation von Percussions oft sehr ähnliche Muster notiere, die sich
im Wesentlichen durch die eigentliche Musik unterscheiden - der Aufbau der Score (inkl. Layout- und MIDI-
Anpassungen und diverser overrides) ist meist völlig identisch.

Da kommen dann pro Stück allerdings jedes Mal recht viele Zeilen zusammen.
Wenn man bspw. ein Stück mit 20 Stimmen schreibt und man eine Funktion wie oben angedeutet hätte,
könnte man die ganze Score-Generierung in 20 Zeilen erledigen (bzw. 40, wenn man ebenfalls MIDIs erzeugt)...

Hat da jemand eine Idee?

kilgore

  • Member
Re:\score dynamisch erzeugen
« Antwort #1 am: Freitag, 18. März 2011, 09:42 »
Warum man so ein Score nicht herstellen kann weiss ich nicht.   Meine Lösung antwortet deine Frage nicht, aber das Resultat ist fast das gleiche.  mit "\include" kann man dein Score-Konstrukt mit einbeziehen, und sich die ganze Zeilen sparen:

\version "2.12.3"
%\include "file/location"


%hier ist dein simple eingabe

myDrumScore = { c c c c }



%Dieser Teil geht in ein andere Datei, deren Ort oben nach "include"
%eingegeben wird.

\score { <<
         \new DrumStaff { \myDrumScore }
       >>
         \layout {
           \context {
               \DrumStaff
                     \remove Clef_engraver
                     \remove Time_signature_engraver
                  \remove Bar_engraver
                   } } }

derburn

  • Member
Re:\score dynamisch erzeugen
« Antwort #2 am: Freitag, 18. März 2011, 22:08 »
Hallo kilgore,

vielen Dank für die Antwort, allerdings löst sie das Problem leider nicht vollständig...
Das Problem ist dabei, dass im include-File die Variable \myDrumScore fest drin steht, d.h.
ein Aufruf wie der folgende ist nicht möglich:

drumsOne = \drummode { bda4 cglo4 toml4 hh4 }
drumsTwo = \drummode { hh4 bda4 cglo4 toml4 }

\include "includeStuff.ily"

drumsOne   = \drummode { cglo4 toml4 hh4 bda4 }
drumsTwo   = \drummode { toml4 hh4 bda4 cglo4 }
drumsThree = \drummode { toml4 tomml4 cghm4 cglo4 }

\include "includeStuff.ily"

Klar könnte ich dafür wieder ein Template in Form eines anderen include-Files bauen,
aber im Allgemeinen gibt es zu viele Varianten davon, welche Staves in
den ansonsten immer gleich aussehenden Scores kombiniert werden.
Ergo: zu viele Templates, die ich dann doch wieder ändern müsste, sollte
sich die Score-Vorlage mal ändern.

Um das zu umgehen, müsste ich eine Funktion haben, der ich nur die ly:music mitgebe und die mir den Rest generiert.
Dass dann mehrere Staves in der Score erscheinen, wenn z.B. mehrere ly:musics übergeben werden, ist dann nur noch
ein bischen weitere Kapselungssarbeit.

Trotzdem nochmals vielen Dank!
Bin für weitere Ideen aller Art dankbar...

P.S.: Gibt es eigtl. ein Pendant zu \displayMusic, das einem den generierten Code auch für Scores anzeigt?

kilgore

  • Member
Re:\score dynamisch erzeugen
« Antwort #3 am: Samstag, 19. März 2011, 13:13 »
...so?

\version "2.12.3"
%\include "file/location"


%hier ist dein simple eingabe

drumOne = { c c c c }

drumTwo = { c c c c }

drumThree = { c c c c }


myDrumScore = << \new StaffGroup
  \new DrumStaff { \drumOne }
  \new DrumStaff { \drumTwo }
  \new DrumStaff { \drumThree }
>>



%Dieser Teil geht in ein andere Datei, deren Ort oben nach "include"
%eingegeben wird.

\score { <<
          { \myDrumScore }
       >>
         \layout {
           \context {
               \DrumStaff
                     \remove Clef_engraver
                     \remove Time_signature_engraver
                  \remove Bar_engraver
                   } } }



Zitat
P.S.: Gibt es eigtl. ein Pendant zu \displayMusic, das einem den generierten Code auch für Scores anzeigt?

oh das weiss ich nicht....

derburn

  • Member
Re:\score dynamisch erzeugen
« Antwort #4 am: Sonntag, 20. März 2011, 20:16 »
Hallöchen,

kilgore, Deine Lösung ist tatsächlich ausreichend, vorausgesetzt,
ich bekomme noch das Problem in den Griff, dass ich die Anzahl
DrumStaffs per Listenübergabe generiere.

Siehe folgendes Bsp.: im folgenden Code werden zwar 2 Listen übergeben,
aber daraus wird immer eine StaffGroup mit den 3 gleichen Stimmen gebastelt.

drumOne   = \drummode { bda4 cglo4 toml4 hh4 }
drumTwo   = \drummode { hh4 bda4 cglo4 toml4 }
drumThree = \drummode { cglo4 toml4 hh4 bda4 }

myDrumStaff = #(define-music-function (parser location iname music) (string? ly:music?)
  #{
      \new DrumStaff {
        \set Staff.instrumentName = \markup { $iname }
        \new DrumVoice { $music }
      }
  #})

myStaffGroup = #(define-music-function (parser location inames music) (list? list?)
  #{
      << \new StaffGroup
         \myDrumStaff #"Drum 1" \drumOne
         \myDrumStaff #"Drum 2" \drumTwo
         \myDrumStaff #"Drum 3" \drumThree
      >>
  #})

Was ich natürlich brauche, ist eine Idee, wie ich die \myDrumStuff - Zeilen entsprechend ersetze.
Meine bisherigen Versuche, aus einer Liste den obigen Code zu generieren,
scheiterten leider kläglich.

Ich stelle mir den Aufruf so vor:

myDrumScore = \myStaffGroup #'("Drum 1" "Drum 2" "Drum 3") #'(\drumOne \drumTwo \drumThree)
\include "includeStuff.ily"

In der Datei "includeStuff.ily" steht dann die eigentliche Score:
\score { <<
         \myDrumScore
       >>
         \layout {
           \context {
               \DrumStaff
                  \remove Clef_engraver
                  \override Stem #'direction = #UP
                  \override Beam #'positions = #'(3.5 . 3.5)
                   } } }

Vielen Dank & viele liebe Grüße,
Manuel

RobUr

  • Member
Re:\score dynamisch erzeugen
« Antwort #5 am: Sonntag, 20. März 2011, 21:31 »
Hallo Manuel!

Zitat von: derburn
Gibt es eigtl. ein Pendant zu \displayMusic, das einem den generierten Code auch für Scores anzeigt?

Reicht dir
\score {
  \displayMusic {
    <<
      \myDrumScore
    >>
  }

  \layout {
    \context {
      \DrumStaff
      \remove Clef_engraver
      \override Stem #'direction = #UP
      \override Beam #'positions = #'(3.5 . 3.5)
    }
  }
}
? Aufpassen, dass der \layout-Block nicht mit eingeklammert wird.

Zitat von: derburn
… vorausgesetzt, ich bekomme noch das Problem in den Griff, dass ich die Anzahl DrumStaffs per Listenübergabe generiere.
Oh, keine Ahnung, wie das in Scheme umgesetzt wird (sicher denkst du an dynamisch generierte [eindimensionale] Arrays wie in Java, PHP & Co.?). Ein Ansatz wäre vielleicht die Simulation von Schleifen durch rekursive Funktionsaufrufe. Oder du schaust dir die Sache mit Paaren und Listen genauer an.

Grüße, Robert

derburn

  • Member
Re:\score dynamisch erzeugen
« Antwort #6 am: Montag, 21. März 2011, 22:37 »
Hi Robert,

leider reicht Dein Vorschlag nicht aus, da es genau der Code aus der Score und dem Layout ist, der mich interessiert...
Trotzdem danke!

Ich bin derzeit dran, mir die Listen in Scheme genauer anzusehen, und bin auf Folgendes gestoßen:
#(define displayList
  (lambda (ls)
    (if (null? ls)
        (newline)
        (begin
          (display (car ls))
          (newline)
          (displayList (cdr ls))))))

#(displayList '())
#(displayList '(I'm a loser baby so why don't you kill me))
#(displayList '(bla blubb))

Das erzeugt die Ausgabe:


I'm
a
loser
baby
so
why
don't
you
kill
me

bla
blubb



Das ist genau der Rekursionsansatz: car gibt einem das erste Listenelement zurück, cdr den Rest der Liste.
Ruft man sich also immer wieder selbst auf mit dem Übergabeparameter cdr, dann schleift man sich
(meines Erachtens höchst umständlich) durch die Liste und kann dann mit jedem einzelnen Listenelement
irgendwas anstellen - in diesem Fall einfach nur ausgeben.
Das ist dann die Zeile "display (car ls)".

Allerdings sind schon meine Versuche gescheitert, die Liste als einen zusammengehängten String zurückzugeben,
also das Pendant zu der PHP- bzw. perl-Funktion "join". Ich versuche, mit "let" einen Leerstring zu definieren und
daran jedes Listenelement anzuhängen, aber schon da passen dem Compiler entweder die Aufrufparameter nicht
oder das Ergebnis ist immer das, was bei der Abfrage "(null? ls)" im true-Falle steht.

Mal sehen, wohin das alles führt...
Ich geb nicht auf und bin natürlich weiterhin für alle Tipps dankbar! ;)

Viele liebe Grüße,
Manuel
« Letzte Änderung: Montag, 21. März 2011, 22:41 von derburn »

derburn

  • Member
Re:\score dynamisch erzeugen
« Antwort #7 am: Mittwoch, 13. April 2011, 11:00 »
Hallöchen allerseits,

mittlerweile hätte ich eine Lösung, die zwar ziemlich unelegant ist, aber doch sehr wirksam.
Ausgehend von meinem ursprünglichen Funktionsvorschlag fiel mir auf, dass die Funktion tatsächlich eine Score erzeugt,
wenn man "\score" durch "\new Score" ersetzt. Vermutlich wird bei \new Score gerade erst das Objekt erzeugt, während
\score ein bereits existierendes Objekt voraussetzt.

Um das Problem mit mehreren Staffs pro Score zu lösen, kann man sich einfach Scores mit jeweils 1,2,3...n Staffs definieren.
Wirklich nicht schön, aber effektiv.
Hier ein Beispielcode:

\version "2.13.51"

myDrumScore = #(define-music-function (parser location musicOne beamsOne) (ly:music? pair?)
  #{
    \new Score <<
      \new DrumStaff \with {
        \remove Clef_engraver
        \override Stem #'direction = #UP
        \override Beam #'positions = #$beamsOne
        \override StaffSymbol #'staff-space = #'1.15
    } << { $musicOne } >>
    >>
  #})

myDrumScoreTwo = #(define-music-function (parser location musicOne beamsOne musicTwo beamsTwo) (ly:music? pair? ly:music? pair?)
  #{
    \new Score <<
      \new DrumStaff \with {
        \remove Clef_engraver
        \override Stem #'direction = #UP
        \override Beam #'positions = #$beamsOne
        \override StaffSymbol #'staff-space = #'1.15
    } << { $musicOne } >>
   
      \new DrumStaff \with {
        \remove Clef_engraver
        \override Stem #'direction = #UP
        \override Beam #'positions = #$beamsTwo
        \override StaffSymbol #'staff-space = #'1.15
    } << { $musicTwo } >>
    >>
  #})

myDrumScoreThree = #(define-music-function (parser location musicOne beamsOne musicTwo beamsTwo musicThree beamsThree) (ly:music? pair? ly:music? pair? ly:music? pair?)
  #{
    \new Score <<
      \new DrumStaff \with {
        \remove Clef_engraver
        \override Stem #'direction = #UP
        \override Beam #'positions = #$beamsOne
        \override StaffSymbol #'staff-space = #'1.15
    } << { $musicOne } >>
   
      \new DrumStaff \with {
        \remove Clef_engraver
        \override Stem #'direction = #UP
        \override Beam #'positions = #$beamsTwo
        \override StaffSymbol #'staff-space = #'1.15
    } << { $musicTwo } >>

      \new DrumStaff \with {
        \remove Clef_engraver
        \override Stem #'direction = #UP
        \override Beam #'positions = #$beamsThree
        \override StaffSymbol #'staff-space = #'1.15
    } << { $musicThree } >>
    >>
  #})


eins = \drummode { bda8. bda16 sn8. bda16 r16 bda8 bda16 sn4 }
   
\displayMusic { <<
  \myDrumScoreThree { \eins }  #'(2.5 . 2.5)
                    { \eins }  #'(3.5 . 3.5)
                    { \eins }  #'(4.5 . 4.5)
>> }
\layout {
  indent = 1\cm
  \context {
    \Score
    \remove "System_start_delimiter_engraver"
    } % context
  } % layout

Im Beispiel kann man die DrumStaffs sogar rudimentär konfigurieren.
Außerdem zeigt \displayMusic den ganzen Score-Inhalt an.

Warum der \layout-Block nicht in die Score selbst eingebaut werden kann, weiß ich nicht. (Fehler: 'unexpected \layout').
Wenn man obige 'myDrumScore...'-Definitionen auslagert, kann man damit eigentlich recht bequem arbeiten.
Sehr viel schöner wäre es natürlich, wenn man die ganzen Definitionen nicht per Hand machen müsste, sondern einfach eine
Liste mit Musik & sonstiger Konfiguration pro DrumStaff übergeben könnte...

Solange dies nicht gelingt, werde ich wohl obigen Vorschlag nutzen...

Ich hoffe, Ihr könnt das gebrauchen... :)
Viele liebe Grüße,
Manuel