chord-name-engraver plus capo

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

chord-name-engraver plus capo

Wols Lists
 Finally! Thanks Neil - it all worked!

So here it is, the new engraver. It could probably be tweaked to look a
bit nicer, but it's there and it works. And I need a column markup
function to finish it off :-) but here it is for your review.

The warning needs fixing - I haven't worked out why the compiler doesn't
like "origin".

All the changes are guarded by an "if (capo) {}", so it shouldn't change
*anything* unless the user has deliberately set capoFret in their .ly file.

The only other file modified is define-context-properties.scm, which
suppresses warnings rather than is critical. I'm away for the weekend,
so I'll put some effort into serious commenting later when I get the
chance. If you can give me any clues where I've guessed wrong it would
be appreciated. Enjoy :-)

-------------

/*
  This file is part of LilyPond, the GNU music typesetter.

  Copyright (C) 1998--2010 Jan Nieuwenhuizen <[hidden email]>
  Copyright (C) 2010 Anthony Youngman <[hidden email]>

  LilyPond is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  LilyPond is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>

#include "chord-name.hh"
#include "context.hh"
#include "dimensions.hh"
#include "engraver.hh"        // required for all engravers ?
#include "font-interface.hh"
#include "international.hh"    // required to translate stuff eg error
messages
#include "item.hh"
#include "output-def.hh"
#include "pitch.hh"
#include "protected-scm.hh"
#include "stream-event.hh"
#include "text-interface.hh"
#include "warn.hh"

#include "translator.icc"    // required to define the engraver as a
translator

// define the class here
class Chord_name_engraver : public Engraver // declare the engraver as
an engraver
{
  TRANSLATOR_DECLARATIONS (Chord_name_engraver);
protected:

  // functions inherited from translator base class - declare and define
as required
  // virtual void initialize();        // set up for each grob as it is
created
  // void start_translation_timestep();    // this is called for all
active grobs at the start of each timeslice ?
  void process_music ();        // called after all events have been heard
  // void process_acknowledged();    //    or grobs acknowledged
  void stop_translation_timestep ();    // called for active grobs at
the end of each timeslice
  virtual void finalize ();        // tie up any loose ends when the
grob finishes

  // other functions
  virtual void derived_mark () const;
  DECLARE_TRANSLATOR_LISTENER (note);    // tells the engraver to
process notes ?
  DECLARE_TRANSLATOR_LISTENER (rest);    // tells the engraver to
process rests ?
 
private:    // member variable names end with an underscore
  Item *chord_name_;
  vector<Stream_event*> notes_;
 
  SCM last_chord_;
  Stream_event *rest_event_;
 
  // ugh - surely there's a better way to do this ...
  Pitch capo_transpose_[11];    // store the pitches to transpose down by
};

Chord_name_engraver::Chord_name_engraver ()
// As each context is created, the engravers are attached. This
initialises the engraver.
// these variables carry context forward from note to note.
{
  chord_name_ = 0;
  last_chord_ = SCM_EOL;
  rest_event_ = 0;
 
  // assume we're starting from C and transposing down // see above -
this must be a crap way of doing it
  capo_transpose_[0] = Pitch (0, -1, 0);    // B
  capo_transpose_[1] = Pitch (0, -1, FLAT_ALTERATION);    // B flat
  capo_transpose_[2] = Pitch (0, -2, 0);    // A
  capo_transpose_[3] = Pitch (0, -2, FLAT_ALTERATION);    // A flat
  capo_transpose_[4] = Pitch (0, -3, 0);    // G
  capo_transpose_[5] = Pitch (0, -4, SHARP_ALTERATION);    // F sharp
  capo_transpose_[6] = Pitch (0, -4, 0);    // F
  capo_transpose_[7] = Pitch (0, -5, 0);    // E
  capo_transpose_[8] = Pitch (0, -5, FLAT_ALTERATION);    // E flat
  capo_transpose_[9] = Pitch (0, -6, 0);    // D
  capo_transpose_[10] = Pitch (0, -7, SHARP_ALTERATION);    // C sharp
}

// initialize()

// start_translation_timestep()

void
Chord_name_engraver::process_music ()
// This is called for each note or rest.
// It is called as a result of being registered with
IMPLEMENT_TRANSLATOR_LISTENER
{
  SCM markup;        // to store markup in
  SCM bass = SCM_EOL;    // remember notes to create chord
  SCM inversion = SCM_EOL;
  SCM pitches = SCM_EOL;

  SCM capo_markup;    // and transposed notes for capo chord
  SCM capo_bass = SCM_EOL;
  SCM capo_inversion = SCM_EOL;
  SCM capo_pitches = SCM_EOL;

  int capo = 0;        // declare capo as both flag and value
 
  if (rest_event_)
    {    // process a rest
      SCM no_chord_markup = get_property ("noChordSymbol");
      if (!Text_interface::is_markup (no_chord_markup))
        return;
      markup = no_chord_markup;
    }
  else
    {  // process a note event
      if (!notes_.size ())
        return;

      // get capo fret
      // This is set by "\set ChordNames.capoFret = #3" in the lily
source file
      // declare properties in define-context-properties.scm
      SCM capofret = get_property ("capoFret");
      if (scm_is_number (capofret))
      {
    capo = scm_to_int (capofret);
    if (capo < 0 || capo > 23)
    {
// !!! TODO !!! FIX !!!
//      origin ()->warning (_f ("invalid capo fret; must be 0 to 23,
found: \"%i\"",
//                  capo));
      capo = 0;    // set to 0 to ignore
    }
    if (capo >= 12)
      capo -= 12;    // if you can put the capo above 12th fret, we
don't care about octaves
      }
     
      Stream_event *inversion_event = 0;
      for (vsize i = 0; i < notes_.size (); i++)
      {    // step through each note in turn
        Stream_event *n = notes_[i];
        SCM p = n->get_property ("pitch");
        if (!unsmob_pitch (p))
          continue;

        if (n->get_property ("inversion") == SCM_BOOL_T)
        {
          inversion_event = n;
          inversion = p;
      if (capo)
      { // transpose the pitch // note that p here is not p above!
        Pitch *p = unsmob_pitch (n->get_property ("pitch"));    //
convert the pitch to a C++ class (Pitch)
        Pitch orig = p->transposed (capo_transpose_[capo-1]);    // Now
transpose it
        capo_inversion = orig.smobbed_copy ();            // and convert
it back to a scheme object
      }
    }
        else if (n->get_property ("bass") == SCM_BOOL_T)
    {
          bass = p;
      if (capo) {
        Pitch *p = unsmob_pitch (n->get_property ("pitch"));
        Pitch orig = p->transposed (capo_transpose_[capo-1]);
        capo_bass = orig.smobbed_copy ();
      }
    }
        else
    {
          pitches = scm_cons (p, pitches);
      if (capo) {
        Pitch *p = unsmob_pitch (n->get_property ("pitch"));
        Pitch orig = p->transposed (capo_transpose_[capo-1]);
        capo_pitches = scm_cons ( orig.smobbed_copy (), capo_pitches);
      }
    }
      }

      if (inversion_event)
      {
        SCM oct = inversion_event->get_property ("octavation");
        if (scm_is_number (oct))
        {
          Pitch *p = unsmob_pitch (inversion_event->get_property ("pitch"));
          int octavation = scm_to_int (oct);
          Pitch orig = p->transposed (Pitch (-octavation, 0, 0));

          pitches = scm_cons (orig.smobbed_copy (), pitches);
     
      // if capo sort this out later !!!
     
        }
        else
          programming_error ("inversion does not have original pitch");
      }

      pitches = scm_sort_list (pitches, Pitch::less_p_proc);
      if (capo)
    capo_pitches = scm_sort_list (capo_pitches, Pitch::less_p_proc);
   
      SCM name_proc = get_property ("chordNameFunction");
      markup = scm_call_4 (name_proc, pitches, bass, inversion,
          context ()->self_scm ());
      if (capo)
    capo_markup = scm_call_4 (name_proc, capo_pitches, capo_bass,
capo_inversion,
        context ()->self_scm ());
    }
  /*
    Ugh. // Why's this ughly?
  */
 
  // do we need any more capo stuff here beyond setting the chord name text?
 
  SCM chord_as_scm = scm_cons (pitches, scm_cons (bass, inversion));

  chord_name_ = make_item ("ChordName",
      rest_event_ ? rest_event_->self_scm () : notes_[0]->self_scm ());
  if (!capo)
    chord_name_->set_property ("text", markup);
  else
  {
    SCM capovertical = get_property ("capoVertical");
    // how do I combine text and markup to get "markup (capo_markup)" !!!
    SCM paren_proc = ly_lily_module_constant ("parenthesize-markup");
    SCM line_proc = ly_lily_module_constant ("line-markup");
    SCM hspace_proc = ly_lily_module_constant ("hspace-markup");

  SCM final_markup = scm_list_n (line_proc,
                 scm_list_3 (markup,
                         scm_list_2 (hspace_proc,
                             scm_from_int (1)),
                         scm_list_2 (paren_proc, capo_markup)),
                 SCM_UNDEFINED);

    chord_name_->set_property ("text", final_markup);
  }

  SCM chord_changes = get_property("chordChanges");
  if (to_boolean (chord_changes) && scm_is_pair (last_chord_)
      && ly_is_equal (chord_as_scm, last_chord_))
    chord_name_->set_property ("begin-of-line-visible", SCM_BOOL_T);

  last_chord_ = chord_as_scm;
}

// process_acknowledged()

void
Chord_name_engraver::stop_translation_timestep ()
{
  chord_name_ = 0;
  notes_.clear ();
  rest_event_ = 0;
}

void
Chord_name_engraver::finalize ()
{
}

void
Chord_name_engraver::derived_mark () const
{
  scm_gc_mark (last_chord_);
}



IMPLEMENT_TRANSLATOR_LISTENER (Chord_name_engraver, note);
void
Chord_name_engraver::listen_note (Stream_event *ev)
{
  notes_.push_back (ev);
}

IMPLEMENT_TRANSLATOR_LISTENER (Chord_name_engraver, rest);
void
Chord_name_engraver::listen_rest (Stream_event *ev)
{
  ASSIGN_EVENT_ONCE(rest_event_, ev);
}
 

/*
  The READs description is not strictly accurate:
  which properties are read depend on the chord naming function active.
*/
ADD_TRANSLATOR (Chord_name_engraver,
        /* doc */
        "Catch note and rest events and generate the appropriate
chordname.",

        /* create */
        "ChordName ",

        /* read */    // list of properties it reads
        "capoFret "    // added for capo
        "capoVertical "
        "chordChanges "
        "chordNameExceptions "
        "chordNameFunction "
        "chordNoteNamer "
        "chordRootNamer "
        "chordNameExceptions "
        "majorSevenSymbol "
                "noChordSymbol ",

        /* write */
        ""
        );


---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Carl Sorensen
Great job!  I've put some inline review comments below.


On 8/27/10 7:44 PM, "Wols Lists" <[hidden email]> wrote:

>  Finally! Thanks Neil - it all worked!
>
> So here it is, the new engraver. It could probably be tweaked to look a
> bit nicer, but it's there and it works. And I need a column markup
> function to finish it off :-) but here it is for your review.
>
> The warning needs fixing - I haven't worked out why the compiler doesn't
> like "origin".
>
> All the changes are guarded by an "if (capo) {}", so it shouldn't change
> *anything* unless the user has deliberately set capoFret in their .ly file.
>
> The only other file modified is define-context-properties.scm, which
> suppresses warnings rather than is critical. I'm away for the weekend,
> so I'll put some effort into serious commenting later when I get the
> chance. If you can give me any clues where I've guessed wrong it would
> be appreciated. Enjoy :-)
>
> -------------
>
> /*
>   This file is part of LilyPond, the GNU music typesetter.
>
>   Copyright (C) 1998--2010 Jan Nieuwenhuizen <[hidden email]>
>   Copyright (C) 2010 Anthony Youngman <[hidden email]>

Just add your name to the copyright name list, rather than adding a new
date.

>
>   LilyPond is free software: you can redistribute it and/or modify
>   it under the terms of the GNU General Public License as published by
>   the Free Software Foundation, either version 3 of the License, or
>   (at your option) any later version.
>
>   LilyPond is distributed in the hope that it will be useful,
>   but WITHOUT ANY WARRANTY; without even the implied warranty of
>   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>   GNU General Public License for more details.
>
>   You should have received a copy of the GNU General Public License
>   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
> */
>
> #include <iostream>
>
> #include "chord-name.hh"
> #include "context.hh"
> #include "dimensions.hh"
> #include "engraver.hh"        // required for all engravers ?
> #include "font-interface.hh"
> #include "international.hh"    // required to translate stuff eg error
> messages
> #include "item.hh"
> #include "output-def.hh"
> #include "pitch.hh"
> #include "protected-scm.hh"
> #include "stream-event.hh"
> #include "text-interface.hh"
> #include "warn.hh"
>
> #include "translator.icc"    // required to define the engraver as a
> translator
>
> // define the class here
> class Chord_name_engraver : public Engraver // declare the engraver as
> an engraver
> {
>   TRANSLATOR_DECLARATIONS (Chord_name_engraver);
> protected:
>
>   // functions inherited from translator base class - declare and define
> as required
>   // virtual void initialize();        // set up for each grob as it is
> created
>   // void start_translation_timestep();    // this is called for all
> active grobs at the start of each timeslice ?
>   void process_music ();        // called after all events have been heard
>   // void process_acknowledged();    //    or grobs acknowledged
>   void stop_translation_timestep ();    // called for active grobs at
> the end of each timeslice
>   virtual void finalize ();        // tie up any loose ends when the
> grob finishes
>
>   // other functions
>   virtual void derived_mark () const;
>   DECLARE_TRANSLATOR_LISTENER (note);    // tells the engraver to
> process notes ?
>   DECLARE_TRANSLATOR_LISTENER (rest);    // tells the engraver to
> process rests ?
>
> private:    // member variable names end with an underscore
>   Item *chord_name_;
>   vector<Stream_event*> notes_;
>
>   SCM last_chord_;
>   Stream_event *rest_event_;
>
>   // ugh - surely there's a better way to do this ...
>   Pitch capo_transpose_[11];    // store the pitches to transpose down by
> };
>
> Chord_name_engraver::Chord_name_engraver ()
> // As each context is created, the engravers are attached. This
> initialises the engraver.
> // these variables carry context forward from note to note.
> {
>   chord_name_ = 0;
>   last_chord_ = SCM_EOL;
>   rest_event_ = 0;
>

Why not just define a C++ function

transpose_pitch (int semitones)

  return Pitch (0, - int (semitones / 2), - (semitones mod 2));


to handle the transposition?  Or perhaps even make this part of the
Pitch class -- i.e. add a transposed_semitones function to Pitch, so then
you could just call p->transposed_semitones (capo)?

>   // assume we're starting from C and transposing down // see above -
> this must be a crap way of doing it
>   capo_transpose_[0] = Pitch (0, -1, 0);    // B
>   capo_transpose_[1] = Pitch (0, -1, FLAT_ALTERATION);    // B flat
>   capo_transpose_[2] = Pitch (0, -2, 0);    // A
>   capo_transpose_[3] = Pitch (0, -2, FLAT_ALTERATION);    // A flat
>   capo_transpose_[4] = Pitch (0, -3, 0);    // G
>   capo_transpose_[5] = Pitch (0, -4, SHARP_ALTERATION);    // F sharp
>   capo_transpose_[6] = Pitch (0, -4, 0);    // F
>   capo_transpose_[7] = Pitch (0, -5, 0);    // E
>   capo_transpose_[8] = Pitch (0, -5, FLAT_ALTERATION);    // E flat
>   capo_transpose_[9] = Pitch (0, -6, 0);    // D
>   capo_transpose_[10] = Pitch (0, -7, SHARP_ALTERATION);    // C sharp
> }
>
> // initialize()
>
> // start_translation_timestep()
>
> void
> Chord_name_engraver::process_music ()
> // This is called for each note or rest.
> // It is called as a result of being registered with
> IMPLEMENT_TRANSLATOR_LISTENER
> {
>   SCM markup;        // to store markup in
>   SCM bass = SCM_EOL;    // remember notes to create chord
>   SCM inversion = SCM_EOL;
>   SCM pitches = SCM_EOL;
>
>   SCM capo_markup;    // and transposed notes for capo chord
>   SCM capo_bass = SCM_EOL;
>   SCM capo_inversion = SCM_EOL;
>   SCM capo_pitches = SCM_EOL;
>
>   int capo = 0;        // declare capo as both flag and value
>
>   if (rest_event_)
>     {    // process a rest

IMO, this comment is unnecessary.  rest_event_ is self explanatory

>       SCM no_chord_markup = get_property ("noChordSymbol");
>       if (!Text_interface::is_markup (no_chord_markup))
>         return;
>       markup = no_chord_markup;
>     }
>   else
>     {  // process a note event
>       if (!notes_.size ())
>         return;
>
>       // get capo fret
>       // This is set by "\set ChordNames.capoFret = #3" in the lily
> source file
>       // declare properties in define-context-properties.scm

Similarly, these comments are unnecessary.  get_property ("capoFret") says
everything that the comments say.

>       SCM capofret = get_property ("capoFret");
Variable name should be capo_fret in C++

>       if (scm_is_number (capofret))
>       {
>     capo = scm_to_int (capofret);
>     if (capo < 0 || capo > 23)
>     {
> // !!! TODO !!! FIX !!!

ly:warning, IIUC.  And your message should mention that capo is set to 0
and continuing.

> //      origin ()->warning (_f ("invalid capo fret; must be 0 to 23,
> found: \"%i\"",
> //                  capo));
>       capo = 0;    // set to 0 to ignore
>     }
>     if (capo >= 12)
>       capo -= 12;    // if you can put the capo above 12th fret, we
> don't care about octaves
>       }
>    
>       Stream_event *inversion_event = 0;
>       for (vsize i = 0; i < notes_.size (); i++)
>       {    // step through each note in turn
>         Stream_event *n = notes_[i];
>         SCM p = n->get_property ("pitch");
>         if (!unsmob_pitch (p))
>           continue;
>
>         if (n->get_property ("inversion") == SCM_BOOL_T)
>         {
>           inversion_event = n;
>           inversion = p;
The code below should be indented to match inversion
>       if (capo)
>       { // transpose the pitch // note that p here is not p above!

Why not

Pitch *c_p = unsmob_pitch (p)

in order to avoid the double call to n->get_property and to avoid the
confusion?


>         Pitch *p = unsmob_pitch (n->get_property ("pitch"));    //
> convert the pitch to a C++ class (Pitch)
>         Pitch orig = p->transposed (capo_transpose_[capo-1]);    // Now
> transpose it
>         capo_inversion = orig.smobbed_copy ();            // and convert
> it back to a scheme object
>       }
>     }
>         else if (n->get_property ("bass") == SCM_BOOL_T)
>     {
>           bass = p;
>       if (capo) {

Since you're calling this transpose multiple times, it should probably
be a C++ function

  SCM capo_transpose (SCM scm_pitch, int capo_fret)
     Pitch *cxx_pitch = unsmob_pitch (scm_pitch);
     Pitch capo_pitch = cxx_pitch->transposed_semitones (capo_fret);
     return (capo_pitch.smobbed_copy ();

Then you can just say

    if (capo)
      capo_bass = capo_transpose (p, capo);

>         Pitch *p = unsmob_pitch (n->get_property ("pitch"));
>         Pitch orig = p->transposed (capo_transpose_[capo-1]);
>         capo_bass = orig.smobbed_copy ();
>       }
>     }
>         else
>     {
>           pitches = scm_cons (p, pitches);
>       if (capo) {
>         Pitch *p = unsmob_pitch (n->get_property ("pitch"));
>         Pitch orig = p->transposed (capo_transpose_[capo-1]);
>         capo_pitches = scm_cons ( orig.smobbed_copy (), capo_pitches);
>       }
>     }
>       }
>
>       if (inversion_event)
>       {
>         SCM oct = inversion_event->get_property ("octavation");
>         if (scm_is_number (oct))
>         {
>           Pitch *p = unsmob_pitch (inversion_event->get_property ("pitch"));
>           int octavation = scm_to_int (oct);
>           Pitch orig = p->transposed (Pitch (-octavation, 0, 0));
>
>           pitches = scm_cons (orig.smobbed_copy (), pitches);
>    
>       // if capo sort this out later !!!
>    
>         }
>         else
>           programming_error ("inversion does not have original pitch");
>       }
>
>       pitches = scm_sort_list (pitches, Pitch::less_p_proc);
>       if (capo)
>     capo_pitches = scm_sort_list (capo_pitches, Pitch::less_p_proc);
>  
>       SCM name_proc = get_property ("chordNameFunction");
>       markup = scm_call_4 (name_proc, pitches, bass, inversion,
>           context ()->self_scm ());
>       if (capo)
>     capo_markup = scm_call_4 (name_proc, capo_pitches, capo_bass,
> capo_inversion,
>         context ()->self_scm ());
>     }
>   /*
>     Ugh. // Why's this ughly?
>   */
>
>   // do we need any more capo stuff here beyond setting the chord name text?
>
>   SCM chord_as_scm = scm_cons (pitches, scm_cons (bass, inversion));
>
>   chord_name_ = make_item ("ChordName",
>       rest_event_ ? rest_event_->self_scm () : notes_[0]->self_scm ());
>   if (!capo)
>     chord_name_->set_property ("text", markup);
>   else
>   {
>     SCM capovertical = get_property ("capoVertical");
>     // how do I combine text and markup to get "markup (capo_markup)" !!!
>     SCM paren_proc = ly_lily_module_constant ("parenthesize-markup");
>     SCM line_proc = ly_lily_module_constant ("line-markup");
>     SCM hspace_proc = ly_lily_module_constant ("hspace-markup");
>
>   SCM final_markup = scm_list_n (line_proc,
>                  scm_list_3 (markup,
>                          scm_list_2 (hspace_proc,
>                              scm_from_int (1)),
>                          scm_list_2 (paren_proc, capo_markup)),
>                  SCM_UNDEFINED);
>
>     chord_name_->set_property ("text", final_markup);
>   }
>
>   SCM chord_changes = get_property("chordChanges");
>   if (to_boolean (chord_changes) && scm_is_pair (last_chord_)
>       && ly_is_equal (chord_as_scm, last_chord_))
>     chord_name_->set_property ("begin-of-line-visible", SCM_BOOL_T);
>
>   last_chord_ = chord_as_scm;
> }
>
> // process_acknowledged()
>
> void
> Chord_name_engraver::stop_translation_timestep ()
> {
>   chord_name_ = 0;
>   notes_.clear ();
>   rest_event_ = 0;
> }
>
> void
> Chord_name_engraver::finalize ()
> {
> }
>
> void
> Chord_name_engraver::derived_mark () const
> {
>   scm_gc_mark (last_chord_);
> }
>
>
>
> IMPLEMENT_TRANSLATOR_LISTENER (Chord_name_engraver, note);
> void
> Chord_name_engraver::listen_note (Stream_event *ev)
> {
>   notes_.push_back (ev);
> }
>
> IMPLEMENT_TRANSLATOR_LISTENER (Chord_name_engraver, rest);
> void
> Chord_name_engraver::listen_rest (Stream_event *ev)
> {
>   ASSIGN_EVENT_ONCE(rest_event_, ev);
> }
>
>
> /*
>   The READs description is not strictly accurate:
>   which properties are read depend on the chord naming function active.
> */
> ADD_TRANSLATOR (Chord_name_engraver,
>         /* doc */
>         "Catch note and rest events and generate the appropriate
> chordname.",
>
>         /* create */
>         "ChordName ",
>
>         /* read */    // list of properties it reads
>         "capoFret "    // added for capo
Don't need the comment on capoFret; its name says it all.

>         "capoVertical "
>         "chordChanges "
>         "chordNameExceptions "
>         "chordNameFunction "
>         "chordNoteNamer "
>         "chordRootNamer "
>         "chordNameExceptions "
>         "majorSevenSymbol "
>                 "noChordSymbol ",
>
>         /* write */
>         ""
>         );
>
>
> ---
> ----
> Join the Frogs!
>
Thanks,

Carl


---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Neil Puttock
On 28 August 2010 19:02, Carl Sorensen <[hidden email]> wrote:

> Why not just define a C++ function
>
> transpose_pitch (int semitones)
>
>  return Pitch (0, - int (semitones / 2), - (semitones mod 2));
>
>
> to handle the transposition?  Or perhaps even make this part of the
> Pitch class -- i.e. add a transposed_semitones function to Pitch, so then
> you could just call p->transposed_semitones (capo)?

The tranposition is subjective (it's mainly flat, but goes sharp for F
and C), so that wouldn't be possible without also telling the function
how to behave for certain tranpositions.

>>       if (scm_is_number (capofret))
>>       {
>>     capo = scm_to_int (capofret);
>>     if (capo < 0 || capo > 23)
>>     {
>> // !!! TODO !!! FIX !!!
>
> ly:warning, IIUC.  And your message should mention that capo is set to 0
> and continuing.

Just warning () will do (ly:warning is for scheme code).

The origin information would come from a stream event, so you could
attach the warning to a note but this doesn't make sense for a context
property setting.

Cheers,
Neil

---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Carl Sorensen



On 8/28/10 1:39 PM, "Neil Puttock" <[hidden email]> wrote:

> On 28 August 2010 19:02, Carl Sorensen <[hidden email]> wrote:
>
>> Why not just define a C++ function
>>
>> transpose_pitch (int semitones)
>>
>>  return Pitch (0, - int (semitones / 2), - (semitones mod 2));
>>
>>
>> to handle the transposition?  Or perhaps even make this part of the
>> Pitch class -- i.e. add a transposed_semitones function to Pitch, so then
>> you could just call p->transposed_semitones (capo)?
>
> The tranposition is subjective (it's mainly flat, but goes sharp for F
> and C), so that wouldn't be possible without also telling the function
> how to behave for certain tranpositions.

Yes, but I suspect that this is probably not correct at all for most chords.
It's defined for a C chord, but probably won't work correctly for any other
chords in the key of C.

Thanks,

Carl


---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Wols Lists
 On 29/08/10 00:42, Carl Sorensen wrote:

>
>
> On 8/28/10 1:39 PM, "Neil Puttock" <[hidden email]> wrote:
>
>> On 28 August 2010 19:02, Carl Sorensen <[hidden email]> wrote:
>>
>>> Why not just define a C++ function
>>>
>>> transpose_pitch (int semitones)
>>>
>>>  return Pitch (0, - int (semitones / 2), - (semitones mod 2));
>>>
>>>
>>> to handle the transposition?  Or perhaps even make this part of the
>>> Pitch class -- i.e. add a transposed_semitones function to Pitch, so then
>>> you could just call p->transposed_semitones (capo)?
>> The tranposition is subjective (it's mainly flat, but goes sharp for F
>> and C), so that wouldn't be possible without also telling the function
>> how to behave for certain tranpositions.
> Yes, but I suspect that this is probably not correct at all for most chords.
> It's defined for a C chord, but probably won't work correctly for any other
> chords in the key of C.
>
From elsewhere, though, I've picked up that just "transposing down X
semitones" probably won't work. For example, apparently transposing C
down three semitones gives you Bbb, not A.

The obvious way round that is for the normalise function to take key
signature into account, but if it doesn't do that already I don't want
to get into that at the moment ...

Cheers,
Wol

---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Carl Sorensen



On 8/29/10 1:42 PM, "Wols Lists" <[hidden email]> wrote:

>  On 29/08/10 00:42, Carl Sorensen wrote:
>>
>>
>> On 8/28/10 1:39 PM, "Neil Puttock" <[hidden email]> wrote:
>>
>>> On 28 August 2010 19:02, Carl Sorensen <[hidden email]> wrote:
>>>
>>>> Why not just define a C++ function
>>>>
>>>> transpose_pitch (int semitones)
>>>>
>>>>  return Pitch (0, - int (semitones / 2), - (semitones mod 2));
>>>>
>>>>
>>>> to handle the transposition?  Or perhaps even make this part of the
>>>> Pitch class -- i.e. add a transposed_semitones function to Pitch, so then
>>>> you could just call p->transposed_semitones (capo)?
>>> The tranposition is subjective (it's mainly flat, but goes sharp for F
>>> and C), so that wouldn't be possible without also telling the function
>>> how to behave for certain tranpositions.
>> Yes, but I suspect that this is probably not correct at all for most chords.
>> It's defined for a C chord, but probably won't work correctly for any other
>> chords in the key of C.
>>
> From elsewhere, though, I've picked up that just "transposing down X
> semitones" probably won't work. For example, apparently transposing C
> down three semitones gives you Bbb, not A.
>
> The obvious way round that is for the normalise function to take key
> signature into account, but if it doesn't do that already I don't want
> to get into that at the moment ...

I think that we have a normalize function that will take into account key
signature.


Doing this well is actually a slightly harder problem than so far indicated.

Probably you need to have both a capo fret and a capo key-signature.
Because what you want is to have the chords in the capo indications have
"good" names, i.e. be common guitar chords.  So you probably want to have F#
instead of Gb, and Bb instead of A#, etc.  I don't think this is an
intractable problem, but I do think it will need both pieces of information.

Thanks,

Carl


---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Wols Lists
In reply to this post by Carl Sorensen
 On 28/08/10 19:02, Carl Sorensen wrote:
>> /*
>> >   This file is part of LilyPond, the GNU music typesetter.
>> >
>> >   Copyright (C) 1998--2010 Jan Nieuwenhuizen <[hidden email]>
>> >   Copyright (C) 2010 Anthony Youngman <[hidden email]>
> Just add your name to the copyright name list, rather than adding a new
> date.
Is that legally proper?

From what I know, the copyright notice is meant to say who holds the
copyrights and what date. The original notice implies the file has been
updated every year since 1998, and Jan holds all the copyrights.

If I just add my name (without a date) it implies I've modified the file
every year too ...

If somebody else alters it after I've submitted it, then be it upon
them, but my notice says I modified the file in 2010, and I own the
copyright to my additions. I don't want to claim any more than that.

(I was distinctly unhappy with the legality of the relicence to GPL3 -
I'm not unhappy that it was done, but I think there was a lot of
ignoring of the law ... and I don't want to be part of things I think
are technically illegal, even if I don't particularly care either way
about it.)

Cheers,
Wol

---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Carl Sorensen



On 8/30/10 6:08 PM, "Wols Lists" <[hidden email]> wrote:

>  On 28/08/10 19:02, Carl Sorensen wrote:
>>> /*
>>>>   This file is part of LilyPond, the GNU music typesetter.
>>>>
>>>>   Copyright (C) 1998--2010 Jan Nieuwenhuizen <[hidden email]>
>>>>   Copyright (C) 2010 Anthony Youngman <[hidden email]>
>> Just add your name to the copyright name list, rather than adding a new
>> date.
> Is that legally proper?

According to the Berne Convention, a copyright notice is not even required
in order to have copyright protection, so whatever we right there would be
legal.

According to the gnu license how to
<http://www.gnu.org/copyleft/gpl-howto.html>
(which is the license we are using), the proper way is "If several people
helped write the code, use all their names".
>
> From what I know, the copyright notice is meant to say who holds the
> copyrights and what date. The original notice implies the file has been
> updated every year since 1998, and Jan holds all the copyrights.
>
> If I just add my name (without a date) it implies I've modified the file
> every year too ...

No, it means that there has been a release at least every year from
1999-2010, and that Jan and Anthony both hold copyright, so both of their
permissions are required in order to have any use not permitted in the
explicit license terms.

>
> If somebody else alters it after I've submitted it, then be it upon
> them, but my notice says I modified the file in 2010, and I own the
> copyright to my additions. I don't want to claim any more than that.

No -- it would be a disaster trying to keep track in the header of which
dates each user made modifications to the code.

But, I'm not a lawyer, so don't take my legal advice.

Thanks,

Carl


---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Graham Percival
On Mon, Aug 30, 2010 at 09:06:26PM -0600, Carl Sorensen wrote:
>
> On 8/30/10 6:08 PM, "Wols Lists" <[hidden email]> wrote:
>
> > Is that legally proper?
>
> According to the Berne Convention, a copyright notice is not even required
> in order to have copyright protection, so whatever we right there would be
                                                        ~~~~~
> legal.

Ouch, bad place for a homophone (which is a stricter term than
homonym).

> According to the gnu license how to
> <http://www.gnu.org/copyleft/gpl-howto.html>
> (which is the license we are using), the proper way is "If several people
> helped write the code, use all their names".

> > If somebody else alters it after I've submitted it, then be it upon
> > them, but my notice says I modified the file in 2010, and I own the
> > copyright to my additions. I don't want to claim any more than that.
>
> No -- it would be a disaster trying to keep track in the header of which
> dates each user made modifications to the code.

Yes, especially since that information is readily available from
the git history.  That's why a number of people recommend against
having specific names in source code.  One project that follows
that advice is svn.


I've been waiting for almost a year before getting into this -- I
don't want to derail things before 2.14 is out.  Can we all
ignore it for another month or two?

Cheers,
- Graham

---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Carl Sorensen



On 8/31/10 5:15 AM, "Graham Percival" <[hidden email]> wrote:

> On Mon, Aug 30, 2010 at 09:06:26PM -0600, Carl Sorensen wrote:
>>
>> On 8/30/10 6:08 PM, "Wols Lists" <[hidden email]> wrote:
>>
>>> Is that legally proper?
>>
>> According to the Berne Convention, a copyright notice is not even required
>> in order to have copyright protection, so whatever we right there would be
>                                                         ~~~~~
>> legal.
>
> Ouch, bad place for a homophone (which is a stricter term than
> homonym).


Dang, it's tough getting old.  It used to be that I could trust my fingers
to do the write^H^H^H^H^Hright thing when I typed; I guess I'm going to have
to proofread everything more carefully now...

Thanks,

Carl


---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

off-topic, was Re: [frogs] chord-name-engraver plus capo

Trevor D-2

Carl Sorensen wrote Tuesday, August 31, 2010 1:04 PM

> On 8/31/10 5:15 AM, "Graham Percival" <[hidden email]>
> wrote:
>
>> On Mon, Aug 30, 2010 at 09:06:26PM -0600, Carl Sorensen wrote:
>>>
>>> According to the Berne Convention, a copyright notice is not
>>> even required
>>> in order to have copyright protection, so whatever we right
>>> there would be
>>
>> Ouch, bad place for a homophone (which is a stricter term than
>> homonym).

> Dang, it's tough getting old.  It used to be that I could trust my
> fingers
> to do the write^H^H^H^H^Hright thing when I typed; I guess I'm
> going to have
> to proofread everything more carefully now...

Tell me about it! :(  It gets tougher, believe me!
My fingers have been dislexic for the last 15 years.

> Carl

Trevor




---
----
Join the Frogs!

Reply | Threaded
Open this post in threaded view
|

Re: chord-name-engraver plus capo

Wols Lists
In reply to this post by Graham Percival
 On 31/08/10 12:15, Graham Percival wrote:

> On Mon, Aug 30, 2010 at 09:06:26PM -0600, Carl Sorensen wrote:
>> On 8/30/10 6:08 PM, "Wols Lists" <[hidden email]> wrote:
>>
>>> Is that legally proper?
>> According to the Berne Convention, a copyright notice is not even required
>> in order to have copyright protection, so whatever we right there would be
>                                                         ~~~~~
>> legal.
> Ouch, bad place for a homophone (which is a stricter term than
> homonym).
>
>> According to the gnu license how to
>> <http://www.gnu.org/copyleft/gpl-howto.html>
>> (which is the license we are using), the proper way is "If several people
>> helped write the code, use all their names".
>>> If somebody else alters it after I've submitted it, then be it upon
>>> them, but my notice says I modified the file in 2010, and I own the
>>> copyright to my additions. I don't want to claim any more than that.
>> No -- it would be a disaster trying to keep track in the header of which
>> dates each user made modifications to the code.
It's each individual developer's problem ... if I care enough to claim
copyright, it's my responsibility to keep my line up-to-date (and it's
courtesy not to claim if the modifications are trivial, here I don't
think they are because it is a significant change in functionality).
> Yes, especially since that information is readily available from
> the git history.  That's why a number of people recommend against
> having specific names in source code.  One project that follows
> that advice is svn.
>
Except that, if someone is searching for MY changes, they now have to
search twelve years of history instead of one ...

All the commercial code I've seen with multiple copyrights lists the
owners individually, with the years of their changes.
> I've been waiting for almost a year before getting into this -- I
> don't want to derail things before 2.14 is out.  Can we all
> ignore it for another month or two?
>
Will my changes be ready by then? :-) I hope so ...

Anyways, as I said, if someone else changes it, I don't really give a
monkeys - their change, their responsibility. imho, if I do it, I'm
falsely claiming to have modified the code on dates I haven't, and *I*
don't want to be the person that does it :-)

Cheers,
Wol

---
----
Join the Frogs!