+2006-07-14 Erik Sandberg <mandolaerik@gmail.com>
+
+ * lily/music.cc: Revised MusicEvent event class. It now contains
+ event data directly instead of encapsulating it in music. The
+ previously used class is renamed to OldMusicEvent.
+
+ * lily/stream-event.cc: Stream events are now probs.
+
+ * lily/translator-group.cc, lily/translator.cc: Translators can
+ now listen directly to stream events, by using macros
+ [DECLARE,IMPLEMENT]_TRANSLATOR_LISTENER.
+
+ * lily/arpeggio-engraver.cc: Converted to use new event system
+
+ * THANKS: Corrected spelling mistake.
+
2006-07-12 Graham Percival <gpermus@gmail.com>
* Documentation/user/SConscript, make/lilypond-vars.make,
2006-07-12 Han-Wen Nienhuys <hanwen@lilypond.org>
- * Documentation/topdocs/NEWS.tely (Top): update prop value
+ * Documentation/topdocs/NEWS.tely (Top): update prop value
* Documentation/user/basic-notation.itely (Tuplets): new property
value.
Paul Scott
Ralph Little
Richard Schoeller
-Robert Vlatasy
+Robert Vlasaty
Roman Kurakin
Russell Lang
Scott Russell
Pedro Kroger
Ray McKinney
Reuben Thomas
-Robert Vlatasy
+Robert Vlasaty
Stef Epardaud
Thomas Willhalm
Thomas Scharkowski
#include "stem.hh"
#include "rhythmic-head.hh"
#include "side-position-interface.hh"
+#include "stream-event.hh"
#include "note-column.hh"
#include "translator.icc"
protected:
void process_music ();
void stop_translation_timestep ();
- virtual bool try_music (Music *);
+ DECLARE_TRANSLATOR_LISTENER (arpeggio);
private:
Item *arpeggio_;
- Music *arpeggio_event_;
+ Stream_event *arpeggio_event_;
};
Arpeggio_engraver::Arpeggio_engraver ()
arpeggio_event_ = 0;
}
-bool
-Arpeggio_engraver::try_music (Music *m)
+IMPLEMENT_TRANSLATOR_LISTENER (Arpeggio_engraver, arpeggio);
+void Arpeggio_engraver::listen_arpeggio (Stream_event *ev)
{
- if (!arpeggio_event_)
- arpeggio_event_ = m;
- return true;
+ arpeggio_event_ = ev;
+ ev->protect ();
}
void
Arpeggio_engraver::process_music ()
{
if (arpeggio_event_)
- arpeggio_ = make_item ("Arpeggio", arpeggio_event_->self_scm ());
+ {
+ arpeggio_ = make_item ("Arpeggio", arpeggio_event_->self_scm ());
+ arpeggio_event_->unprotect ();
+ }
}
void
arpeggio_event_ = 0;
}
-ADD_ACKNOWLEDGER (Arpeggio_engraver, stem)
- ADD_ACKNOWLEDGER (Arpeggio_engraver, rhythmic_head)
- ADD_ACKNOWLEDGER (Arpeggio_engraver, note_column)
+ADD_ACKNOWLEDGER (Arpeggio_engraver, stem);
+ADD_ACKNOWLEDGER (Arpeggio_engraver, rhythmic_head);
+ADD_ACKNOWLEDGER (Arpeggio_engraver, note_column);
- ADD_TRANSLATOR (Arpeggio_engraver,
- /* doc */ "Generate an Arpeggio symbol",
- /* create */ "Arpeggio",
- /* accept */ "arpeggio-event",
- /* read */ "",
- /* write */ "");
+ADD_TRANSLATOR (Arpeggio_engraver,
+ /* doc */ "Generate an Arpeggio symbol",
+ /* create */ "Arpeggio",
+ /* accept */ "arpeggio-event",
+ /* read */ "",
+ /* write */ "");
#include "stream-event.hh"
#include "warn.hh"
-// ES todo: move to lily-guile.hh
-SCM appendable_list ();
-void appendable_list_append (SCM l, SCM elt);
-
IMPLEMENT_SMOBS (Dispatcher);
IMPLEMENT_TYPE_P (Dispatcher, "dispatcher");
IMPLEMENT_DEFAULT_EQUAL_P (Dispatcher);
SCM class_list = scm_call_1 (ly_lily_module_constant ("ly:make-event-class"), class_symbol);
if (!scm_is_pair (class_list))
{
- ev->origin ()->warning (_f ("Unknown event class %s", ly_symbol2string (class_symbol).c_str ()));
+ // TODO: Re-enable this warning when the translator cleanup is finished
+ //ev->origin ()->warning (_f ("Unknown event class %s", ly_symbol2string (class_symbol).c_str ()));
return;
}
bool sent = false;
#include "output-def.hh"
#include "pointer-group-interface.hh"
#include "stencil.hh"
+#include "stream-event.hh"
#include "system.hh"
#include "warn.hh"
if (Music *m = unsmob_music (cause))
m->origin ()->warning (s);
+ else if (Stream_event *ev = unsmob_stream_event (cause))
+ ev->origin ()->warning (s);
else
::warning (s);
}
if (Music *m = unsmob_music (cause))
m->origin ()->message (s);
+ else if (Stream_event *ev = unsmob_stream_event (cause))
+ ev->origin ()->warning (s);
else
::message (s);
}
public:
Listener (const void *target, Listener_function_table *type);
Listener (Listener const &other);
+ Listener ();
+
void listen (SCM ev) const;
bool operator == (Listener const &other) const
};
DECLARE_UNSMOB (Listener, listener);
-#define IMPLEMENT_LISTENER(cl, method) \
-void \
-cl :: method ## _callback (void *self, SCM ev) \
-{ \
- cl *s = (cl *)self; \
- s->method (ev); \
-} \
-void \
-cl :: method ## _mark (void *self) \
-{ \
- cl *s = (cl *)self; \
- scm_gc_mark (s->self_scm ()); \
-} \
-Listener \
-cl :: method ## _listener () const \
-{ \
- static Listener_function_table callbacks; \
- callbacks.listen_callback = &cl::method ## _callback; \
- callbacks.mark_callback = &cl::method ## _mark; \
- return Listener (this, &callbacks); \
+#define IMPLEMENT_LISTENER(cl, method) \
+void \
+cl :: method ## _callback (void *self, SCM ev) \
+{ \
+ cl *s = (cl *)self; \
+ s->method (ev); \
+} \
+void \
+cl :: method ## _mark (void *self) \
+{ \
+ cl *s = (cl *)self; \
+ scm_gc_mark (s->self_scm ()); \
+} \
+Listener \
+cl :: method ## _listener () const \
+{ \
+ static Listener_function_table callbacks; \
+ callbacks.listen_callback = &cl::method ## _callback; \
+ callbacks.mark_callback = &cl::method ## _mark; \
+ return Listener (this, &callbacks); \
}
-#define GET_LISTENER(proc) ( proc ## _listener ())
+#define GET_LISTENER(proc) proc ## _listener ()
#define DECLARE_LISTENER(name) \
inline void name (SCM); \
#include "smobs.hh"
#include "prob.hh"
-class Stream_event
+class Stream_event : public Prob
{
- void init ();
- SCM property_alist_;
- Input *origin_;
-
public:
Stream_event ();
Input *origin () const;
DECLARE_SCHEME_CALLBACK (dump, (SCM));
// todo: remove unneeded constructors
+ Stream_event (SCM event_class, SCM mutable_props);
Stream_event (SCM property_alist);
Stream_event (SCM class_name, Input *);
Stream_event (Stream_event *ev);
-
- SCM internal_get_property (SCM) const;
- void internal_set_property (SCM prop, SCM val);
-
-protected:
- DECLARE_SMOBS (Stream_event,);
};
-DECLARE_UNSMOB (Stream_event, stream_event);
+Stream_event *unsmob_stream_event (SCM);
DECLARE_TYPE_P (Stream_event);
#endif /* STREAM_EVENT_HH */
Translator_group_void_method
precomputed_self_method_bindings_[TRANSLATOR_METHOD_PRECOMPUTE_COUNT];
+ SCM protected_events_;
+
DECLARE_LISTENER (create_child_translator);
DECLARE_LISTENER (eat_event);
virtual void initialize ();
virtual void finalize ();
+ void protect_event (SCM ev);
+
void stop_translation_timestep ();
void start_translation_timestep ();
Engraver_void_function_engraver_grob_info function_;
};
+
+/*
+ Each translator class has a static list of listener records. Each
+ record makes one explains how to register one of the class's stream event
+ listeners to a context.
+*/
+typedef struct translator_listener_record {
+ Listener (*get_listener_) (void *);
+ SCM event_class_;
+ struct translator_listener_record *next_;
+} translator_listener_record;
+
#define TRANSLATOR_DECLARATIONS(NAME) \
+private: \
+ static translator_listener_record *listener_list_; \
public: \
NAME (); \
VIRTUAL_COPY_CONSTRUCTOR (Translator, NAME); \
} \
static Engraver_void_function_engraver_grob_info static_get_acknowledger (SCM sym); \
static Engraver_void_function_engraver_grob_info static_get_end_acknowledger(SCM); \
+public: \
+ virtual translator_listener_record *get_listener_list () const \
+ { \
+ return listener_list_; \
+ } \
/* end #define */
+#define DECLARE_TRANSLATOR_LISTENER(m) \
+public: \
+inline void listen_ ## m (Stream_event *); \
+/* Should be private */ \
+static void _internal_declare_ ## m (); \
+private: \
+static Listener _get_ ## m ## _listener (void *); \
+DECLARE_LISTENER (_listen_scm_ ## m);
#define DECLARE_ACKNOWLEDGER(x) public : void acknowledge_ ## x (Grob_info); protected:
#define DECLARE_END_ACKNOWLEDGER(x) public : void acknowledge_end_ ## x (Grob_info); protected:
virtual void initialize ();
virtual void finalize ();
+ /*should maybe be virtual*/
+ void connect_to_context (Context *c);
+ void disconnect_from_context (Context *c);
+
void stop_translation_timestep ();
void start_translation_timestep ();
void process_music ();
protected: // should be private.
Context *daddy_context_;
+ void protect_event (SCM ev);
virtual void derived_mark () const;
+ static void add_translator_listener (translator_listener_record **listener_list, translator_listener_record *r, Listener (*get_listener) (void *), const char *ev_class);
friend class Translator_group;
};
#ifndef TRANSLATOR_ICC
#define TRANSLATOR_ICC
+#include "listener.hh"
#include "std-vector.hh"
#include "translator.hh"
A macro to automate administration of translators.
*/
#define ADD_THIS_TRANSLATOR(T) \
+ translator_listener_record *T::listener_list_; \
SCM T::static_description_ = SCM_EOL; \
static void _ ## T ## _adder () \
{ \
} \
ADD_SCM_INIT_FUNC (CLASS ## NAME ## _end_ack_adder_initclass, CLASS ## NAME ## _end_ack_adder);
+/*
+ Implement the method cl::listen_##m, and make it listen to stream
+ events of class m.
+ */
+#define IMPLEMENT_TRANSLATOR_LISTENER(cl, m) \
+void \
+cl :: _internal_declare_ ## m () \
+{ \
+ static translator_listener_record r; \
+ add_translator_listener (&listener_list_, &r, _get_ ## m ## _listener, #m); \
+} \
+ \
+ADD_SCM_INIT_FUNC (cl ## _declare_event_ ## m, cl::_internal_declare_ ## m); \
+ \
+Listener \
+cl :: _get_ ## m ## _listener (void *me) \
+{ \
+ cl *obj = (cl *) me; \
+ return obj->GET_LISTENER (_listen_scm_ ## m); \
+} \
+ \
+IMPLEMENT_LISTENER (cl, _listen_scm_ ## m) \
+void \
+cl::_listen_scm_ ## m (SCM sev) \
+{ \
+ Stream_event *ev = unsmob_stream_event (sev); \
+ protect_event (sev); \
+ listen_ ## m (ev); \
+}
#endif /* TRANSLATOR_ICC */
-
#include "ly-smobs.icc"
#include "warn.hh"
+Listener::Listener ()
+{
+ target_ = 0;
+ type_ = 0;
+}
+
Listener::Listener (const void *target, Listener_function_table *type)
{
target_ = (void *)target;
Listener::mark_smob (SCM sm)
{
Listener *me = (Listener *) SCM_CELL_WORD_1 (sm);
- (me->type_->mark_callback) (me->target_);
+ if (me->type_)
+ (me->type_->mark_callback) (me->target_);
return SCM_EOL;
}
int
-Listener::print_smob (SCM s, SCM p, scm_print_state*)
+Listener::print_smob (SCM, SCM p, scm_print_state*)
{
scm_puts ("#<Listener>", p);
return 1;
{
if (music_context_)
{
- music_context_->event_source()->remove_listener (GET_LISTENER (set_busy), ly_symbol2scm ("MusicEvent"));
+ music_context_->event_source()->remove_listener (GET_LISTENER (set_busy), ly_symbol2scm ("OldMusicEvent"));
lyrics_context_->unset_property (ly_symbol2scm ("associatedVoiceContext"));
}
music_context_ = to;
if (to)
{
- to->event_source()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("MusicEvent"));
+ to->event_source()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("OldMusicEvent"));
lyrics_context_->set_property ("associatedVoiceContext", to->self_scm ());
}
}
#include "warn.hh"
/*
- Music is anything that has duration and supports both time compression
- and transposition.
+ Music is anything that has (possibly zero) duration and supports
+ both time compression and transposition.
In Lily, everything that can be thought to have a length and a pitch
- (which has a duration which can be transposed) is considered "music",
+ (which has a duration which can be transposed) is considered "music".
*/
bool
Music::internal_is_music_type (SCM k) const
void
Music::send_to_context (Context *c)
{
- send_stream_event (c, "MusicEvent", origin (),
+ /*
+ TODO: This is a work-in-progress solution. Send the event so it
+ can be read both by old-style translators and the new ones.
+ */
+ send_stream_event (c, "OldMusicEvent", origin (),
ly_symbol2scm("music"), self_scm (), 0);
+
+ /* UGH. This is a temp hack for Music->Stream_event transition */
+ SCM orig_sym = get_property ("name");
+ char out[200];
+ string in = ly_symbol2string (orig_sym);
+ /* don't add '-' before first character */
+ out[0] = tolower (in[0]);
+ size_t outpos = 1;
+ for (size_t inpos = 1; inpos < in.size () && outpos < 190; inpos++)
+ {
+ if (isupper (in[inpos]))
+ out[outpos++] = '-';
+ out[outpos++] = tolower (in[inpos]);
+ }
+ out[outpos] = 0;
+ SCM class_name = ly_symbol2scm (out);
+
+ Stream_event *e = new Stream_event (class_name, mutable_property_alist_);
+ c->event_source ()->broadcast (e);
}
Music *
Context *contexts[] = {one, two, solo_tr, tr, 0};
for (int i = 0; contexts[i]; i++)
{
- contexts[i]->event_source ()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("MusicEvent"));
+ contexts[i]->event_source ()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("OldMusicEvent"));
}
for (char const **p = syms; *p; p++)
#include "input.hh"
#include "input-smob.hh"
-Stream_event::~Stream_event ()
+Stream_event::Stream_event ()
+ : Prob (ly_symbol2scm ("Stream_event"), SCM_EOL)
{
}
-void
-Stream_event::init ()
+Stream_event::Stream_event (SCM event_class, SCM mutable_props)
+ : Prob (ly_symbol2scm ("Stream_event"),
+ scm_list_1 (scm_cons (ly_symbol2scm ("class"), event_class)))
{
- self_scm_ = SCM_EOL;
- property_alist_ = SCM_EOL;
-
- smobify_self ();
-}
-
-Stream_event::Stream_event ()
-{
- init ();
+ mutable_property_alist_ = mutable_props;
}
Stream_event::Stream_event (SCM property_alist)
+ : Prob (ly_symbol2scm ("Stream_event"), SCM_EOL)
{
- init ();
- property_alist_ = property_alist;
+ mutable_property_alist_ = property_alist;
}
-/*
- Hm. Perhaps Stream_event should be a prob, with class_name as an
- immutable property?
- */
Stream_event::Stream_event (SCM class_name, Input *origin)
+ : Prob (ly_symbol2scm ("Stream_event"),
+ scm_list_1 (scm_cons (ly_symbol2scm ("class"), class_name)))
{
- init ();
- set_property ("class", class_name);
if (origin)
set_spot (origin);
}
Stream_event::Stream_event (Stream_event *ev)
+ : Prob (ly_symbol2scm ("Stream_event"), SCM_EOL)
{
- init ();
- property_alist_ = scm_copy_tree (ev->property_alist_);
+ mutable_property_alist_ = scm_copy_tree (ev->mutable_property_alist_);
+ immutable_property_alist_ = ev->immutable_property_alist_;
}
Input *
set_property ("origin", make_input (*i));
}
-SCM
-Stream_event::mark_smob (SCM sm)
-{
- Stream_event *me = (Stream_event *) SCM_CELL_WORD_1 (sm);
- return me->property_alist_;
-}
-
-int
-Stream_event::print_smob (SCM s, SCM port, scm_print_state *)
-{
- scm_puts ("#<Stream_event ", port);
- scm_write (dump (s), port);
- scm_puts (" >", port);
- return 1;
-}
-
-IMPLEMENT_SMOBS (Stream_event);
-IMPLEMENT_DEFAULT_EQUAL_P (Stream_event);
IMPLEMENT_TYPE_P (Stream_event, "ly:stream-event?");
MAKE_SCHEME_CALLBACK (Stream_event, undump, 1);
{
Stream_event *ev = unsmob_stream_event (self);
// Reversed alists look prettier.
- return scm_reverse (ev->property_alist_);
+ return scm_cons (scm_reverse (ev->immutable_property_alist_),
+ scm_reverse (ev->mutable_property_alist_));
}
SCM
Stream_event::undump (SCM data)
{
Stream_event *obj = new Stream_event ();
- obj->property_alist_ = scm_reverse (data);
+ obj->immutable_property_alist_ = scm_reverse (scm_car (data));
+ obj->mutable_property_alist_ = scm_reverse (scm_cdr (data));
return obj->unprotect ();
}
-SCM
-Stream_event::internal_get_property (SCM sym) const
-{
- SCM s = scm_sloppy_assq (sym, property_alist_);
- if (s != SCM_BOOL_F)
- return scm_cdr (s);
- return SCM_EOL;
-}
-
-void
-Stream_event::internal_set_property (SCM prop, SCM val)
+Stream_event *
+unsmob_stream_event (SCM m)
{
- property_alist_ = scm_assq_set_x (property_alist_, prop, val);
+ return dynamic_cast<Stream_event*> (unsmob_prob (m));
}
programming_error ("translator group is already connected to a context");
context_ = c;
c->event_source ()->add_listener (GET_LISTENER (eat_event),
- ly_symbol2scm ("MusicEvent"));
+ ly_symbol2scm ("OldMusicEvent"));
c->event_source ()->add_listener (GET_LISTENER (create_child_translator),
ly_symbol2scm ("AnnounceNewContext"));
+ for (SCM tr_list = simple_trans_list_; scm_is_pair (tr_list); tr_list = scm_cdr (tr_list))
+ {
+ Translator *tr = unsmob_translator (scm_car (tr_list));
+ tr->connect_to_context (c);
+ }
}
void
Translator_group::disconnect_from_context ()
{
+ for (SCM tr_list = simple_trans_list_; scm_is_pair (tr_list); tr_list = scm_cdr (tr_list))
+ {
+ Translator *tr = unsmob_translator (scm_car (tr_list));
+ tr->disconnect_from_context (context_);
+ }
context_->event_source ()->remove_listener (GET_LISTENER (eat_event),
- ly_symbol2scm ("MusicEvent"));
+ ly_symbol2scm ("OldMusicEvent"));
context_->event_source ()->remove_listener (GET_LISTENER (create_child_translator),
ly_symbol2scm ("AnnounceNewContext"));
context_ = 0;
+ protected_events_ = SCM_EOL;
}
void
return ell;
}
+/*
+ Protects the parameter from being garbage collected. The object is
+ protected until the next disconnect_from_context call.
+
+ Whenever a child translator hears an event, the event is added to
+ this list. This eliminates the need for derived_mark methods in most
+ translators; all incoming events are instead protected by the
+ translator group.
+
+ TODO: Should the list also be flushed at the beginning of each new
+ moment?
+ */
+void
+Translator_group::protect_event (SCM ev)
+{
+ protected_events_ = scm_cons (ev, protected_events_);
+}
+
/*
Create a new translator for a newly created child context. Triggered
by AnnounceNewContext events.
}
}
- g->simple_trans_list_ = trans_list;
-
/* Filter unwanted translator types. Required to make
\with {\consists "..."} work. */
if (dynamic_cast<Engraver_group *> (g))
- g->simple_trans_list_ = filter_performers (g->simple_trans_list_);
+ g->simple_trans_list_ = filter_performers (trans_list);
else if (dynamic_cast<Performer_group *> (g))
- g->simple_trans_list_ = filter_engravers (g->simple_trans_list_);
+ g->simple_trans_list_ = filter_engravers (trans_list);
// TODO: scrap Context::implementation
new_context->implementation_ = g;
if (p->get_parent_context())
// ES todo: Make Translators listeners directly instead.
return p->implementation ()->try_music (m);
+ // 'junking event' warning is temporarily disabled during translator cleanup
+ /*
else
// We have tried all possible contexts. Give up.
m->origin ()->warning (_f ("junking event: `%s'", m->name ()));
+ */
return false;
}
{
simple_trans_list_ = SCM_EOL;
accept_hash_table_ = SCM_EOL;
+ protected_events_ = SCM_EOL;
context_ = 0;
smobify_self ();
me->derived_mark ();
scm_gc_mark (me->accept_hash_table_);
+ scm_gc_mark (me->protected_events_);
return me->simple_trans_list_;
}
#include "warn.hh"
#include "translator-group.hh"
#include "context-def.hh"
+#include "dispatcher.hh"
#include "global-context.hh"
#include "translator.icc"
return daddy_context_->implementation ();
}
+void
+Translator::protect_event (SCM ev)
+{
+ get_daddy_translator ()->protect_event (ev);
+}
+
SCM
Translator::internal_get_property (SCM sym) const
{
{
}
+void
+Translator::connect_to_context (Context *c)
+{
+ for (translator_listener_record *r = get_listener_list (); r; r=r->next_)
+ c->events_below ()->add_listener (r->get_listener_ (this), r->event_class_);
+}
+
+void
+Translator::disconnect_from_context (Context *c)
+{
+ for (translator_listener_record *r = get_listener_list (); r; r=r->next_)
+ c->events_below ()->remove_listener (r->get_listener_ (this), r->event_class_);
+}
+
+/*
+ Internally called once, statically, for each translator
+ listener. Connects the name of an event class with a procedure that
+ fetches the corresponding listener.
+
+ The method should only be called from the macro
+ IMPLEMENT_TRANSLATOR_LISTENER.
+ */
+void
+Translator::add_translator_listener (translator_listener_record **listener_list,
+ translator_listener_record *r,
+ Listener (*get_listener) (void *),
+ const char *ev_class)
+{
+ /* ev_class is the C++ identifier name. Convert to scm symbol */
+ string name = string (ev_class);
+ name = replace_all (name, '_', '-');
+ name = name + "-event";
+ /* It's OK to use scm_gc_protect_object for protection, because r is
+ statically allocated. */
+ r->event_class_ = scm_gc_protect_object (scm_str2symbol (name.c_str ()));
+ r->get_listener_ = get_listener;
+ r->next_ = *listener_list;
+ *listener_list = r;
+}
+
/*
SMOBS
*/
;; Event class hierarchy. Each line is on the form ((List of children) . Parent)
(define event-classes
'(((StreamEvent) . '())
- ((RemoveContext ChangeParent Override Revert UnsetProperty SetProperty
- MusicEvent CreateContext Prepare OneTimeStep Finish) . StreamEvent)
+ ((RemoveContext ChangeParent Override Revert UnsetProperty
+ SetProperty MusicEvent OldMusicEvent CreateContext Prepare
+ OneTimeStep Finish) . StreamEvent)
+ ((arpeggio-event) . MusicEvent)
((Announcement) . '())
((AnnounceNewContext) . Announcement)
))