[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.11 Engraver tutorial

Engravers are C++ classes that catch music events and create the appropriate grobs for display on the page. Though the majority of engravers are responsible for the creation of a single grob, in some cases (e.g. New_fingering_engraver), several different grobs may be created.

Engravers listen for events and acknowledge grobs. Events are passed to the engraver in time-step order during the iteration phase. Grobs are made available to the engraver when they are created by other engravers during the iteration phase.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.11.1 Useful methods for information processing

An engraver inherits the following public methods from the Translator base class, which can be used to process listened events and acknowledged grobs:

These methods are listed in order of translation time, with initialize () and finalize () bookending the whole process. initialize () can be used for one-time initialization of context properties before translation starts, whereas finalize () is often used to tie up loose ends at the end of translation: for example, an unterminated spanner might be completed automatically or reported with a warning message.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.11.2 Translation process

At each timestep in the music, translation proceeds by calling the following methods in turn:

start_translation_timestep () is called before any user information enters the translators, i.e., no property operations (\set, \override, etc.) or events have been processed yet.

process_music () and process_acknowledged () are called after all events in the current time step have been heard, or all grobs in the current time step have been acknowledged. The latter tends to be used exclusively with engravers which only acknowledge grobs, whereas the former is the default method for main processing within engravers.

stop_translation_timestep () is called after all user information has been processed prior to beginning the translation for the next timestep.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.11.3 Preventing garbage collection for SCM member variables

In certain cases, an engraver might need to ensure private Scheme variables (with type SCM) do not get swept away by Guile’s garbage collector: for example, a cache of the previous key signature which must persist between timesteps. The method virtual derived_mark () const can be used in such cases:

 
Engraver_name::derived_mark ()
{
  scm_gc_mark (private_scm_member_)
}

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.11.4 Listening to music events

External interfaces to the engraver are implemented by protected macros including one or more of the following:

where event_name is the type of event required to provide the input the engraver needs and Engraver_name is the name of the engraver.

Following declaration of a listener, the method is implemented as follows:

 
IMPLEMENT_TRANSLATOR_LISTENER (Engraver_name, event_name)
void
Engraver_name::listen_event_name (Stream event *event)
{
  ...body of listener method...
}

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.11.5 Acknowledging grobs

Some engravers also need information from grobs as they are created and as they terminate. The mechanism and methods to obtain this information are set up by the macros:

where grob_interface is an interface supported by the grob(s) which should be acknowledged. For example, the following code would declare acknowledgers for a NoteHead grob (via the note-head-interface) and any grobs which support the side-position-interface:

 
DECLARE_ACKNOWLEDGER (note_head)
DECLARE_ACKNOWLEDGER (side_position)

The DECLARE_END_ACKNOWLEDGER () macro sets up a spanner-specific acknowledger which will be called whenever a spanner ends.

Following declaration of an acknowledger, the method is coded as follows:

 
void
Engraver_name::acknowledge_interface_name (Grob_info info)
{
  ...body of acknowledger method...
}

Acknowledge functions are called in the order engravers are \consist-ed (the only exception is if you set must-be-last to #t).

There will always be a call to process-acknowledged () whenever grobs have been created, and reading stuff from grobs should be delayed until then since other acknowledgers might write stuff into a grob even after your acknowledger has been called. So the basic workflow is to use the various acknowledgers to record the grobs you are interested in and write stuff into them (or do read/write stuff that more or less is accumulative and/or really unrelated to other engravers), and then use the process-acknowledged () hook for processing (including reading) the grobs you had recorded.

You can create new grobs in process-acknowledged (). That will lead to a new cycle of acknowledger () calls followed by a new cycle of process-acknowledged () calls.

Only when all those cycles are over is stop-translator-timestep () called, and then creating grobs is no longer an option. You can still ‘process’ parts of the grob there (if that means just reading out properties and possibly setting context properties based on them) but stop-translation-timestep () is a cleanup hook, and other engravers might have already cleaned up stuff you might have wanted to use. Creating grobs in there is not possible since engravers and other code may no longer be in a state where they could process them, possibly causing a crash.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.11.6 Engraver declaration/documentation

An engraver must have a public macro

where Engraver_name is the name of the engraver. This defines the common variables and methods used by every engraver.

At the end of the engraver file, one or both of the following macros are generally called to document the engraver in the Internals Reference:

where Engraver_name is the name of the engraver, grob_interface is the name of the interface that will be acknowledged, Engraver_doc is a docstring for the engraver, Engraver_creates is the set of grobs created by the engraver, Engraver_reads is the set of properties read by the engraver, and Engraver_writes is the set of properties written by the engraver.

The ADD_ACKNOWLEDGER and ADD_TRANSLATOR macros use a non-standard indentation system. Each interface, grob, read property, and write property is on its own line, and the closing parenthesis and semicolon for the macro all occupy a separate line beneath the final interface or write property. See existing engraver files for more information.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated by gub on July 13, 2020 using texi2html 1.82.