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.
{
}
-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)
{
{
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)
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);
}
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);
Moment length_mom_;
Pitch transposing_;
Audio_dynamic *dynamic_;
+ int extra_velocity_;
Audio_note *tied_;
bool tie_event_;
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)))
{
}
void process_music ();
DECLARE_TRANSLATOR_LISTENER (note);
+ DECLARE_TRANSLATOR_LISTENER (breathing);
private:
vector<Stream_event *> note_evs_;
vector<Audio_note *> notes_;
{
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)
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);
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 */
"",
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?)
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")
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")
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))
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.")
(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