]> git.donarmstrong.com Git - lilypond.git/commitdiff
Support articulations and breaths in MIDI. (issue 3664)
authorDevon Schudy <dschudy@gmail.com>
Sun, 24 Nov 2013 12:07:10 +0000 (07:07 -0500)
committerJanek Warchoł <lemniskata.bernoullego@gmail.com>
Thu, 5 Dec 2013 21:02:45 +0000 (22:02 +0100)
Articulations and breaths can alter the length and volume of the note.
(Breaths affect the previous chord.) This is controlled by their
midi-length and midi-extra-velocity properties. The standard articulations
now have these properties where appropriate.

(seconds->moment s context) is a convenience function for making
absolute durations.

lily/audio-item.cc
lily/drum-note-performer.cc
lily/include/audio-item.hh
lily/midi-item.cc
lily/note-performer.cc
ly/music-functions-init.ly
ly/script-init.ly
scm/define-music-properties.scm
scm/lily-library.scm

index a41357b28f296239350e38e770af2c01b7df7d21..7934c34c4a1e86e3eea155c94298c36870c1aa68 100644 (file)
@@ -44,11 +44,13 @@ Audio_item::Audio_item ()
 {
 }
 
-Audio_note::Audio_note (Pitch p, Moment m, bool tie_event, Pitch transposing)
+Audio_note::Audio_note (Pitch p, Moment m, bool tie_event, Pitch transposing,
+                        int velocity)
   : pitch_ (p),
     length_mom_ (m),
     transposing_ (transposing),
     dynamic_ (0),
+    extra_velocity_ (velocity),
     tied_ (0),
     tie_event_ (tie_event)
 {
index 94c2d55ea319b0edc6c3e6730a706f323095ffc4..09ab3f7cf4417130ed165fca35edf3e1ad5d2dc9 100644 (file)
@@ -62,9 +62,9 @@ Drum_note_performer::process_music ()
         {
           SCM articulations = n->get_property ("articulations");
           Stream_event *tie_event = 0;
-          for (SCM s = articulations;
-               !tie_event && scm_is_pair (s);
-               s = scm_cdr (s))
+          Moment len = get_event_length (n, now_mom ());
+          int velocity = 0;
+          for (SCM s = articulations; scm_is_pair (s); s = scm_cdr (s))
             {
               Stream_event *ev = unsmob_stream_event (scm_car (s));
               if (!ev)
@@ -72,12 +72,16 @@ Drum_note_performer::process_music ()
 
               if (ev->in_event_class ("tie-event"))
                 tie_event = ev;
+              SCM f = ev->get_property ("midi-length");
+              if (ly_is_procedure (f))
+                len = robust_scm2moment (scm_call_2 (f, len.smobbed_copy (),
+                                                     context ()->self_scm ()),
+                                         len);
+              velocity += robust_scm2int (ev->get_property ("midi-extra-velocity"), 0);
             }
 
-          Moment len = get_event_length (n, now_mom ());
-
           Audio_note *p = new Audio_note (*pit, len,
-                                          tie_event, Pitch (0, 0, 0));
+                                          tie_event, Pitch (0, 0, 0), velocity);
           Audio_element_info info (p, n);
           announce_element (info);
         }
index 43ed2e05a91e6fd8ccb80b8ff351354be927fb02..9ded5301a1cc5d443b173848d8d99068a8effab7 100644 (file)
@@ -82,7 +82,7 @@ public:
 class Audio_note : public Audio_item
 {
 public:
-  Audio_note (Pitch p, Moment m, bool tie_event, Pitch transposition);
+  Audio_note (Pitch p, Moment m, bool tie_event, Pitch transposition, int velocity);
 
   // with tieWaitForNote, there might be a skip between the tied notes!
   void tie_to (Audio_note *, Moment skip = 0);
@@ -93,6 +93,7 @@ public:
   Moment length_mom_;
   Pitch transposing_;
   Audio_dynamic *dynamic_;
+  int extra_velocity_;
 
   Audio_note *tied_;
   bool tie_event_;
index 54d00ff683d59ce4f1ffc98975c492e91de9c26b..f98e3d05e1ec0049a022fd3400b31d9a252948eb 100644 (file)
@@ -193,8 +193,10 @@ Midi_time_signature::to_string () const
 Midi_note::Midi_note (Audio_note *a)
   : Midi_channel_item (a),
     audio_ (a),
-    dynamic_byte_ (a->dynamic_ && a->dynamic_->volume_ >= 0
-                   ? Byte (a->dynamic_->volume_ * 0x7f) : Byte (0x5a))
+    dynamic_byte_ (min (max (Byte ((a->dynamic_ && a->dynamic_->volume_ >= 0
+                                    ? a->dynamic_->volume_ * 0x7f : 0x5a)
+                                   + a->extra_velocity_),
+                             Byte (0)), Byte (0x7f)))
 {
 }
 
index 83ecb52d561439ac4157e5982d88ef9f7f8b9dec..81f35d70ac1a8d3c6b140840750620f9309e37ff 100644 (file)
@@ -36,6 +36,7 @@ protected:
   void process_music ();
 
   DECLARE_TRANSLATOR_LISTENER (note);
+  DECLARE_TRANSLATOR_LISTENER (breathing);
 private:
   vector<Stream_event *> note_evs_;
   vector<Audio_note *> notes_;
@@ -65,9 +66,9 @@ Note_performer::process_music ()
         {
           SCM articulations = n->get_property ("articulations");
           Stream_event *tie_event = 0;
-          for (SCM s = articulations;
-               !tie_event && scm_is_pair (s);
-               s = scm_cdr (s))
+          Moment len = get_event_length (n, now_mom ());
+          int velocity = 0;
+          for (SCM s = articulations; scm_is_pair (s); s = scm_cdr (s))
             {
               Stream_event *ev = unsmob_stream_event (scm_car (s));
               if (!ev)
@@ -75,12 +76,16 @@ Note_performer::process_music ()
 
               if (ev->in_event_class ("tie-event"))
                 tie_event = ev;
+              SCM f = ev->get_property ("midi-length");
+              if (ly_is_procedure (f))
+                len = robust_scm2moment (scm_call_2 (f, len.smobbed_copy (),
+                                                     context ()->self_scm ()),
+                                         len);
+              velocity += robust_scm2int (ev->get_property ("midi-extra-velocity"), 0);
             }
 
-          Moment len = get_event_length (n, now_mom ());
-
           Audio_note *p = new Audio_note (*pitp, len,
-                                          tie_event, transposing);
+                                          tie_event, transposing, velocity);
           Audio_element_info info (p, n);
           announce_element (info);
           notes_.push_back (p);
@@ -128,6 +133,26 @@ Note_performer::listen_note (Stream_event *ev)
   note_evs_.push_back (ev);
 }
 
+IMPLEMENT_TRANSLATOR_LISTENER (Note_performer, breathing)
+void
+Note_performer::listen_breathing (Stream_event *ev)
+{
+  //Shorten previous note if needed
+  SCM f = ev->get_property ("midi-length");
+  if (ly_is_procedure (f))
+    for (vsize i = 0; i < last_notes_.size (); i++)
+      {
+        Audio_note *tie_head = last_notes_[i]->tie_head ();
+        //Give midi-length the available time since the note started,
+        //including rests. It returns how much is left for the note.
+        Moment available = now_mom () - tie_head->audio_column_->when ();
+        Moment len = robust_scm2moment (scm_call_2 (f, available.smobbed_copy (),
+                                                    context ()->self_scm ()), available);
+        if (len < tie_head->length_mom_)
+          tie_head->length_mom_ = len;
+      }
+}
+
 ADD_TRANSLATOR (Note_performer,
                 /* doc */
                 "",
index bb577e28b58c63bb651a09be26d0ac1bb0c6ab62..83c2535af58909766ac617bb0aa3733a935cc9c3 100644 (file)
@@ -253,7 +253,15 @@ bookOutputSuffix =
 breathe =
 #(define-music-function (parser location) ()
    (_i "Insert a breath mark.")
-   (make-music 'BreathingEvent))
+   (make-music 'BreathingEvent
+     'midi-length
+     (lambda (len context)
+       ;;Shorten by half, or by up to a second, but always by a power of 2
+       (let* ((desired (min (ly:moment-main (seconds->moment 1 context))
+                            (* (ly:moment-main len) 1/2)))
+              (scale (inexact->exact (ceiling (/ (log desired) (log 1/2)))))
+              (breath (ly:make-moment (expt 1/2 scale))))
+         (ly:moment-sub len breath)))))
 
 clef =
 #(define-music-function (parser location type) (string?)
index 89c1f5802bd37b91fc6d42b2801f5b96cdf1722c..3b3e39ed31d11b1ca86e8d5f7a80e0f214070023 100644 (file)
@@ -4,7 +4,8 @@
 
 harmonic = #(make-music 'HarmonicEvent)
 
-accent = #(make-articulation "accent")
+accent = #(make-articulation "accent"
+          'midi-extra-velocity 20)
 coda = #(make-articulation "coda")
 downbow = #(make-articulation "downbow")
 downmordent = #(make-articulation "downmordent")
@@ -17,10 +18,15 @@ lheel = #(make-articulation "lheel")
 lineprall = #(make-articulation "lineprall")
 longfermata = #(make-articulation "longfermata")
 ltoe = #(make-articulation "ltoe")
-marcato = #(make-articulation "marcato")
+marcato = #(make-articulation "marcato"
+           'midi-extra-velocity 40)
 mordent = #(make-articulation "mordent")
 open = #(make-articulation "open")
-portato = #(make-articulation "portato")
+
+portato = #(make-articulation "portato"
+           'midi-length
+           (lambda (len context)
+            (ly:moment-mul len (ly:make-moment 3/4))))
 prall = #(make-articulation "prall")
 pralldown = #(make-articulation "pralldown")
 prallmordent = #(make-articulation "prallmordent")
@@ -33,8 +39,17 @@ segno = #(make-articulation "segno")
 shortfermata = #(make-articulation "shortfermata")
 signumcongruentiae = #(make-articulation "signumcongruentiae")
 snappizzicato = #(make-articulation "snappizzicato")
-staccatissimo = #(make-articulation "staccatissimo")
-staccato = #(make-articulation "staccato")
+staccatissimo = #(make-articulation "staccatissimo"
+                 'midi-length
+                 (lambda (len context)
+                   (seconds->moment 1/8 context))
+                 'midi-extra-velocity 6)
+staccato = #(make-articulation "staccato"
+            'midi-length
+            (lambda (len context)
+              (moment-min (ly:moment-mul len (ly:make-moment 1/2))
+                          (seconds->moment 1/2 context)))
+            'midi-extra-velocity 4)
 stopped = #(make-articulation "stopped")
 tenuto = #(make-articulation "tenuto")
 thumb = \finger \markup \scale #(cons (magstep 5) (magstep 5))
index 5c9c12538e03b761798e17122d5ac807ac84ce4b..4469f88ff0ad8646f2083962e69133bcd2e6bc15 100644 (file)
@@ -119,6 +119,12 @@ This property can only be defined as initializer in
 whether to allow, forbid or force a line break.")
 
      (metronome-count ,number-or-pair? "How many beats in a minute?")
+     (midi-extra-velocity ,integer? "How much louder or softer should
+this note be in MIDI output? The default is 0.")
+     (midi-length ,procedure? "Function to determine how long to play
+a note in MIDI. It should take a moment (the written length of the
+note) and a context, and return a moment (the length to play the
+note).")
      (moment ,ly:moment? "The moment at which an event happens.")
      (music-cause ,ly:music? "The music object that is the cause of
 an event.")
index 43b05e57c20d8fa6b5b9e784d434cb5c564d1c58..79134ca7d46caf1f16c80478755aa5fa97f71472 100644 (file)
   (cons (ly:moment-main-numerator moment)
         (ly:moment-main-denominator moment)))
 
+(define-public (seconds->moment s context)
+  "Return a moment equivalent to s seconds at the current tempo."
+  (ly:moment-mul (ly:context-property context 'tempoWholesPerMinute)
+                 (ly:make-moment (/ s 60))))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; durations