/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2008 */

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: October 2008 */


/* This file contains code for transposing notes and key signatures. */


#include "pmwhdr.h"
#include "readhdr.h"

static uschar tp_keytable[] = {
  15, 2,17,18, 5,20,14,  /* natural */
  0,  0, 0, 0, 0, 6, 0,  /* sharp */
  0,  1, 2, 3, 4, 5, 6,  /* flat */
  36,23,30,39,26,33,34,  /* minor */
  22, 0,24,25, 0,27,21,  /* sharp minor */
  21,22,23,24,25,25,26}; /* flat minor */

/* Table of enharmonic keys; the first of each pair is a key that is never
automatically selected (i.e. is not in the above table); the second is the
equivalent. */

static uschar enh_keytable[] = {
  16,  1,       /* C$  = B% */
   9, 17,       /* C#  = D$ */
  12, 20,       /* F#  = G$ */
  35, 34,       /* A$m = G#m */
  31, 39,       /* D#m = E$m */
  28, 36,       /* A#m = B$m */
  255};         /* Marks end */

static uschar sharpable[] = {
  TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE,
  FALSE, TRUE, FALSE, TRUE, FALSE };

static uschar flatable[] = {
  FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE,
  FALSE, TRUE, FALSE, TRUE, TRUE };

static uschar dsharpable[] = {
  FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE,
  TRUE, FALSE, TRUE, FALSE, TRUE };

static uschar dflatable[] = {
  TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE,
  TRUE, FALSE, TRUE, FALSE, FALSE };

static uschar naturalable[] = {
  TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE,
  TRUE, FALSE, TRUE, FALSE, TRUE };

static uschar *able[] = {
  NULL, dsharpable, flatable, dflatable, naturalable, sharpable };


static uschar tp_newacc[] = {
  ac_dflat, ac_flat, ac_natural, ac_sharp, ac_dsharp };


static uschar tp_forward_offset[] = {
  2, 0, 4, 0, 5, 7, 0, 9, 0, 11, 0, 0 };

static uschar tp_forward_pitch[] = {
  2, 0, 2, 0, 1, 2, 0, 2, 0, 2, 0, 1 };

static uschar tp_reverse_offset[] = {
  11, 0, 0, 0, 2, 4, 0, 5, 0, 7, 0, 9 };

static uschar tp_reverse_pitch[] = {
  1, 0, 2, 0, 2, 1, 0, 2, 0, 2, 0, 2 };




/*************************************************
*            Transpose key signature             *
*************************************************/

/* As well as returning the transposed key, we also (optionally) set up the
variable (stave_transpose_letter) containing the number of letter changes that
are required for any transposition. This function is called even when there is
no transposition. Do nothing if the transposition amount is too large, to catch
sillinesses.

Arguments:
  key        key signature
  amount     number of semitones (signed)
  setletter  TRUE if stave_transpose_letter is to be set

Returns:     new key signature
*/

int
transpose_key(int key, int amount, BOOL setletter)
{
int newkey = key;

if (amount < max_transpose)
  {
  int i;
  int letterkey;
  int j = amount;
  trkeystr *k = main_transposedkeys;

  while (j < 0) j += 12;
  for (i = 0; i < j; i++) newkey = tp_keytable[newkey];
  letterkey = newkey;

  /* See if there's been a transposed key request for the new key. */

  while (k != NULL)
    {
    if (k->oldkey == newkey) { newkey = k->newkey; break; }
    k = k->next;
    }

  /* Set stave_transpose_letter if required. If the new key has changed to an
  enharmonic key, use the forced key to compute the number of letter changes;
  otherwise use the default new key. This copes with the two different uses of
  "transposedkey": (a) to use an enharmonic key and (b) to print music with a
  key signature different to the tonality. */

  if (setletter)
    {
    if (letterkey != newkey)
      {
      uschar *p = enh_keytable;
      while (*p < 255)
        {
        if (letterkey == p[1] && newkey == p[0])
          {
          letterkey = newkey;
          break;
          }
        p += 2;
        }
      }
    stave_transpose_letter = (letterkey%7) - (key%7);
    if (amount > 0 && stave_transpose_letter < 0) stave_transpose_letter += 7;
    if (amount < 0 && stave_transpose_letter > 0) stave_transpose_letter -= 7;
    }
  }

return newkey;
}


/*************************************************
*           Transpose a note                     *
*************************************************/

/* This function is called when reading a note, and also when processing a
string that contains note (chord) names. The amount by which to transpose is
set in stave_transpose.

Arguments:
  abspitch               the absolute pitch
  pitch                  the normal pitch (updated)
  acc                    the accidental value (updated)
  transposeacc           if not 0, accidental required
  transposedaccforce     retain accidental, even if implied by new key
  acc_onenote            TRUE if accidental is printed above/below, and
                           hence applies only to a single note
  texttranspose          TRUE if transposing a note name in text
  tiedcount              < 0 if note is not tied, else note number in a tie

Returns:                 the transposed absolute pitch
                         the transposed pitch in *pitch
                         the transposed accidental in *acc
*/

int
transpose_note(int abspitch, int *pitch, int *acc, int transposeacc,
  BOOL transposedaccforce, BOOL acc_onenote, BOOL texttranspose, int tiedcount)
{
int impliedacc;
int newpitch;
int newacc;
int newaccpitch;

/* First, transpose the absolute pitch */

abspitch += stave_transpose;

/* If a particular accidental is requested, and the new note is suitable, use
it. */

if (transposeacc != 0 && able[transposeacc][abspitch%12])
  {
  newacc = transposeacc;
  newpitch = abspitch - read_accpitch[newacc] + 2;
  }

/* Otherwise we must change the note letter by the same amount as the note
letter of the keysignature has changed. */

else
  {
  int i = stave_transpose_letter;
  int offset;

  newpitch = *pitch;

  /* Correct for >= octave transposition. This is surely rare, so these loops
  won't matter. */

  while (newpitch <= abspitch - 12) newpitch += 12;
  while (newpitch >= abspitch + 12) newpitch -= 12;

  offset = newpitch%12;

  /* The two cases are written out separately for maximum speed. (It also means
  the tables can be uschar rather than int.) */

  if (i >= 0)
    {
    while (i-- > 0)
      {
      newpitch += tp_forward_pitch[offset];
      offset = tp_forward_offset[offset];
      }
    }
  else
    {
    while (i++ < 0)
      {
      newpitch -= tp_reverse_pitch[offset];
      offset = tp_reverse_offset[offset];
      }
    }

  /* We now have the required new pitch, and must find the accidental that
  yields the correct absolute pitch. */

  offset = abspitch - newpitch;
  if (offset > 2 || offset < (-2))
    {
    error_moan(50, *pitch, *acc, abspitch, newpitch);
    offset = 0;
    }

  newacc = tp_newacc[offset+2];
  }

/* We now have the new pitch and its accidental. If we are transposing an
actual note, as opposed to a note name in some test, there is extra work to do
on the accidental.

If there was no accidental in the input, or if the noforce option is set, see
if this accidental is already implied and if so, cancel the accidental. The
case of a tied note is special and is handled by passing in the accidental to
check against.

If not, remember the accidental for next time, unless acc_onenote is set, which
means that the accidental applies only to this note (printed above/below) and
does not apply to later notes in the bar. The remembering table is in baraccs
format (pitch values + 2). */

if (!texttranspose)
  {
  newaccpitch = read_accpitch[newacc];

  if (tiedcount < 0) impliedacc = baraccs_tp[newpitch]; else
    impliedacc = stave_tiedata[tiedcount].acc_tp;

  if ((*acc == 0 || !transposedaccforce) && impliedacc == newaccpitch)
    newacc = 0;
  else if (!acc_onenote) baraccs_tp[newpitch] = newaccpitch;
  }

/* Return the transposed pitch+accidental and absolute pitch */

*pitch = newpitch;
*acc = newacc;
return abspitch;
}


/* End of transpose_c */
