define-scheme-function endet mit error: bad expression type -- gelöst

Begonnen von Manuela, Sonntag, 29. März 2020, 15:56

« vorheriges - nächstes »

Manuela

Hi,
ich möchte mehrere Midi-Files anhand einer Liste von Midi-Instrumenten ausgeben.
Der folgende Code funktioniert:
\version "2.20.0"
\language "deutsch"

mus=\relative c'' { c4 c c c c }

make-score =
#(define-scheme-function (instrument)(string?)
   #{
     \score {
       \new Staff
       {
         \set Staff.midiInstrument = $instrument
         \mus
       }
       \midi {
         \tempo 4=180
         \context {
           \Score
           midiMinimumVolume = #0.1
           midiMaximumVolume = #0.99
         }
       }
     }
   #})


Nun möchte ich mehrere Instrumente in einem Rutsch ausprobieren und habe dazu folgendes geschrieben:
\version "2.20.0"
\language "deutsch"

mus=\relative c'' { c4 c c c c }

make-midi-list =
#(define-scheme-function (instrumentlist mus)(list? ly:music?)
   (map (lambda (p)
          #{
            \score {
              \new Staff
              {
                \set Staff.midiInstrument = $p
                $mus
              }
              \midi {
                \tempo 4=180
                \context {
                  \Score
                  midiMinimumVolume = #0.1
                  midiMaximumVolume = #0.99
                }
              }
            }
          #}) instrumentlist))

\make-midi-list #'( "acoustic grand" "bright acoustic") \mus


was zur folgenden Fehlermeldung führt:
error: bad expression type

Ersetze ich define-scheme-function durch define-void-function so wird keine Fehlermeldung, aber auch keine Ausgabe erzeugt.

Wahrscheinlich irgend ein blöder Fehler, ich komme aber nicht dahinter. Was mache ich falsch?
Danke für eure Hilfe
viele Grüße
-- Manuela

harm6

Hallo Manuela,

schauen wir erstmal was falsch läuft.
(Alle Beispiele für 2.20.0 und mit mus = \relative c'' { c4 c c c c } )
    \displayScheme \make-midi-list #'( "acoustic grand" "bright acoustic") \mus
führt zu
    (list #<Score> #<Score>)
LilyPond kann aber nichts mit einer bloßen Liste auf toplevel-Niveau anfangen.

Ich kann Deinen code unverändert ans laufen kriegen, wenn ich den Aufruf so verändere, daß die Liste aufgelöst wird.
    $@(make-midi-list '( "acoustic grand" "bright acoustic") mus)
Jetzt erscheinen die scores einzeln auf toplevel-Niveau und werden der internen Liste mit Namen toplevel-scores hinzugefügt.
Natürlich werden sie als midi-scores nicht gedruckt. Die erwähnte interne Liste kann übrigens mittels
    #(pretty-print (ly:parser-lookup 'toplevel-scores))
(gesetzt ans Ende eines files) eingesehen werden.

Man kann die scores auch mittels add-score direkt dieser Liste hinzufügen. Dann braucht man aber keine scheme-function, eine void-function wäre angemessener. Auch soll die void-function ja keine eigene Liste ausgeben, sondern man iteriert über die "instrumentlist" um add-score anzuwenden , also for-each statt map.
Führt zu:


make-midi-list-harm-I =
#(define-void-function (instrumentlist mus)(list? ly:music?)
   (for-each
     (lambda (p)
       (add-score
          #{
            \score {
              \new Staff
              {
                \set Staff.midiInstrument = $p
                $mus
              }
              \midi {
                \tempo 4=180
                \context {
                  \Score
                  midiMinimumVolume = #0.1
                  midiMaximumVolume = #0.99
                }
              }
            }
          #}))
     instrumentlist))

\make-midi-list-harm-I #'( "acoustic grand" "bright acoustic") \mus


Allerdings ist es so, daß all diese midis file-name.midi, file-name-1.midi, file-name-2.midi etc heißen.
Besser fände ich den Namen so zu verändern, daß er eine Referenz zum benutzten midiInstrument hat und optional einen eigenen base-name bekommt.
Also etwas wie my-name-or-file-name-midiInstrumentName.midi
Um dahin zu kommen sollte man sich klar machen, daß jede \midi {}-Angabe ein eigenes output-file erzeugt, anders gesprochen ein book.
Also kann man ja gleich ein book mittels einer void-function erstellen, die den gewünschten Namen berücksichtigt.
Führt zu:


make-midi-list-harm-II =
#(define-void-function (name score tempo-list)((string? #f) ly:score? list?)
  (for-each
    (lambda (instrument)
      (ly:book-process
         (apply
           ly:make-book
           $defaultpaper
           $defaultheader
           (list score))
         $defaultpaper
         #{
            \midi {
              \tempo 4 = 180
              \context {
                \Score
                midiMinimumVolume = #0.1
                midiMaximumVolume = #0.99
              }
              \context {
                \Staff
                midiInstrument = #instrument
              }
            }
         #}
         (format #f "~a-~a"
                 (or name (ly:parser-output-name))
                 (string-join (string-split instrument #\SPACE) "-"))))
     tempo-list))

\make-midi-list-harm-II
  \score { \new Staff { $mus } }
  #'( "acoustic grand" "bright acoustic")

\make-midi-list-harm-II
  "atest-102-x"
  \score { \new Staff { $mus } }
  #'( "acoustic grand" "bright acoustic")


Der erste Aufruf (ohne Angabe des base-names) führt zu midis mit dem default file-name (bei mir atest-102).
Der zweite Aufruf setzt "atest-102-x" an den Anfang des Namens, gefolgt vom midiInstrument-Name (eventuelle Leerzeichen werden durch "-" ersetzt)

Zitat von: terminal
$ lilypond atest-102.ly
GNU LilyPond 2.20.0
Processing `atest-102.ly'
Parsing...
Interpreting music...
MIDI output to `atest-102-acoustic-grand.midi'...
Interpreting music...
MIDI output to `atest-102-bright-acoustic.midi'...
Interpreting music...
MIDI output to `atest-102-x-acoustic-grand.midi'...
Interpreting music...
MIDI output to `atest-102-x-bright-acoustic.midi'...
Success: compilation successfully completed

HTH,
  Harm

Manuela

Vielen lieben Dank Harm.  :) :) :)

Wen der Name des Midi-Files den Instrumentnamen enthält, ist das natürlich noch viel besser als ich gedacht hätte. Ich war der Meinung, das geht nicht (habe irgendwo in der Mailingliste gelesen, vermutlich missverstanden).

Ich probier das heute abend mal aus und melde mich dann nochmal
Danke für eure Hilfe
viele Grüße
-- Manuela

Manuela

Funktioniert 1a! Super, danke!!!

Ich habe eine kleine Änderung angebracht, sodass die Dateien fortlaufend numeriert werden.
Die Idee dabei ist, dass jede Mididatei mit der Nummer ihres Instruments versehen ist.

\version "2.20.0"
\language "deutsch"

mus=\relative c'' { c4 c c c c }

#(define myzahl 0)

make-midi-list-harm-II =
#(define-void-function (name score tempo-list)
   ((string? #f) ly:score? list?)
   (for-each
    (lambda (instrument)
      (set! myzahl (+ myzahl 1))
      (ly:book-process
       (apply
        ly:make-book
        $defaultpaper
        $defaultheader
        (list score))
       $defaultpaper
       #{
         \midi {
           \tempo 4 = 180
           \context {
             \Score
             midiMinimumVolume = #0.1
             midiMaximumVolume = #0.99
           }
           \context {
             \Staff
             midiInstrument = #instrument
           }
         }
       #}
       (format #f "~3,'0d_~a"
         myzahl
         (string-join (string-split instrument #\SPACE) "_"))))
    tempo-list))
Danke für eure Hilfe
viele Grüße
-- Manuela

harm6

Hallo Manuela,

anstatt myZahl toplevel zu definieren und dann hochzuzählen, kannst Du Dich auch auf die Länge der Liste beziehen und iota verwenden.
Auch hast Du die Funktionalität die für die "name"-Variable da war entfernt, die (jetzt nutzlose) Variable aber drin gelassen.
Ich würde da lieber eine Wahlmöglichkeit haben. Führt zu:


\version "2.20.0"
\language "deutsch"

mus=\relative c'' { c4 c c c c }


make-midi-list-harm-III =
#(define-void-function (name score instrument-list)
   ((string? #f) ly:score? list?)
   (for-each
    (lambda (index instrument)
      (ly:book-process
       (apply
        ly:make-book
        $defaultpaper
        $defaultheader
        (list score))
       $defaultpaper
       #{
         \midi {
           \tempo 4 = 180
           \context {
             \Score
             midiMinimumVolume = #0.1
             midiMaximumVolume = #0.99
           }
           \context {
             \Staff
             midiInstrument = #instrument
           }
         }
       #}
       (format #f "~3,'0d~a_~a"
         index
         (if name (format #f "_~a" name) "")
         (string-join (string-split instrument #\SPACE) "_"))))
    (iota (length instrument-list) 1 1)
    instrument-list))
   
\make-midi-list-harm-III
  \score { \new Staff { $mus } }
  #'( "acoustic grand" "bright acoustic")
 
\make-midi-list-harm-III
  "foo"
  \score { \new Staff { $mus } }
  #'( "acoustic grand" "bright acoustic")
 
\make-midi-list-harm-III
  #(ly:parser-output-name)
  \score { \new Staff { $mus } }
  #'( "acoustic grand" "bright acoustic")



->
Zitat von: terminal
Interpreting music...
MIDI output to `001_acoustic_grand.midi'...
Interpreting music...
MIDI output to `002_bright_acoustic.midi'...
Interpreting music...
MIDI output to `001_foo_acoustic_grand.midi'...
Interpreting music...
MIDI output to `002_foo_bright_acoustic.midi'...
Interpreting music...
MIDI output to `001_atest-102_acoustic_grand.midi'...
Interpreting music...
MIDI output to `002_atest-102_bright_acoustic.midi'...

Gruß,
  Harm

Manuela

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