@end ignore
+@item
+When outputting MIDI, LilyPond will now store the @code{title}
+defined in a score's @code{\header} block (or, if there is no
+such definition on the @code{\score} level, the first such
+definition found in a @code{\header} block of the score's
+enclosing @code{\bookpart}, @code{\book}, or top-level scope)
+as the name of the MIDI sequence in the MIDI file. Optionally,
+the name of the MIDI sequence can be overridden using the new
+@code{midititle} @code{\header} field independently of
+@code{title} (for example, in case @code{title} contains markup
+code which does not render as plain text in a satisfactory way
+automatically).
+
@item
Music (and scheme and void) functions and markup commands that
just supply the final parameters to a chain of overrides, music
--- /dev/null
+\version "2.19.25"
+
+\header {
+ texidoc="If a score has a @code{\header} block which defines a title,
+ this title should override any title defined in a @code{\header} block
+ of the score's enclosing @code{\bookpart} or @code{\book} (or a title
+ defined in a top-level @code{\header} block) when naming the MIDI
+ sequence generated from the score. Otherwise, if the score has no title
+ defined, the MIDI sequence generated from the score should get named
+ using the title defined in the @code{\header} block of the nearest
+ enclosing @code{\bookpart}, @code{\book}, or top-level scope that
+ contains a title definition."
+ title = "Top-level title"
+}
+
+music = \new Staff { c1 }
+
+% Book with a title defined in a \header block, and book parts
+\book {
+ \header { title = "Book" }
+
+ % score without a \header block outside of any book part -- the MIDI
+ % sequence should get the title of the book as its name
+ \score {
+ \music
+ \layout { }
+ \midi { }
+ }
+
+ % score with a \header and a title outside of any book part -- the MIDI
+ % sequence should be named with the title from this \header block
+ \score {
+ \music
+ \header { title = "Score in a \book" }
+ \layout { }
+ \midi { }
+ }
+
+ % Book part with a \header block and a title
+ \bookpart {
+ \header { title = "Book part" }
+
+ % score without a \header block -- the MIDI sequence should get its name
+ % from the title of the enclosing book part
+ \score {
+ \music
+ \layout { }
+ \midi { }
+ }
+
+ % score with a \header (and a title) of its own -- the MIDI sequence
+ % should get its name from the title in this \header block
+ \score {
+ \music
+ \header { title = "Score in a book part (w/ a title) of a book" }
+ \layout { }
+ \midi { }
+ }
+ }
+
+ % Book part without a \header block
+ \bookpart {
+
+ % score without a \header block -- the MIDI sequence should be named
+ % using the title from the enclosing book
+ \score {
+ \music
+ \layout { }
+ \midi { }
+ }
+
+ % score with a \header block and title -- the MIDI sequence should get
+ % its name from the title in this \header block
+ \score {
+ \music
+ \header { title = "Score in a book part (w/o a title) of a book" }
+ \layout { }
+ \midi { }
+ }
+ }
+
+}
--- /dev/null
+\version "2.19.25"
+
+\header {
+ texidoc="The MIDI sequence generated from a score should get its name
+ from the title defined in the score's @code{\header} block (if any).
+ The title used for layout can be overridden for MIDI output by
+ specifying a separate @code{midititle} in the @code{\header} block.
+ If the score does not define a title of its own, and has no enclosing
+ @code{\bookpart}, @code{\book}, or top-level scope with a @code{\header}
+ block that defines a title, either, the MIDI sequence should get the
+ default name."
+}
+
+music = \new Staff { c1 }
+
+% Book without a \header block
+\book {
+
+ % score with a \header block including a title -- the MIDI sequence
+ % should get its name from this \header block
+ \score {
+ \music
+ \header { title = "Title shared between layout and MIDI" }
+ \layout { }
+ \midi { }
+ }
+
+ % score with no title, but a midititle defined in a \header block --
+ % the MIDI sequence should be named using the midititle in this \header
+ % block
+ \score {
+ \music
+ \header { midititle = "No title for layout, but a title for MIDI" }
+ \layout { }
+ \midi { }
+ }
+
+ % score with a title and a midititle defined in a \header block -- the
+ % MIDI sequence should get the midititle in this \header block as its
+ % name
+ \score {
+ \music
+ \header {
+ title = "Title for layout"
+ midititle = "Title for MIDI"
+ }
+ \layout { }
+ \midi { }
+ }
+
+ % Book part with no \header block
+ \bookpart {
+
+ % score without a \header -- the MIDI sequence should get the default
+ % name
+ \score {
+ \music
+ \layout { }
+ \midi { }
+ }
+ }
+
+}
Music_output *output = unsmob<Music_output> (scm_car (outputs));
if (Performance *perf = dynamic_cast<Performance *> (output))
- output_paper_book->add_performance (perf->self_scm ());
+ {
+ output_paper_book->add_performance (perf->self_scm ());
+ // Associate the performance with a \header block (if there is
+ // one in effect in the scope of the current score), to make the
+ // header metadata accessible when outputting the performance.
+ if (ly_is_module (score->get_header ()))
+ perf->set_header (score->get_header ());
+ else if (ly_is_module (output_paper_book->header_))
+ perf->set_header (output_paper_book->header_);
+ else if (ly_is_module (output_paper_book->header_0_))
+ perf->set_header (output_paper_book->header_0_);
+ }
else if (Paper_score *pscore = dynamic_cast<Paper_score *> (output))
{
if (ly_is_module (score->get_header ()))
void
Control_track_performer::initialize ()
{
- control_track_ = new Audio_staff;
+ control_track_ = new Audio_control_track_staff;
announce_element (Audio_element_info (control_track_, 0));
string id_string = String_convert::pad_to (gnu_lilypond_version_string (), 30);
+ // The first audio element in the control track is a placeholder for the
+ // name of the MIDI sequence. The actual name is stored in the element
+ // later before outputting the track (in Performance::output, see
+ // performance.cc).
add_text (Audio_text::TRACK_NAME, "control track");
add_text (Audio_text::TEXT, "creator: ");
add_text (Audio_text::TEXT, id_string);
vector<Audio_item *> audio_items_;
};
+// Subtype to identify a staff that represents the "control track" of a MIDI
+// sequence (created by Control_track_performer).
+struct Audio_control_track_staff : public Audio_staff
+{
+};
+
#endif // AUDIO_STAFF_HH
~Performance ();
DECLARE_CLASSNAME (Performance);
+ SCM get_header () const;
+ void set_header (SCM header);
+
+ virtual void derived_mark () const;
+
void add_element (Audio_element *p);
virtual void process ();
void remap_grace_durations ();
- void output (Midi_stream &midi_stream) const;
+ void output (Midi_stream &midi_stream, const string &performance_name) const;
void output_header_track (Midi_stream &midi_stream) const;
void print () const;
- void write_output (string filename) const;
+ void write_output (string filename, const string &performance_name) const;
vector<Audio_staff *> audio_staffs_;
vector<Audio_element *> audio_elements_;
Output_def *midi_;
bool ports_;
+ SCM header_;
};
#endif /* PERFORMANCE_HH */
#include "performance.hh"
+LY_DEFINE (ly_performance_header, "ly:performance-header",
+ 1, 0, 0, (SCM performance),
+ "Return header of performance.")
+{
+ LY_ASSERT_SMOB (Performance, performance, 1);
+ Performance *p = unsmob<Performance> (performance);
+ return p->get_header ();
+}
+
+LY_DEFINE (ly_performance_set_header_x, "ly:performance-set-header!",
+ 2, 0, 0, (SCM performance, SCM module),
+ "Set the performance header.")
+{
+ LY_ASSERT_SMOB (Performance, performance, 1);
+ SCM_ASSERT_TYPE (ly_is_module (module), module, SCM_ARG2, __FUNCTION__,
+ "module");
+
+ Performance *p = unsmob<Performance> (performance);
+ p->set_header (module);
+ return SCM_UNSPECIFIED;
+}
+
LY_DEFINE (ly_performance_write, "ly:performance-write",
- 2, 0, 0, (SCM performance, SCM filename),
- "Write @var{performance} to @var{filename}.")
+ 3, 0, 0, (SCM performance, SCM filename, SCM name),
+ "Write @var{performance} to @var{filename} storing @var{name} as "
+ "the name of the performance in the file metadata.")
{
LY_ASSERT_SMOB (Performance, performance, 1);
LY_ASSERT_TYPE (scm_is_string, filename, 2);
+ LY_ASSERT_TYPE (scm_is_string, name, 3);
- unsmob<Performance> (performance)->write_output (ly_scm2string (filename));
+ unsmob<Performance> (performance)->write_output (ly_scm2string (filename),
+ ly_scm2string (name));
return SCM_UNSPECIFIED;
}
-
Performance::Performance (bool ports)
: midi_ (0),
- ports_ (ports)
+ ports_ (ports),
+ header_ (SCM_EOL)
{
}
}
void
-Performance::output (Midi_stream &midi_stream) const
+Performance::derived_mark () const
+{
+ scm_gc_mark (header_);
+}
+
+SCM
+Performance::get_header () const
+{
+ return header_;
+}
+
+void
+Performance::set_header (SCM module)
+{
+ assert (ly_is_module (module));
+ header_ = module;
+}
+
+void
+Performance::output (Midi_stream &midi_stream,
+ const string &performance_name) const
{
int tracks_ = audio_staffs_.size ();
for (vsize i = 0; i < audio_staffs_.size (); i++)
{
Audio_staff *s = audio_staffs_[i];
+ if (Audio_control_track_staff *c =
+ dynamic_cast<Audio_control_track_staff *>(s))
+ {
+ // The control track, created by Control_track_performer, should
+ // contain a placeholder for the name of the MIDI sequence as its
+ // initial audio element. Fill in the name of the sequence to
+ // this element before outputting MIDI.
+ assert (!c->audio_items_.empty ());
+ Audio_text *text =
+ dynamic_cast<Audio_text *>(c->audio_items_.front ());
+ assert (text != 0);
+ assert (text->type_ == Audio_text::TRACK_NAME);
+ assert (text->text_string_ == "control track");
+ text->text_string_ = performance_name;
+ }
debug_output ("[" + ::to_string (i), true);
s->output (midi_stream, i, ports_, moment_to_ticks (start_mom));
debug_output ("]", false);
}
void
-Performance::write_output (string out) const
+Performance::write_output (string out, const string &performance_name) const
{
if (out == "-")
out = "lelie.midi";
Midi_stream midi_stream (out);
message (_f ("MIDI output to `%s'...", out));
- output (midi_stream);
+ output (midi_stream, performance_name);
progress_indication ("\n");
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
+;;; Adapted from the handle-metadata function in framework-ps.scm
+(define (performance-name-from-header header)
+ (define (metadata-lookup-output overridevar fallbackvar)
+ (let* ((overrideval (ly:modules-lookup (list header) overridevar))
+ (fallbackval (ly:modules-lookup (list header) fallbackvar))
+ (val (if overrideval overrideval fallbackval)))
+ (if val (ly:encode-string-for-pdf (markup->string val)) "")))
+ (if (null? header)
+ ""
+ (metadata-lookup-output 'midititle 'title)))
+
(define-public (write-performances-midis performances basename . rest)
(let ((midi-ext (ly:get-option 'midi-extension)))
(let
((perfs performances)
(count (if (null? rest) 0 (car rest))))
(if (pair? perfs)
- (begin
+ (let ((perf (car perfs)))
(ly:performance-write
- (car perfs)
+ perf
(if (> count 0)
(format #f "~a-~a.~a" basename count midi-ext)
- (format #f "~a.~a" basename midi-ext)))
+ (format #f "~a.~a" basename midi-ext))
+ (performance-name-from-header (ly:performance-header perf)))
(loop (cdr perfs) (1+ count)))))))