Hi Johan (and list),
Last night our little girl kept me awake again, so i
took the oppertunity to teach her a bit about chords.
I've made a version-0 attempt at reading and typsetting
chord names, see input/test/chords.ly for an example.
Don't worry about the red-tape '@' and ';', just a
temporary hack.
The code that generates the chord names is rather crude
and broken, but it shows essentially how it can be done.
At each moment in time the Chord_name_engraver builds an
array of pitches. From this array a chord name must be
generated (tex(t)-only is the easiest, as in this example,
but it's trivial to make a nice class Chord_item, and
have the Chord_name_engraver use that. Engravers and
Items usually come in pairs, e.g. Beam_engraver/Beam etc.)
Have a look at the routine that generates the chord names,
perhaps you'ld like to have a go at perl->c++ translation?
lily/chord-name-engraver.cc:
[..]
void
Chord_name_engraver::do_process_requests ()
Of course, we should support different types of chord names,
perhaps switched using a property (chordNameStyle). Or
even scheme code, some time later.
Have Fun,
Jan.
Generated by [EMAIL PROTECTED] using package-diff 0.62,
>From = lilypond-1.1.10, To = lilypond-1.1.10.jcn1
usage
cd lilypond-source-dir; patch -E -p1 < lilypond-1.1.10.jcn1.diff
Patches do not contain automatically generated files
or (urg) empty directories,
i.e., you should rerun autoconf, configure
and possibly make outdirs.
--state
1.1.10
1.1.10.jcn1
++state
diff -urN ../lilypond-1.1.10/NEWS ./NEWS
--- ../lilypond-1.1.10/NEWS Thu Dec 3 16:34:26 1998
+++ ./NEWS Fri Dec 4 02:41:42 1998
@@ -1,3 +1,8 @@
+pl 10.jcn1
+ - input/test/chords.ly
+ - \type ChordNames and chord-name-engraver.*
+ - chords mode: \chords { <c e g> @c; @d7; }
+
pl 10
pl 9.jcn3
diff -urN ../lilypond-1.1.10/VERSION ./VERSION
--- ../lilypond-1.1.10/VERSION Thu Dec 3 16:34:26 1998
+++ ./VERSION Thu Dec 3 19:25:55 1998
@@ -2,7 +2,7 @@
MAJOR_VERSION=1
MINOR_VERSION=1
PATCH_LEVEL=10
-MY_PATCH_LEVEL=
+MY_PATCH_LEVEL=jcn1
# use the above to send patches: MY_PATCH_LEVEL is always empty for a
# released version.
diff -urN ../lilypond-1.1.10/init/engraver.ly ./init/engraver.ly
--- ../lilypond-1.1.10/init/engraver.ly Thu Dec 3 16:34:26 1998
+++ ./init/engraver.ly Fri Dec 4 02:08:02 1998
@@ -68,6 +68,7 @@
\accepts "RhythmicStaff";
\accepts "GrandStaff";
\accepts "Lyrics";
+ \accepts "ChordNames";
}
@@ -132,6 +133,7 @@
\accepts "RhythmicStaff";
\accepts "GrandStaff";
\accepts "Lyrics";
+ \accepts "ChordNames";
}
\translator{
@@ -152,6 +154,21 @@
\accepts "LyricVoice";
}
+\translator{
+ \type "Line_group_engraver_group";
+
+ \name ChordNameVoice ;
+ \consists "Separating_line_group_engraver";
+ \consists "Chord_name_engraver";
+}
+
+\translator {
+ \type "Line_group_engraver_group";
+ \name ChordNames;
+ \consists "Vertical_align_engraver";
+ \accepts "ChordNameVoice";
+}
+
ScoreContext = \translator {
\type Score_engraver;
\name Score;
@@ -169,6 +186,7 @@
\accepts "Staff";
\accepts "RhythmicStaff";
\accepts "Lyrics";
+ \accepts "ChordNames";
\accepts "GrandStaff";
\accepts "ChoirStaff";
};
@@ -259,5 +277,6 @@
\accepts "Staff";
\accepts "RhythmicStaff";
\accepts "Lyrics";
+ \accepts "ChordNames";
\accepts "GrandStaff";
};
diff -urN ../lilypond-1.1.10/input/test/chords.ly ./input/test/chords.ly
--- ../lilypond-1.1.10/input/test/chords.ly Thu Jan 1 01:00:00 1970
+++ ./input/test/chords.ly Fri Dec 4 02:50:56 1998
@@ -0,0 +1,27 @@
+%{
+Would this be acceptable/good enough/convenient for entry?
+
+ Convention/Standard Logical/Lily(?)
+
+ C# cis
+ Cb ces
+ Cm/Cmin c3-
+ Caug c5+
+ Cdim c5-
+ Cmaj7 c7
+ C7 c7-
+ Csus/Csus4 c4^3
+%}
+
+scales = \notes\transpose c''\chords{
+ @c; @g; @d; @a; @e; @b; @fis;
+ @c; @f; @bes; @es; @as; @des; @ges;
+ @c6; @c7; @c9; @c11; @c13;
+ }
+
+\score{
+ <
+ \type ChordNames \scales
+ \type Staff \scales
+ >
+}
diff -urN ../lilypond-1.1.10/lily/chord-name-engraver.cc
./lily/chord-name-engraver.cc
--- ../lilypond-1.1.10/lily/chord-name-engraver.cc Thu Jan 1
01:00:00 1970
+++ ./lily/chord-name-engraver.cc Fri Dec 4 02:52:40 1998
@@ -0,0 +1,92 @@
+/*
+ chord-name-engraver.cc -- implement Chord_name_engraver
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 1998 Jan Nieuwenhuizen <[EMAIL PROTECTED]>
+*/
+
+#include "chord-name-engraver.hh"
+#include "musical-request.hh"
+#include "text-item.hh"
+#include "paper-def.hh"
+#include "lookup.hh"
+#include "paper-def.hh"
+#include "main.hh"
+#include "dimensions.hh"
+
+ADD_THIS_TRANSLATOR (Chord_name_engraver);
+
+Chord_name_engraver::Chord_name_engraver ()
+{
+}
+
+void
+Chord_name_engraver::acknowledge_element (Score_element_info i)
+{
+ if (Note_req* n = dynamic_cast<Note_req*> (i.req_l_))
+ pitch_arr_.push (n->pitch_);
+}
+
+bool
+Chord_name_engraver::do_try_music (Music* m)
+{
+ if (Note_req* n = dynamic_cast<Note_req*> (m))
+ {
+ pitch_arr_.push (n->pitch_);
+ return true;
+ }
+ return false;
+}
+
+void
+Chord_name_engraver::do_process_requests ()
+{
+ if (text_p_arr_.size ())
+ return;
+ if (!pitch_arr_.size ())
+ return;
+
+ Scalar style = get_property ("textstyle");
+ Scalar alignment = get_property ("textalignment");
+ Text_def* text_p = new Text_def;
+ text_p->align_dir_ = LEFT;
+ if (style.length_i ())
+ text_p->style_str_ = style;
+ if (alignment.isnum_b())
+ text_p->align_dir_= (Direction)(int)alignment;
+
+ Musical_pitch tonic = pitch_arr_[0];
+ text_p->text_str_ = tonic.str ();
+ for (int i=1; i < pitch_arr_.size (); i++)
+ {
+ Musical_pitch p = pitch_arr_[i];
+ int trap = (p.notename_i_ - tonic.notename_i_ + 8) % 8 + 1;
+ if (((trap != 3) && (trap != 5)) || (p.accidental_i_))
+ {
+ text_p->text_str_ += to_str ((p.notename_i_ -
tonic.notename_i_ + 8) % 8 + 1);
+ if (p.accidental_i_)
+ text_p->text_str_ += p.accidental_i_ < 0 ? "-" : "+";
+ if (i + 1 < pitch_arr_.size ())
+ text_p->text_str_ += "/";
+ }
+ }
+
+ Text_item* item_p = new Text_item (text_p);
+ item_p->dir_ = DOWN;
+ item_p->fat_b_ = true;
+ text_p_arr_.push (item_p);
+ announce_element (Score_element_info (item_p, 0));
+}
+
+void
+Chord_name_engraver::do_pre_move_processing ()
+{
+ for (int i=0; i < text_p_arr_.size (); i++)
+ {
+ typeset_element (text_p_arr_[i]);
+ }
+ text_p_arr_.clear ();
+ pitch_arr_.clear ();
+}
+
diff -urN ../lilypond-1.1.10/lily/include/chord-name-engraver.hh
./lily/include/chord-name-engraver.hh
--- ../lilypond-1.1.10/lily/include/chord-name-engraver.hh Thu Jan
1 01:00:00 1970
+++ ./lily/include/chord-name-engraver.hh Fri Dec 4 02:47:39 1998
@@ -0,0 +1,35 @@
+/*
+ chord-name-engraver.hh -- declare Chord_name_engraver
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 1998 Jan Nieuwenhuizen <[EMAIL PROTECTED]>
+*/
+
+#ifndef CHORD_NAME_ENGRAVER_HH
+#define CHORD_NAME_ENGRAVER_HH
+
+#include "engraver.hh"
+#include "array.hh"
+#include "musical-pitch.hh"
+
+#include "lily-proto.hh"
+
+class Chord_name_engraver : public Engraver
+{
+protected:
+ virtual void do_pre_move_processing ();
+ virtual void acknowledge_element (Score_element_info i);
+ virtual void do_process_requests ();
+ virtual bool do_try_music (Music* m);
+
+public:
+ Chord_name_engraver ();
+ VIRTUAL_COPY_CONS (Translator);
+
+private:
+ Array<Musical_pitch> pitch_arr_;
+ Link_array<Text_item> text_p_arr_;
+};
+
+#endif // CHORD_NAME_ENGRAVER_HH
diff -urN ../lilypond-1.1.10/lily/include/lily-proto.hh
./lily/include/lily-proto.hh
--- ../lilypond-1.1.10/lily/include/lily-proto.hh Fri Nov 27
14:27:38 1998
+++ ./lily/include/lily-proto.hh Fri Dec 4 02:30:25 1998
@@ -46,7 +46,7 @@
struct Cadenza_req;
struct Change_iterator;
struct Change_translator;
-struct Simultaneous_music;
+struct Chord_name_engraver;
struct Clef_change_req;
struct Clef_item;
struct Clef_engraver;
@@ -186,7 +186,6 @@
struct Rhythmic_head;
struct Rhythmic_grouping_req;
struct Rhythmic_req;
-struct Single_malt_grouping_item;
struct Scope;
struct Separating_group_spanner;
struct Score;
@@ -200,6 +199,8 @@
struct Script_engraver;
struct Script_req;
struct Simple_music;
+struct Simultaneous_music;
+struct Single_malt_grouping_item;
struct Skip_req;
struct Slur;
struct Slur_engraver;
diff -urN ../lilypond-1.1.10/lily/include/lyric-engraver.hh
./lily/include/lyric-engraver.hh
--- ../lilypond-1.1.10/lily/include/lyric-engraver.hh Fri Nov 27
14:27:38 1998
+++ ./lily/include/lyric-engraver.hh Fri Dec 4 02:28:19 1998
@@ -20,7 +20,6 @@
virtual void do_pre_move_processing();
virtual bool do_try_music (Music*);
virtual void do_process_requests();
- virtual void do_post_move_processing();
public:
Lyric_engraver();
diff -urN ../lilypond-1.1.10/lily/include/my-lily-lexer.hh
./lily/include/my-lily-lexer.hh
--- ../lilypond-1.1.10/lily/include/my-lily-lexer.hh Mon Oct 12
14:10:55 1998
+++ ./lily/include/my-lily-lexer.hh Thu Dec 3 18:43:47 1998
@@ -50,6 +50,7 @@
Identifier*lookup_identifier (String s);
Musical_pitch lookup_pitch (String s);
void push_note_state();
+ void push_chord_state();
void push_lyric_state();
void pop_state();
void LexerError (char const *);
@@ -60,6 +61,7 @@
void print_declarations (bool init_b) const;
void add_notename (String, Musical_pitch);
bool note_state_b() const;
+ bool chord_state_b() const;
bool lyric_state_b() const;
};
diff -urN ../lilypond-1.1.10/lily/include/my-lily-parser.hh
./lily/include/my-lily-parser.hh
--- ../lilypond-1.1.10/lily/include/my-lily-parser.hh Mon Nov 16
23:53:57 1998
+++ ./lily/include/my-lily-parser.hh Thu Dec 3 23:18:23 1998
@@ -27,6 +27,7 @@
void add_requests (Simultaneous_music*v);
Simultaneous_music * get_note_element (Note_req * ,Duration *);
+ Simultaneous_music * get_chord (Musical_pitch, Array<Musical_pitch>*,
Array<Musical_pitch>*, Duration);
Simultaneous_music* get_rest_element (String, Duration *);
Simultaneous_music* get_word_element (String, Duration*);
Melodic_req* get_melodic_req (Melodic_req* melodic, int quotes);
diff -urN ../lilypond-1.1.10/lily/lexer.ll ./lily/lexer.ll
--- ../lilypond-1.1.10/lily/lexer.ll Mon Nov 16 23:53:57 1998
+++ ./lily/lexer.ll Thu Dec 3 23:35:13 1998
@@ -62,6 +62,7 @@
%option never-interactive
%option warn
+%x chords
%x incl
%x lyrics
%x notes
@@ -102,7 +103,7 @@
// windows-suck-suck-suck
}
-<notes,incl,INITIAL,lyrics>{
+<INITIAL,chords,incl,lyrics,notes>{
"%{" {
yy_push_state (longcomment);
}
@@ -136,11 +137,11 @@
}
-<notes,INITIAL,lyrics>\\maininput {
+<INITIAL,chords,lyrics,notes>\\maininput {
start_main_input ();
}
-<notes,INITIAL,lyrics>\\include {
+<INITIAL,chords,lyrics,notes>\\include {
yy_push_state (incl);
}
<incl>\"[^"]*\";? { /* got the include file name */
@@ -184,29 +185,27 @@
<notes>R {
return MEASURES;
}
-<INITIAL,lyrics,notes>\\\${BLACK}*{WHITE} {
+<INITIAL,chords,lyrics,notes>\\\${BLACK}*{WHITE} {
String s=YYText () + 2;
s=s.left_str (s.length_i () - 1);
return scan_escaped_word (s);
}
-<INITIAL,lyrics,notes>\${BLACK}*{WHITE} {
+<INITIAL,chords,lyrics,notes>\${BLACK}*{WHITE} {
String s=YYText () + 1;
s=s.left_str (s.length_i () - 1);
return scan_bare_word (s);
}
-<INITIAL,lyrics,notes>\\\${BLACK}* { // backup rule
+<INITIAL,chords,lyrics,notes>\\\${BLACK}* { // backup rule
cerr << _ ("white expected") << endl;
exit (1);
}
-<INITIAL,lyrics,notes>\${BLACK}* { // backup rule
+<INITIAL,chords,lyrics,notes>\${BLACK}* { // backup rule
cerr << _ ("white expected") << endl;
exit (1);
}
<notes>{
-
{ALPHAWORD} {
return scan_bare_word (YYText ());
-
}
{NOTECOMMAND} {
@@ -249,7 +248,6 @@
}
<lyrics>{
-
\" {
start_quote ();
}
@@ -281,6 +279,18 @@
return yylval.c = YYText ()[0];
}
}
+<chords>{
+ {ALPHAWORD} {
+ return scan_bare_word (YYText ());
+ }
+ {UNSIGNED} {
+ yylval.i = String_convert::dec2_i (String (YYText ()));
+ return UNSIGNED;
+ }
+ . {
+ return yylval.c = YYText ()[0];
+ }
+}
<<EOF>> {
DOUT << "<<eof>>";
@@ -364,10 +374,17 @@
}
void
+My_lily_lexer::push_chord_state ()
+{
+ yy_push_state (chords);
+}
+
+void
My_lily_lexer::push_lyric_state ()
{
yy_push_state (lyrics);
}
+
void
My_lily_lexer::pop_state ()
{
@@ -389,7 +406,7 @@
yylval.id = id;
return id->token_code_i_;
}
- if (YYSTATE != notes) {
+ if ((YYSTATE != notes) && (YYSTATE != chords)) {
if (notename_b (str))
{
yylval.pitch = new Musical_pitch (lookup_pitch
(str));
@@ -412,7 +429,7 @@
My_lily_lexer::scan_bare_word (String str)
{
DOUT << "word: `" << str<< "'\n";
- if (YYSTATE == notes){
+ if ((YYSTATE == notes) || (YYSTATE == chords)) {
if (notename_b (str)) {
DOUT << "(notename)\n";
yylval.pitch = new Musical_pitch (lookup_pitch
(str));
@@ -430,6 +447,12 @@
My_lily_lexer::note_state_b () const
{
return YY_START == notes;
+}
+
+bool
+My_lily_lexer::chord_state_b () const
+{
+ return YY_START == chords;
}
bool
diff -urN ../lilypond-1.1.10/lily/lyric-engraver.cc
./lily/lyric-engraver.cc
--- ../lilypond-1.1.10/lily/lyric-engraver.cc Fri Nov 27 14:27:38 1998
+++ ./lily/lyric-engraver.cc Fri Dec 4 02:21:35 1998
@@ -66,11 +66,6 @@
}
void
-Lyric_engraver::do_post_move_processing()
-{
-}
-
-void
Lyric_engraver::do_pre_move_processing()
{
for (int i=0; i < text_p_arr_.size (); i++)
diff -urN ../lilypond-1.1.10/lily/my-lily-lexer.cc
./lily/my-lily-lexer.cc
--- ../lilypond-1.1.10/lily/my-lily-lexer.cc Thu Dec 3 16:34:26 1998
+++ ./lily/my-lily-lexer.cc Fri Dec 4 02:34:56 1998
@@ -28,6 +28,7 @@
{"alternative", ALTERNATIVE},
{"bar", BAR},
{"cadenza", CADENZA},
+ {"chords", CHORDS},
{"clef", CLEF},
{"cm", CM_T},
{"consists", CONSISTS},
diff -urN ../lilypond-1.1.10/lily/my-lily-parser.cc
./lily/my-lily-parser.cc
--- ../lilypond-1.1.10/lily/my-lily-parser.cc Mon Nov 16 23:53:57 1998
+++ ./lily/my-lily-parser.cc Fri Dec 4 01:59:22 1998
@@ -158,6 +158,108 @@
}
Simultaneous_music *
+My_lily_parser::get_chord (Musical_pitch tonic, Array<Musical_pitch>*
add_arr_p, Array<Musical_pitch>* sub_arr_p, Duration d)
+{
+ Simultaneous_music*v = new Request_chord;
+ v->set_spot (here_input ());
+
+ Note_req* n = new Note_req;
+ n->pitch_ = tonic;
+ n->duration_ = d;
+ v->add_music (n);
+
+ for (int i = 0; i < add_arr_p->size (); i++)
+ {
+ Musical_pitch p = tonic;
+ p.transpose ((*add_arr_p)[i]);
+ (*add_arr_p)[i] = p;
+ }
+ add_arr_p->sort (Musical_pitch::compare);
+ for (int i = 0; i < sub_arr_p->size (); i++)
+ {
+ Musical_pitch p = tonic;
+ p.transpose ((*sub_arr_p)[i]);
+ (*sub_arr_p)[i] = p;
+ }
+ sub_arr_p->sort (Musical_pitch::compare);
+
+ Musical_pitch third;
+ third.notename_i_ = 2;
+
+ Musical_pitch mthird;
+ mthird.notename_i_ = 2;
+ mthird.accidental_i_ = -1;
+
+ Musical_pitch missing;
+ missing = tonic;
+ missing.transpose (third);
+
+ Musical_pitch p;
+ p = tonic;
+ p.transpose (third);
+ p.transpose (mthird);
+
+ /*
+ must have minimum at 5 (3 is added automatically as missing)
+ */
+ if (!add_arr_p->size ()
+ || ((add_arr_p->size () == 1) &&
+ ((add_arr_p->top ().notename_i_ != p.notename_i_)
+ || (add_arr_p->top () < p))))
+ add_arr_p->push (p);
+
+ Array<Musical_pitch> triads;
+ triads.push (third); // c e
+ triads.push (mthird); // d f
+ triads.push (mthird); // e g
+ triads.push (third); // f a
+ triads.push (third); // g b
+ triads.push (mthird); // a c
+ triads.push (mthird); // b d
+
+ /*
+ add missing triads
+ */
+ for (int i = 0; i < add_arr_p->size (); i++)
+ {
+ Musical_pitch p = (*add_arr_p)[i];
+ if ((p > missing) && (p.notename_i_ != missing.notename_i_))
+ while ((p > missing) && (p.notename_i_ != missing.notename_i_))
+ {
+ add_arr_p->insert (missing, i++);
+ missing.transpose (triads[(missing.notename_i_ -
tonic.notename_i_ + 8) % 8]);
+ }
+ else
+ i++;
+ }
+
+ /*
+ add all that aren't subtracted
+ */
+ for (int i = 0; i < add_arr_p->size (); i++)
+ {
+ Musical_pitch p = (*add_arr_p)[i];
+ Note_req* n = new Note_req;
+ n->pitch_ = p;
+ n->duration_ = d;
+ for (int j = 0; j < sub_arr_p->size (); j++)
+ {
+ if (p == (*sub_arr_p)[j])
+ {
+ delete n;
+ n = 0;
+ break;
+ }
+ }
+ if (n)
+ v->add_music (n);
+ }
+
+ v->set_spot (here_input ());
+ return v;
+}
+
+Simultaneous_music *
My_lily_parser::get_note_element (Note_req *rq, Duration * duration_p)
{
Simultaneous_music*v = new Request_chord;
diff -urN ../lilypond-1.1.10/lily/parser.yy ./lily/parser.yy
--- ../lilypond-1.1.10/lily/parser.yy Thu Dec 3 16:34:26 1998
+++ ./lily/parser.yy Fri Dec 4 01:14:03 1998
@@ -163,6 +163,7 @@
%token BAR
%token BEAMPLET
%token CADENZA
+%token CHORDS
%token CLEF
%token CM_T
%token CONSISTS
@@ -264,6 +265,9 @@
%type <pitch> explicit_musical_pitch steno_musical_pitch
musical_pitch absolute_musical_pitch
%type <notereq> steno_notepitch
%type <pitch_arr> pitch_list
+%type <music> chord
+%type <pitch_arr> chord_additions chord_subtractions
+%type <pitch> chord_note
%type <midi> midi_block midi_body
%type <duration> duration_length
@@ -807,7 +811,13 @@
{ $$ = $3;
THIS->lexer_p_->pop_state ();
}
-
+ | CHORDS
+ { THIS->lexer_p_->push_chord_state (); }
+ Music
+ {
+ $$ = $3;
+ THIS->lexer_p_->pop_state ();
+ }
| LYRICS
{ THIS->lexer_p_->push_lyric_state (); }
Music
@@ -1400,7 +1410,8 @@
simple_element:
steno_notepitch notemode_duration {
- if (!THIS->lexer_p_->note_state_b ())
+ if (!THIS->lexer_p_->note_state_b ()
+ && !THIS->lexer_p_->chord_state_b ())
THIS->parser_error (_ ("have to be in Note mode
for notes"));
$1->duration_ = *$2;
$$ = THIS->get_note_element ($1, $2);
@@ -1425,8 +1436,61 @@
$$ = THIS->get_word_element (*$1, $2);
delete $1;
}
+ | '@' chord ';' {
+ if (!THIS->lexer_p_->chord_state_b ())
+ THIS->parser_error (_ ("have to be in Chord mode
for chords"));
+ $$ = $2;
+ }
+ ;
+
+
+chord:
+ NOTENAME_PITCH chord_additions chord_subtractions {
+ Duration d;
+ d.durlog_i_ = 0;
+ $$ = THIS->get_chord (*$1, $2, $3, d);
+ };
+
+chord_additions:
+ {
+ $$ = new Array<Musical_pitch>;
+ }
+ | chord_additions chord_note {
+ $1->push (*$2);
+ $$ = $1;
+ }
;
+chord_note:
+ UNSIGNED {
+ $$ = new Musical_pitch;
+ $$->notename_i_ = ($1 - 1) % 8;
+ $$->octave_i_ = $1 > 7 ? 1 : 0;
+ $$->accidental_i_ = 0;
+ }
+ | UNSIGNED '+' {
+ $$ = new Musical_pitch;
+ $$->notename_i_ = ($1 - 1) % 8;
+ $$->octave_i_ = $1 > 7 ? 1 : 0;
+ $$->accidental_i_ = 1;
+ }
+ | UNSIGNED '-' {
+ $$ = new Musical_pitch;
+ $$->notename_i_ = ($1 - 1) % 8;
+ $$->octave_i_ = $1 > 7 ? 1 : 0;
+ $$->accidental_i_ = -1;
+ }
+ ;
+
+chord_subtractions:
+ {
+ $$ = new Array<Musical_pitch>;
+ }
+ | '^' chord_subtractions chord_note {
+ $2->push (*$3);
+ $$ = $2;
+ }
+ ;
/*
UTILITIES