% File:          abc.sl      -*- mode: SLang; mode: fold -*-
%
% Author:        Guido Gonzato, <guido dot gonzato at poste dot it>
% Version:       1.18.1
% 
% Changes:
%
%   - added dependency on abc-latin1.sl or abc-utf8.sl
%   - added Lydian modes
%   - title trimmed if too long in index mode
%   - abc_listen_to_midi () proceeds directly when only one tune
%   - added %%abcm2ps flags support
%   - changed fsearch ("?:") to bol_fsearch
%   - in index mode, display the M: field if R: not given
%   - fixed conflicting ^Zl
%   - added 'strathspey' rhythm
%   - don't move the spot after renumbering
%   - added %%annotationfont
%   - fixed %%header and %%footer (" instead of ')
%   - added 'march' rhythm
%   - added ADD_NEWLINE
%   - changed 'reel' tempo
%   - don't renumber commented-out tunes
%   - reformat_mode is false by default
%   - don't include tunes in index if commented out
%   - added %%setfont-n to the Font Settings menu
%   - fixed latin1 support
%   - added gmix and ddor to the 0 accidentals case
%   - fixed K: in index
%   - added 'x' as pause character
%   - changed "static define" -> "private define"
%   - fixed bug affecting ' and , when beep disabled
%   
%   This program 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 2 of the License, or
%   (at your option) any later version.
%   
%   This program 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 this program; if not, write to the Free Software
%   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
% 
% Description:   This mode is designed to facilitate the task of editing
%                ABC (Plus) music files. The ABC Plus home page is at
%                http://abcplus.sourceforge.net
%                
% Installation:  copy abc.sl and jedabc.txt to JED_ROOT/lib. To create
%                the DFA syntax cache, type:
%                
%                  jed -batch -n -l preparse
%                
%                then insert these lines in your .jedrc:
%
%                  variable abc_language = "it"; % default: "en"
%                  autoload ("abc_mode", "abc");
%                  add_mode_for_extension ("abc", "abc");
%                  add_mode_for_extension ("abc", "abp");
%                  enable_dfa_syntax_for_mode ("abc");
%                
%                I also suggest that Unix users append this entry in
%                their .jedrc:
%                
%                  define abc_mode_hook ()
%                  { setbuf_info (getbuf_info () | 0x400); }
%                  
%                This will force Jed to save with CR/LF line endings,
%                which helps DOS/Windows users read the abc file.
%
% Last updated: February 16, 2010

% If you're running Jed under DOS/Windows, you should have the program
% beep.exe in your PATH. It's used to play the notes as you type them.
% If you don't install it, then set the variable "do_beeps" to FALSE.
% Linux users don't have to compile beep.c, as the sound is generated
% directly by this S-lang code.

require ("keydefs");

% The following variables should be defined in your .jedrc, like:
%   variable abc_compile_ps_cmd = "abc2ps";
% If they're undefined, these are the defaults.
% Please note that "abcm2ps" is virtually a no-option: currently,
% there's no ABC -> Postscript translator of comparable quality.

% ----- custom variables %{{{

custom_variable ("abc_encoding",           "latin1"); % or "utf8"
custom_variable ("abc_language",           "en");     % or "it"
custom_variable ("abc_preprocess_cmd",     "abcpp");
custom_variable ("abc_compile_ps_cmd",     "abcm2ps");
custom_variable ("abc_compile_midi_cmd",   "abc2midi");
custom_variable ("abc_xvoice_cmd",         "abc2prt");
% custom_variable ("abc_transpose_cmd",      "abc2abc");
custom_variable ("abc_compile_ps_flags",   "-c");
custom_variable ("abc_compile_midi_flags", "");
custom_variable ("abc_start_with_beep",    1);
custom_variable ("abc_start_with_doremi",  0);
custom_variable ("abc_start_with_autobar", 1);
custom_variable ("abc_start_with_chars",   1);
#ifdef WIN32 
% Windows users must make sure these programs are in the PATH
custom_variable ("abc_view_cmd",     "gsview32.exe");
custom_variable ("abc_viewer_flags", "");
custom_variable ("abc_player_cmd",   ""); % default system player
custom_variable ("abc_player_flags", "");
custom_variable ("beep_duration", 50); % beep length; shorter for Windows
#else
custom_variable ("abc_view_cmd", "gv");
custom_variable ("abc_viewer_flags", "--watch");
custom_variable ("abc_player_cmd", "timidity");
custom_variable ("abc_player_flags", "-A 200");
custom_variable ("beep_duration", 100);
#endif

%}}}

% ----- variables %{{{

variable
  abc_file,
  abc_file_dir,
  abc_ps_file = "",
  abc_midi_file = "",
  abc_file_index,
  custom_variables = % !!! add abc_transpose_cmd when it's ready!
  "abc_encoding,abc_language,abc_preprocess_cmd,abc_compile_ps_cmd," + 
  "abc_xvoice_cmd,abc_compile_ps_flags," + 
  "abc_compile_midi_cmd,abc_compile_midi_flags," +
  "abc_view_cmd,abc_player_cmd,abc_player_flags,beep_duration";

#ifdef WIN32
static variable devnull = " > NUL";
#else
static variable devnull = " > /dev/null 2>&1";
#endif

% not static - needed by abc-latin1 or abc-utf8
variable TRUE = 1;
variable FALSE = 0;

static variable selection = FALSE;    % for index_mode
static variable is_abcplus = FALSE;   % is this plain ABC or ABC+?
static variable note_mode = FALSE;    % writing notes or other things?
static variable reformat_mode = FALSE; % reformat bars?
static variable sharp = FALSE;        % will insert a sharped note
static variable doublesharp = FALSE;  % will insert a double sharped note
static variable flat = FALSE;         % will insert a flattened note
static variable doubleflat = FALSE;   % will insert a double flattened note
static variable natural = FALSE;      % last note is natural
static variable convert_drm = FALSE;  % from 'drm' (do re mi) to 'cde'
static variable key = Double_Type [14];      % note frequencies
static variable naturals = Double_Type [14]; % unaltered notes
static variable alter = Double_Type [14];    % altered for current measure
static variable naturalised = Integer_Type [14]; 
static variable root12of2 = 1.0594630943;    % to obtain the next note
static variable frequency;            % for beeping
static variable inversion = FALSE;    % type '2A', insert 'A2'
       % not static, as above
       variable insert_chars = TRUE;  % type an accented 'a' to insert '\`a'
static variable octave_up = 0;        % when inserting '
static variable octave_down = 0;      % when inserting ,
static variable division = 0;         % when inserting /
static variable times = '*';          % note multiplyed by n
static variable abc_system = "none";  % current system
static variable measure = -1.0;       % current measure length
static variable measure_length;       % tune measure length
static variable meter_set = FALSE;    % tune meter
static variable last_note = 0.0;      % last note length
static variable note_length = 0.0;    % standard note length
static variable beep_length = 1.0;    % beep multiplier
static variable do_beeps = TRUE;      % play notes yes/no
static variable tune_key = "*";       % current tune's key
static variable compound = FALSE;     % meter
static variable num_of_tunes = 0;     % number of tunes
static variable tempo = 0;            % MIDI tempo

% Central A is 440 Hz; the following note is obtained 
% as frequency * root12of2.

alter[*] = 1.0; % this is used for temporarily altering a note
key = [440.0, 494.0, 262.0, 294.0, 330.0, 350.0, 392.0,
       880.0, 988.0, 524.0, 588.0, 660.0, 700.0, 784.0];
naturals [*] = key [*];
naturalised [*] = 0;

%}}}

% ----- utility functions %{{{

define read_key (msg)
{
  !if (input_pending (3))
    flush (msg);
  tolower (getkey ());
} % read_key (msg)

define key_comma ()
{
  if (TRUE == inversion) {
    octave_down++;
    vmessage ("%d octaves lower.", octave_down);
  }
  else
    insert (",");
} % key_comma ()

define key_apostrophe ()
{
  if (TRUE == inversion) {
    octave_up++;
    vmessage ("%d octaves higher.", octave_up);
  }
  else
    insert ("'");
} % key_apostrophe ()

define toggle_autobar_mode ()
{
  if (FALSE == inversion) {
    message ("Autobar and symbol inversion enabled.");
    inversion = TRUE;
  }
  else {
    message ("Autobar and symbol inversion disabled.");
    inversion = FALSE;
  }
} % toggle_autobar_mode ()

define key_sharp ()
{
  if (TRUE == sharp)
    doublesharp = TRUE;
  sharp = TRUE;
  insert ("^");
} % key_sharp ()

define key_flat ()
{
  if (TRUE == flat)
    doubleflat = TRUE;
  flat = TRUE;
  insert ("_");
} % key_flat ()

define key_natural ()
{
  natural = TRUE;
  insert ("=");
} % key_natural ()

define get_word ()
{
  variable word;
  skip_chars (" \t");
  push_mark ();
  skip_chars ("0-9.A-Za-z");
  word = bufsubstr ();
  pop_mark_0 ();
  return word;
}

%}}}

% ----- 'compiler' interface %{{{

static variable out_file;

define abc_preprocess (cmd, dflt, verbose)
{
  out_file = abc_file;
  push_spot ();
  bob ();
  % !!! TODO - make it better!
  if (0 == bol_fsearch ("#")) { % nothing to preprocess
    pop_spot ();
    return;
  }
  
  % if this file has .abp extension, then by default the name of the
  % preprocessed file will be .abc; otherwise, .ab.
  if (is_abcplus)
    out_file = extract_element (abc_file, 0, '.') + ".abc";
  else
    out_file = extract_element (abc_file, 0, '.') + ".ab";
  bob ();
  % if this file has preprocessor directives,
  % then ask how to preprocess it
  if (0 != bol_fsearch ("#")) { % directive found
    !if (strcmp (cmd, ""))
      cmd = sprintf ("%s", abc_preprocess_cmd);
    if (verbose) {
      pop_spot ();
      cmd = read_mini ("Preprocessor switches:", Null_String, cmd + dflt);
      push_spot ();
    }
    % now add the file names
    cmd = cmd + " " + dircat (abc_file_dir, abc_file) + " " +
      dircat (abc_file_dir, out_file);
    sw2buf (abc_file);
    % ok, preprocess.
    if (0 != system (cmd)) {
      beep ();
      flush ("Error running the preprocessor!");
    }
    else {
      flush ("Preprocessed file written on " + out_file);
      usleep (1000);
    }
    pop_spot ();
    return;
  }
  pop_spot ();
} % abc_preprocess (cmd, verbose)

% -----

static variable abc_compile_buffer = "*compile*";
static variable 
  exit_code,
  ps_convert_cmd1 = "",
  ps_convert_cmd2 = "",
  jump_to_next_error = " (" + _Reserved_Key_Prefix +
  "' = next error.)";

% -----

define abc_find_next_error ()
{
  variable tmp, problem = "", line, col, error_msg;

  sw2buf (abc_compile_buffer);
  problem = "";
  % find the first occurrence of 'Error' or 'Warning'
  if (0 != bol_fsearch ("Error"))
    problem = "Error";
  else 
    if (0 != bol_fsearch ("Warning"))
      problem = "Warning";
  % no problems found
  if (0 == strlen (problem))
    return;
  % get line and column of offending character
  bol ();
  () = right (strlen (problem + " in line "));
  push_mark ();
  skip_chars ("0123456789");
  line = integer (bufsubstr ());
  () = right (1); % skip dot
  pop_mark_0 ();
  % column
  push_mark ();
  skip_chars ("0123456789");
  col = integer (bufsubstr ());
  if (col != 0)
    () = right (2);
  pop_mark_0 ();
  % get error message
  push_mark ();
  eol ();
  error_msg = bufsubstr ();
  pop_mark_0 ();
  sw2buf (abc_file);
  widen (); % leave index mode, if it was active
  goto_line (line);
  if (col != 0) 
    {
      () = right (col);
      tmp = problem + " in line " + string (line) + "." + 
        string (col + 1) + ": " + error_msg + jump_to_next_error;
    }
  else
    tmp = problem + " in line " + string (line) + ": " +
    error_msg + jump_to_next_error;
  
  message (tmp);
}

% -----

define set_ps_converter_flags ();

% -----

define abc_compile_and_parse ()
{
  push_spot ();
  sw2buf (abc_compile_buffer);
  erase_buffer ();
  
  ps_convert_cmd2 = ps_convert_cmd2 + " -O " + 
    dircat (abc_file_dir, abc_ps_file);
  if (TRUE == selection)
    ps_convert_cmd2 = ps_convert_cmd2 +
    " -e " + abc_file_index;
  
#ifdef WIN32
  () = system (ps_convert_cmd2 + " " +
               dircat (abc_file_dir, out_file));
#else
  () = run_shell_cmd (ps_convert_cmd2 + " " +
                      dircat (abc_file_dir, out_file) + 
                      " 2>&1"); % stderr goes to abc_compile_buffer
#endif
  % find errors and warnings
  bob ();
  abc_find_next_error ();
  % no errors found
  sw2buf (abc_file);
  pop_spot ();
  message ("Conversion to PostScript succeded!");
} % abc_compile_and_parse ()

% -----

private define get_tune_number ()
{
  variable buf, tmp, idx;
  buf = whatbuf ();
  push_spot ();
  sw2buf (abc_file);
  push_spot ();
  bob ();
  () = bol_fsearch ("X:");
  () = right (2);
  idx = get_word ();
  pop_spot ();
  sw2buf (buf);
  pop_spot ();
  return idx;
}

% -----

define abc_convert_to_ps (verbose)
{
  variable buf, tmp;
  
  % the user could have switched to another ABC buffer
  (abc_file, abc_file_dir,,) = getbuf_info ();
  save_buffer ();
  abc_file_index = get_tune_number ();
  abc_preprocess (ps_convert_cmd1, " ", verbose);
  
  % get %%abcm2ps options, if any
  set_ps_converter_flags ();
  
  !if (strcmp (ps_convert_cmd2, ""))
    ps_convert_cmd2 = sprintf ("%s %s", abc_compile_ps_cmd, 
                               abc_compile_ps_flags);

  % ??? STILL NEEDED ???
  
  if (verbose) {
    if (TRUE == selection)
      abc_ps_file = extract_element (abc_file, 0, '.') + 
      abc_file_index + ".ps";
    else % default name using the X: number
      abc_ps_file = extract_element (abc_file, 0, '.') + ".ps";
    abc_compile_and_parse ();
    ps_convert_cmd2 = "";
    return;
  }

  % non interactive mode
  if (TRUE == selection)
    abc_ps_file = extract_element (abc_file, 0, '.') + 
    abc_file_index + ".ps";
  else % default name using the X: number
    abc_ps_file = extract_element (abc_file, 0, '.') + ".ps";
  
  tmp = ps_convert_cmd2 + " " + dircat (abc_file_dir, out_file) + 
    devnull;
  % abcm2ps outputs to stderr, which is not handled well in Windoze!
  buf = whatbuf ();
  setbuf (abc_compile_buffer);
  erase_buffer ();
  exit_code = run_shell_cmd (tmp);
  setbuf (buf);

  if (1 == exit_code)
    error ("Error(s) detected! Check the last measure.");

} % abc_convert_to_ps (verbose)

% -----

static variable tmpbuf = "*tmp*";

% does not work on Windows

define check_if_present (cmd)
{
  % !!! NOPE - CHANGE IT USING A TMP BUFFER:
  % run_shell_cmd (cmd + " -h" + " 2>&1");
  % if (0 == bol_fsearch (cmd + " -h")) ...
  variable goahead,
    exitcode = run_shell_cmd (cmd + " -h" + devnull);
  if (0 != exitcode) {
    beep ();
    goahead = read_mini ("ERROR - " + cmd +
                         " not found! Continue (y/n)?",
                         Null_String, "n");
    !if (strcmp (goahead, "n")) {
      setbuf (tmpbuf);
      set_buffer_modified_flag (0);
      delbuf (tmpbuf);
      exit_jed ();
    }
  }
}

% -----

define check_programs ()
{
  variable tmp = whatbuf ();
  % check for presence of abcm2ps, abc2midi, abcpp, and abc2prt
  setbuf (tmpbuf);
  check_if_present (abc_compile_ps_cmd);
  check_if_present (abc_compile_midi_cmd);
  check_if_present (abc_preprocess_cmd);
  check_if_present (abc_xvoice_cmd);
  % are these necessary? Can't be checked anyway.
  % check_if_present (abc_view_cmd);
  % check_if_present (abc_player_cmd);
  set_buffer_modified_flag (0);
  delbuf (tmpbuf);
  sw2buf (tmp);
}

%}}}

% ----- main functions %{{{

static variable number_of_bars = 0;

private define key_bar_raw ()
{
  insert ("|");
  alter [*] = 1.0;
  naturalised [*] = 0;
  measure = 0.0;
} % key_bar_raw ()

define key_bar ()
{
  variable skip, tmp, above_col, current_col = what_column ();
  
  if (FALSE == reformat_mode) {
    key_bar_raw ();
    return;
  }
  
  % find out the number of current bars
  push_spot ();
  number_of_bars = 0;
  bol ();
  do {
    tmp = ffind ("|");
    if (tmp != 0) {
      () = right (1);
      number_of_bars++;
    }
  } while (tmp != 0);
  number_of_bars++;
  % look above and try to insert | at the same position.
  skip = 1;
  () = up (1);
  bol ();
  % if this line starts with '%', then let it alone;
  % otherwise, skip w: lines above
  while (0 != ffind ("w:")) {
    skip++;
    () = up (1);
    bol ();
  }
  loop (number_of_bars) {
    tmp = ffind ("|");
    if (0 != tmp)
      () = right (1);
  }
  
  if (0 != tmp) { % there was a music line above
    () = left (1);
    above_col = what_column ();
    % now, if the bar above is on the left of the current one,
    % then move it to the right; if it is on the left, move the 
    % current bar to the right.
    if (above_col < current_col) {
      % realign all lines above
      do {
        bol ();
        () = right (above_col - 1);
        tmp = what_char ();
        if (tmp == '|')
          loop (current_col - above_col)
            insert (" ");
        () = up (1);
        bol ();
        while (0 != ffind ("w:")) {
          () = up (1);
          bol ();
        }
      } while (tmp == '|');
      pop_spot ();
    }
    else {
      pop_spot ();
      loop (above_col - current_col)
        insert (" ");
    }
  } % if (0 != tmp) {
  else
    pop_spot ();
  key_bar_raw ();
  % and finally...
  save_buffer ();
  abc_convert_to_ps (TRUE);
} % key_bar ()

% -----

define key_slash ()
{
  variable tmp = "";
  
  if (TRUE == inversion) {
    last_note /= 2.0;
    beep_length /= 2.0;
    division++;
    loop (division)
      tmp = tmp + "/";
    vmessage ("Next note will be divided by %s.", tmp);
  }
  else
    insert ("/");
} % key_slash ()

% -----

define key_digit (digit)
{
  variable value = digit - '0';
  times = char (digit);
  
  if (TRUE == inversion) {
    last_note *= value;
    beep_length *= value;
    vmessage ("Next note will be multiplied by %s.", times);
  }
  else
    insert (times);
} % key_digit (digit)

% -----

define key_parenthesis ()
{
  if (markp ()) { % region defined - insert ( )
    exchange_point_and_mark ();
    insert ("(");
    exchange_point_and_mark ();
    insert (")");
    pop_spot ();
    pop_mark_0 ();
  }
  else
    insert ("(");
} % key_parenthesis ()

% -----

define sharpen (vec, note)
{
  variable index = toupper (note) - 'A';

  vec [index] *= root12of2;
  vec [index + 7] *= root12of2; 
} % sharpen (vec, note)

% -----

define flatten (vec, note)
{
  variable index = toupper (note) - 'A';

  vec [index] /= root12of2;
  vec [index + 7] /= root12of2; 
} % flatten (vec, note)

% -----

#ifdef UNIX % linux only, actually
define do_beep (frequency, beep_duration)
{
  variable fp = fopen ("/dev/console", "w");
  if (NULL == fp)
    return;
  () = fprintf (fp, "\e[10;%d]\e[11;%d]\a", frequency, beep_duration);
  () = fflush (fp);
} % do_beep (frequency, beep_duration)
#endif

% -----

static variable chord = FALSE;
static variable skip_notes = FALSE;
static variable tuplet = FALSE;
static variable tuplet_notes = -1;
static variable tuplet_counter = 0;

define check_bar ()
{
  variable i;
  
  if (TRUE == inversion) {
    if (division != 0) {
      loop (division)
        insert ("/");
      division = 0;
    }
    if (times[0] != '*') {
      insert (times);
      times = '*';
    }
  }
  last_note = note_length;
  beep_length = 1.0;
  % special case: M:none
  if (-1 == measure_length)
    return;
  if ( (measure == measure_length) and (FALSE == chord) )
    key_bar ();
  if (measure > measure_length) {
    measure = 0.0;
    last_note = note_length;
    beep_length = 1.0;
    error ("Error: too many notes or rests! Fix the last measure.");
  }
} % check_bar ()

define key_invpause ()
{
  measure += last_note;
  insert ("x");
  if (TRUE == inversion)
    check_bar ();
} % key_invpause ()

define key_pause ()
{
  measure += last_note;
  insert ("z");
  if (TRUE == inversion)
    check_bar ();
} % key_pause ()

% -----

static variable tune_settings;

define abc_set_meter ()
{
  variable l_tmp, m_tmp, case_sch_tmp, n, d,
    length_set = FALSE;
  
  m_tmp = "4/4"; % default
  l_tmp = "1/8";
  case_sch_tmp = CASE_SEARCH;
  CASE_SEARCH = TRUE;
  measure_length = 1.0; % = 4/4
  note_length = 1.0 / 8.0;
  push_spot ();
  % eob ();
  if (bsearch ("L:") != 0) {
    length_set = TRUE;
    skip_chars ("L: ");
    push_mark ();
    skip_chars ("0123456789"); % find numerator
    n = bufsubstr ();
    pop_mark_0 ();
    skip_chars ("/"); % if it's a fraction
    push_mark ();
    skip_chars ("0123456789"); % find denominator
    d = bufsubstr ();
    pop_mark_0 ();
    l_tmp = n + "/" + d;
    !if (strlen (d))
      d = "1";
    n = typecast (integer(n), Double_Type);
    d = typecast (integer(d), Double_Type);
    note_length = n / d;
    pop_spot ();
  }
  push_spot ();
  if (bsearch ("M:") != 0) {
    if (ffind ("none")) {
      m_tmp = "none";
      measure_length = -1.0; % a very special case!
    }
    else
    if (ffind ("C|")) {
      m_tmp = "C|";
      measure_length = 1.0;
    }
    else
    if (ffind ("C")) {
      m_tmp = "C";
      measure_length = 1.0;
    }
    else {
    % all other cases
      skip_chars ("M: ");
      push_mark ();
      skip_chars ("0123456789"); % find numerator
      n = bufsubstr ();
      pop_mark_0 ();
      skip_chars ("/"); % if it's a fraction
      push_mark ();
      skip_chars ("0123456789"); % find denominator
      d = bufsubstr ();
      pop_mark_0 ();
      !if (strlen (d))
        d = "1";
      m_tmp = n + "/" + d;
      n = typecast (integer(n), Double_Type);
      d = typecast (integer(d), Double_Type);
      measure_length = n / d;
    }
  } % if ( (bsearch ("M:") ...
  if (FALSE == length_set)
    if (measure_length < 0.75)
      note_length = 1.0 / 16.0;
  pop_spot ();
  last_note = note_length;
  beep_length = 1.0;
  tune_settings = sprintf ("Key: %s, meter: %s, default note length: %s",
                           tune_key, m_tmp, l_tmp);
  % is the meter compound?
  switch (m_tmp)
  {case "6/8" or case "9/8" or case "12/8": compound = TRUE;}
  {compound = FALSE;}
  
  message (tune_settings);
  meter_set = TRUE;
  CASE_SEARCH = case_sch_tmp;
} % abc_set_meter ()

% -----

define abc_note (note)
{
  variable index, cmd;
  
  % normal notes
  if (FALSE == skip_notes)
    measure += last_note;
  
  % notes in a chord, grace notes, or tuplets
  if (TRUE == chord)
    skip_notes = TRUE;
  
  % tuplets
  if (TRUE == tuplet)
    tuplet_counter++;
  % tuplet completed
  if (tuplet_counter == tuplet_notes) {
    
    % add the right number of notes
    switch (tuplet_notes)
    {case 2: measure += 3 * last_note;}
    {case 3: measure += 2 * last_note;}
    {case 4: measure += 3 * last_note;}
    {case 5:
      if (TRUE == compound) 
        measure += 3 * last_note;
      else
        measure += 2 * last_note;
    }
    {case 6: measure += 2 * last_note;}
    {case 7:
      if (TRUE == compound) 
        measure += 3 * last_note;
      else
        measure += 2 * last_note;
    }
    {case 8: measure += 3 * last_note;}
    {case 9:
      if (TRUE == compound) 
        measure += 3 * last_note;
      else
        measure += 2 * last_note;
    }
    tuplet_counter = 0;
    tuplet_notes = -1;
    tuplet = FALSE;
    skip_notes = FALSE;
    flush ("Tuplet completed.");
  }
  
  vinsert ("%c", note);
  
  if (tolower (note) == note) % a-g
    index = note - 'a' + 7;
  else
    index = note - 'A';
    
  % first of all, check if it is a note that was
  % turned into natural with =
    
  if ( (1 == naturalised [index]) and
       (FALSE == flat) and
       (FALSE == sharp) )
    frequency = naturals [index];
  else % turn an altered note into natural
    if (TRUE == natural) {
      naturalised [index] = 1;
      naturalised [index - 7] = 1;
      frequency = naturals [index];
    }
  else { % handle sharps and flats
    frequency = key [index];

    % now we want to check if the note was flattened or sharpened
    % for the current measure
    
    if (TRUE == flat)
      flatten (alter, note);
    if (TRUE == doubleflat)  % flatten again
      flatten (alter, note);
    if (TRUE == sharp)
      sharpen (alter, note);
    if (TRUE == doublesharp) % sharpen again
      sharpen (alter, note);
    
    frequency *= alter [index];
    naturalised [index] = 0;
  }
    
  % ok, now treat "," and "'"
  if (TRUE == inversion) {
    if (0 != octave_up) {
      loop (octave_up) {
	frequency *= 2;
	insert ("'");
      }
      octave_up = 0;
    }
    else
      if (0 != octave_down) {
        loop (octave_down) {
          frequency /= 2;
          insert (",");
        }
        octave_down = 0;
      }
  }
  if (TRUE == do_beeps) {
#ifdef IBMPC_SYSTEM
    cmd = sprintf ("beep %d %d", int (frequency),
                   int (beep_duration * beep_length));
    () = system (cmd); % a subprocess will not do any better
#else
    % divide the beep length by last_note
    do_beep (int(frequency), int (beep_duration * beep_length));
#endif
  }
  sharp = FALSE;
  doublesharp = FALSE;
  flat = FALSE;
  doubleflat = FALSE;
  natural = FALSE;
  
  if (TRUE == inversion)
    check_bar ();
} % abc_note (note)

% -----

define key_openchord ()
{
  % only the first note in the chord will be counted for autobar
  insert ("[");
  chord = TRUE;
  % in abc_note () skip_notes is set to TRUE
  message ("Chord opened.");
} % key_openchord ()

% -----

define key_closechord ()
{
  insert ("]");
  chord = FALSE;
  skip_notes = FALSE;
  check_bar ();
  message ("Chord closed.");

} % key_closechord ()

% -----

define toggle_note_mode ();

define key_opentuplet ()
{
  % count the right number of number in a tuplet
  insert ("(");
  tuplet = TRUE;
  skip_notes = TRUE;
  if (FALSE == note_mode)
    toggle_note_mode ();
  tuplet_notes = integer (read_mini ("How many notes?", Null_String, "3"));
  insert (string (tuplet_notes));
  message ("Tuplet started.");
} % key_opentuplet ()

% -----

define key_opengrace ()
{
  % don't count grace notes for measure length
  insert ("{");
  skip_notes = TRUE;
  message ("Grace notes start.");
} % key_opengrace ()

% -----

define key_closegrace ()
{
  insert ("}");
  skip_notes = FALSE;
  message ("End of grace notes.");
} % key_closegrace ()

%}}}

% ----- keys functions %{{{

static variable mode = "abc";

define define_common_keys ()
{
  definekey ("key_flat",        "_", mode);
  definekey ("key_sharp",       "^", mode);
  definekey ("key_natural",     "=", mode);
  definekey ("key_bar",         "|", mode);
  definekey ("key_bar",         "^I", mode);
  definekey ("key_slash",       "/", mode);
  definekey ("key_comma",       ",", mode);
  definekey ("key_apostrophe",  "'", mode);
  definekey ("key_pause",       "z", mode);
  definekey ("key_invpause",    "x", mode);
  definekey ("key_doublequote", "\"", mode);
  definekey ("key_openchord",   "[", mode);
  definekey ("key_closechord",  "]", mode);
  definekey ("key_opengrace",   "{", mode);
  definekey ("key_closegrace",  "}", mode);
  definekey ("key_deco",        "!", mode);
  definekey ("key_parenthesis", "(", mode);
  definekey ("key_digit ('2')", "2", mode);
  definekey ("key_digit ('3')", "3", mode);
  definekey ("key_digit ('4')", "4", mode);
  definekey ("key_digit ('5')", "5", mode);
  definekey ("key_digit ('6')", "6", mode);
  definekey ("key_digit ('7')", "7", mode);
  definekey ("key_digit ('8')", "8", mode);
  definekey ("key_digit ('9')", "9", mode);
  definekey ("key_percent ()",  "%", mode);
} % define_common_keys ()

% -----

define define_doremi_keys ()
{
  define_common_keys ();
  definekey ("abc_note ('A'); message (\"LA\")", "L", mode);
  definekey ("abc_note ('B'); message (\"SI\")", "T", mode);
  definekey ("abc_note ('C'); message (\"DO\")", "D", mode);
  definekey ("abc_note ('D'); message (\"RE\")", "R", mode);
  definekey ("abc_note ('E'); message (\"MI\")", "M", mode);
  definekey ("abc_note ('F'); message (\"FA\")", "F", mode);
  definekey ("abc_note ('G'); message (\"SOL\")","S", mode);
  definekey ("abc_note ('a'); message (\"la\")", "l", mode);
  definekey ("abc_note ('b'); message (\"si\")", "t", mode);
  definekey ("abc_note ('c'); message (\"do\")", "d", mode);
  definekey ("abc_note ('d'); message (\"re\")", "r", mode);
  definekey ("abc_note ('e'); message (\"mi\")", "m", mode);
  definekey ("abc_note ('f'); message (\"fa\")", "f", mode);
  definekey ("abc_note ('g'); message (\"sol\")","s", mode);
} % define_doremi_keys ()

% -----

define undefine_common_keys ()
{
  undefinekey ("_", mode); definekey (" _", "_", mode);
  undefinekey ("^", mode); definekey (" ^", "^", mode);
  undefinekey ("=", mode); definekey (" =", "=", mode);
  undefinekey ("|", mode); definekey (" |", "|", mode);
  undefinekey ("^I", mode);
  definekey ("call(\"self_insert_cmd\")", "^I", mode);
  undefinekey ("/", mode); definekey (" /", "/", mode);
  undefinekey (",", mode); definekey (" ,", ",", mode);
  undefinekey ("'", mode); definekey (" '", "'", mode);
  undefinekey ("z", mode); definekey (" z", "z", mode);
  % BEWARE!!!
  % undefinekey ("\"", mode); definekey (" \"", "\"", mode);
  undefinekey ("[", mode); definekey (" [", "[", mode);
  undefinekey ("]", mode); definekey (" ]", "]", mode);
  undefinekey ("{", mode); definekey (" {", "{", mode);
  undefinekey ("}", mode); definekey (" }", "}", mode);
  % undefinekey ("!", mode); definekey (" !", "!", mode);
  undefinekey ("(", mode); definekey (" (", "(", mode);
  undefinekey ("2", mode); definekey (" 2", "2", mode);
  undefinekey ("3", mode); definekey (" 3", "3", mode);
  undefinekey ("4", mode); definekey (" 4", "4", mode);
  undefinekey ("5", mode); definekey (" 5", "5", mode);
  undefinekey ("6", mode); definekey (" 6", "6", mode);
  undefinekey ("7", mode); definekey (" 7", "7", mode);
  undefinekey ("8", mode); definekey (" 8", "8", mode);
  undefinekey ("9", mode); definekey (" 9", "9", mode);
  undefinekey ("%", mode); definekey (" %", "%", mode);
} % undefine_common_keys ()

% -----

define undefine_doremi_keys ()
{
  undefine_common_keys ();
  undefinekey ("L", mode); definekey (" L", "L", mode);
  undefinekey ("T", mode); definekey (" T", "T", mode);
  undefinekey ("D", mode); definekey (" D", "D", mode);
  undefinekey ("R", mode); definekey (" R", "R", mode);
  undefinekey ("M", mode); definekey (" M", "M", mode);
  undefinekey ("F", mode); definekey (" F", "F", mode);
  undefinekey ("S", mode); definekey (" S", "S", mode);
  undefinekey ("l", mode); definekey (" l", "l", mode);
  undefinekey ("t", mode); definekey (" t", "t", mode);
  undefinekey ("d", mode); definekey (" d", "d", mode);
  undefinekey ("r", mode); definekey (" r", "r", mode);
  undefinekey ("m", mode); definekey (" m", "m", mode);
  undefinekey ("f", mode); definekey (" f", "f", mode);
  undefinekey ("s", mode); definekey (" s", "s", mode);
} % undefine_doremi_keys ()

% -----

define set_keys ()
{
  define_common_keys ();
  definekey ("abc_note ('A')", "A", mode);
  definekey ("abc_note ('B')", "B", mode);
  definekey ("abc_note ('C')", "C", mode);
  definekey ("abc_note ('D')", "D", mode);
  definekey ("abc_note ('E')", "E", mode);
  definekey ("abc_note ('F')", "F", mode);
  definekey ("abc_note ('G')", "G", mode);
  definekey ("abc_note ('a')", "a", mode);
  definekey ("abc_note ('b')", "b", mode);
  definekey ("abc_note ('c')", "c", mode);
  definekey ("abc_note ('d')", "d", mode);
  definekey ("abc_note ('e')", "e", mode);
  definekey ("abc_note ('f')", "f", mode);
  definekey ("abc_note ('g')", "g", mode);
} % set_keys ()

% -----

define unset_keys ()
{
  undefine_common_keys ();
  undefinekey ("A", mode); definekey (" A", "A", mode);
  undefinekey ("B", mode); definekey (" B", "B", mode);
  undefinekey ("C", mode); definekey (" C", "C", mode);
  undefinekey ("D", mode); definekey (" D", "D", mode);
  undefinekey ("E", mode); definekey (" E", "E", mode);
  undefinekey ("F", mode); definekey (" F", "F", mode);
  undefinekey ("G", mode); definekey (" G", "G", mode);
  undefinekey ("a", mode); definekey (" a", "a", mode);
  undefinekey ("b", mode); definekey (" b", "b", mode);
  undefinekey ("c", mode); definekey (" c", "c", mode);
  undefinekey ("d", mode); definekey (" d", "d", mode);
  undefinekey ("e", mode); definekey (" e", "e", mode);
  undefinekey ("f", mode); definekey (" f", "f", mode);
  undefinekey ("g", mode); definekey (" g", "g", mode);
} % unset_keys ()

%}}}

% ----- music functions %{{{

define abc_doremi2cde ()
{
  if (FALSE == note_mode) {
    beep ();
    flush ("You are not in note mode!");
    return;
  }
    
  if (FALSE == convert_drm) {
    unset_keys ();
    define_doremi_keys ();
    if (FALSE == inversion) {
      abc_set_meter ();
      toggle_autobar_mode ();
    }
    message ("Note substitution enabled: drmfslt -> cdefgab");
    note_mode = TRUE;
    convert_drm = TRUE;
  }
  else {
    set_keys ();
    undefine_doremi_keys ();
    message ("Note substitution disabled.");
    convert_drm = FALSE;
  }
} % abc_doremi2cde ()

% -----

define abc_range_error (max)
{
  beep ();
  vmessage ("Wrong value! Only 1-%d allowed. ", max);
} % abc_range_error (max)

% -----

define abc_sharps (do_msg, shp)
{
  variable which_key, sharps, ns, ok = FALSE;
  
  if (shp == "")
    while (FALSE == ok) {
      sharps = read_mini ("How many sharps (1-7)?", Null_String, "1");
      ns = sharps [0] - '0';
      if ( (ns > 0) and (ns < 8) )
        ok = TRUE;
      !if (ok)
        abc_range_error (7);
    }
  else
    ns = shp [0] - '0';

  key [*] = naturals [*];

  switch (ns)
    {case 1: 
      if (do_msg) which_key = read_mini ("G Em:",   Null_String, "G");
      sharpen (key, 'f');
    }
    {case 2: 
      if (do_msg) which_key = read_mini ("D Bm:",   Null_String, "D");
      sharpen (key, 'f');
      sharpen (key, 'c');
    }
    {case 3: 
      if (do_msg) which_key = read_mini ("A F#m:",  Null_String, "A");
      sharpen (key, 'f');
      sharpen (key, 'c');
      sharpen (key, 'g');
    }
    {case 4:
      if (do_msg) which_key = read_mini ("E C#m:",  Null_String, "E");
      sharpen (key, 'f');
      sharpen (key, 'c');
      sharpen (key, 'g');
      sharpen (key, 'd');
    }
    {case 5: 
      if (do_msg) which_key = read_mini ("B G#m:",  Null_String, "B");
      sharpen (key, 'f');
      sharpen (key, 'c');
      sharpen (key, 'g');
      sharpen (key, 'd');
      sharpen (key, 'a');
    }
    {case 6: 
      if (do_msg) which_key = read_mini ("F# D#m:", Null_String, "F#");
      sharpen (key, 'f');
      sharpen (key, 'c');
      sharpen (key, 'g');
      sharpen (key, 'd');
      sharpen (key, 'a');
      sharpen (key, 'e');
    }
    {case 7: 
      if (do_msg) which_key = read_mini ("C# A#m:", Null_String, "C#");
      sharpen (key, 'f');
      sharpen (key, 'c');
      sharpen (key, 'g');
      sharpen (key, 'd');
      sharpen (key, 'a');
      sharpen (key, 'e');
      sharpen (key, 'b');
    }
  
  if (do_msg) return (which_key);

} % abc_sharps (do_msg, shp)

% -----

define abc_flats (do_msg, flt)
{
  variable which_key, flats, nf, ok = FALSE;
  
  if (flt == "")
    while (FALSE == ok) {
      flats = read_mini ("How many flats (1-7)?", Null_String, "1");
      nf = flats [0] - '0';
      if ( (nf > 0) and (nf < 8) )
        ok = TRUE;
      !if (ok)
        abc_range_error (7);
    }
  else
    nf = flt [0] - '0';
  
  key [*] = naturals [*];
  
  switch (nf)
    {case 1: 
      if (do_msg) which_key = read_mini ("F Dm:",   Null_String, "F");
      flatten (key, 'b');
    }
    {case 2: 
      if (do_msg) which_key = read_mini ("Bb Gm:",  Null_String, "Bb");
      flatten (key, 'b');
      flatten (key, 'e');
    }
    {case 3: 
      if (do_msg) which_key = read_mini ("Eb Cm:",  Null_String, "Eb");
      flatten (key, 'b');
      flatten (key, 'e');
      flatten (key, 'a');
    }
    {case 4: 
      if (do_msg) which_key = read_mini ("Ab Fm:",  Null_String, "Ab");
      flatten (key, 'b');
      flatten (key, 'e');
      flatten (key, 'a');
      flatten (key, 'd');
    }
    {case 5: 
      if (do_msg) which_key = read_mini ("Db Bbm:", Null_String, "Db");
      flatten (key, 'b');
      flatten (key, 'e');
      flatten (key, 'a');
      flatten (key, 'd');
      flatten (key, 'g');
    }
    {case 6: 
      if (do_msg) which_key = read_mini ("Gb Ebm:", Null_String, "Gb");
      flatten (key, 'b');
      flatten (key, 'e');
      flatten (key, 'a');
      flatten (key, 'd');
      flatten (key, 'g');
      flatten (key, 'c');
    }
    {case 7: 
      if (do_msg) which_key = read_mini ("Cb Abm:", Null_String, "Cb");
      flatten (key, 'b');
      flatten (key, 'e');
      flatten (key, 'a');
      flatten (key, 'd');
      flatten (key, 'g');
      flatten (key, 'c');
      flatten (key, 'f');
    }
  
  if (do_msg)
    return (which_key);
} % abc_flats (do_msg, flt)

% -----

define abc_key ()
{
  variable ok, ch, whatkey;
  
  ok = FALSE;
  while (FALSE == ok) {
    flush ("Key wizard - your key has  S)harps, F)lats, N)othing) ");
    ch = tolower (getkey ());
    if (orelse
        { 'f' == ch }
        { 'n' == ch }
        { 's' == ch }
        { 13 == ch } % return
        )
      ok = TRUE;
    else
      beep ();    
  }
  switch (ch)
  { case 'f': whatkey = abc_flats (TRUE, "");  }
  { case 'n' or case 13:
    whatkey = read_mini ("C Am:", Null_String, "C");
    key [*] = naturals [*];
  }
  { case 's': whatkey = abc_sharps (TRUE, ""); }
  
  flush ("");
  insert (sprintf ("K: %s", whatkey));
  tune_key = whatkey;
} % abc_key ()

% -----

define abc_guess_key ()
{
  variable tmp;
  
  push_spot ();
  if (0 == bsearch ("K:"))
    return;
  () = right (2);
  skip_chars (" ");
  push_mark ();
  skip_chars ("#A-Za-z");
  tmp = bufsubstr ();
  pop_mark_0 ();
  
  switch (strlow (tmp))
    % flats
    { case "f"  or case "fmaj"  or case "dm" or case "dmin"
        or case "gmix" or case "ddor" or case "bblyd":
      abc_flats (FALSE, "1"); }
    { case "bb" or case "bbmaj" or case "gm" or case "gmin"
        or case "fmix" or case "gdor" or case "eblyd":
      abc_flats (FALSE, "2"); }
    { case "eb" or case "ebmaj" or case "cm" or case "cmin"
        or case "bbmix" or case "fdor" or case "ablyd":
      abc_flats (FALSE, "3"); }
    { case "ab" or case "abmaj" or case "fm" or case "fmin"
        or case "ebmix" or case "bbdor" or case "dblyd":
      abc_flats (FALSE, "4"); }
    { case "db" or case "dbmaj" or case "dbm" or case "dbmin"
        or case "abmix" or case "ebdor" or case "gblyd":
      abc_flats (FALSE, "5"); }
    { case "gb" or case "gbmaj" or case "ebm" or case "ebmin"
        or case "dbmix" or case "abdor" or case "cblyd":
    abc_flats (FALSE, "6"); }
    { case "cb" or case "cbmaj" or case "abm" or case "abmin"
        or case "gbmix" or case "dbdor" or case "fblyd":
      abc_flats (FALSE, "7"); }
  % sharps
    { case "g"  or case "gmaj" or case "em" or case "emin"
        or case "dmix" or case "ador" or case "clyd":
      abc_sharps (FALSE, "1"); }
    { case "d"  or case "dmaj" or case "bm" or case "bmin"
        or case "amix" or case "edor" or case "hp" or case "glyd":
      abc_sharps (FALSE, "2"); }
    { case "a"  or case "amaj" or case "f#m" or case "f#min"
        or case "emix" or case "bdor" or case "dlyd":
      abc_sharps (FALSE, "3"); }
    { case "e"  or case "emaj" or case "c#m" or case "c#min"
        or case "bmix" or case "f#dor" or case "alyd":
      abc_sharps (FALSE, "4"); }
    { case "b"  or case "bmaj" or case "g#m" or case "g#min"
        or case "f#mix" or case "c#dor" or case "elyd":
      abc_sharps (FALSE, "5"); }
    { case "f#" or case "f#maj" or case "d#m" or case "d#min"
        or case "c#mix" or case "g#dor" or case "blyd":
      abc_sharps (FALSE, "6"); }
    { case "c#" or case "c#maj" or case "a#m" or case "a#min"
        or case "g#mix" or case "d#dor" or case "f#lyd":
      abc_sharps (FALSE, "7"); }
  % no sharps or flats
    { case "c" or case "cmaj" or case "am" or case "amin"
	or case "gmix" or case "cdor": ; }
  % other keys are unknown
    {
      error ("Warning - unknown key!");
    }
  tune_key = tmp;
  pop_spot ();
  % abc_set_meter () will inform the user
  
} % abc_guess_key ()

% -----

define abc_guess_rhythm ()
{
  variable tmp;
  
  push_spot ();
  bob ();
  
  % BUG - what if these fields belonged to another tune?
  
  % if there's a Q: field, leave it alone
  if (0 != bol_fsearch ("Q:")) {
    pop_spot ();
    return;
  }
  bob ();
  if (0 == bol_fsearch ("R:")) { % no rhythm specified
    pop_spot ();
    return;
  }
  () = right (2);
  skip_chars (" ");
  push_mark ();
  skip_chars ("A-Za-z");
  tmp = bufsubstr ();
  pop_mark_0 ();
  
  if (0 == strcmp (strlow (tmp), "bourr")) {
    if (0 != ffind ("2"))
      tmp = "bourree2";
    else
      tmp = "bourree3";
  }
  
  switch (strlow (tmp))
    { case "bourree2": tempo = 120; }
    { case "bourree3": tempo = 200; }
    { case "hornpipe": tempo = 160; }
    { case "jig":      tempo = 180; }
    { case "march":    tempo = 120; }
    { case "mazurka":  tempo = 120; }
    { case "polka":    tempo = 140; }
    { case "reel":     tempo = 220; }
    { case "slide":    tempo = 180; }
    { case "slip":     tempo = 180; }
    { case "strathspey": tempo = 120; }
    { case "waltz":    tempo = 120; }
  
  % add more tune types here!
  
  pop_spot ();
}

% -----

define toggle_note_mode ()
{
  if (FALSE == note_mode) {
    set_keys ();
    abc_guess_key ();
    abc_set_meter ();
    toggle_autobar_mode ();
    message ("Note mode enabled. " + tune_settings);
    if (measure < 0.0) % init measure
      measure = 0.0;
    note_mode = TRUE;
  }
  else {
    unset_keys ();
    undefine_doremi_keys ();
    message ("Note mode disabled.");
    note_mode = FALSE;
    inversion = FALSE;
  }
} % toggle_note_mode ()

define toggle_bar_reformat ()
{
  if (FALSE == reformat_mode) {
    reformat_mode = TRUE;
    message ("Measure formatting enabled.");
  }
  else {
    reformat_mode = FALSE;
    message ("Measure formatting disabled.");
  }
} % toggle_bar_reformat ()

define key_percent ()
{
  % when the user inserts '%' (start of a comment), turn note mode off
  if (TRUE == note_mode)
    toggle_note_mode ();
  insert ("%");
} % key_percent ()

define key_deco ()
{
  % when the user inserts '!' (start of a decoration), turn note mode off
  insert ("!");
  toggle_note_mode ();
} % key_deco ()

static variable measure_tmp;

define key_doublequote ()
{
  % when the user inserts '"' in note mode, suspend note mode
  insert ("\"");
  toggle_note_mode ();
} % key_doublequote ()

define toggle_do_beeps ()
{
  if (FALSE == do_beeps) {
    message ("Note playing enabled.");
    do_beeps = TRUE;
  }
  else {
    message ("Note playing disabled.");
    do_beeps = FALSE;
  }
} % toggle_do_beeps ()

%}}}

% ----- template functions %{{{

static variable abc_available_templates = 14;
static variable abc_templates_en =
  "solo,duet,trio,satb,quintet,sextet,piano2,piano4," +
  "solo_piano2,solo_piano4,satb_piano2,satb_piano4,organ";
  
static variable system_voices =
  [ "1", "S", "A", "T", "B", "SI", "SII", "TI", "TII",
    "LH", "RH", "LHI", "LHII", "RHI", "RHII", "Ped" ];
static variable insert_lyrics = TRUE;

define abc_restore_system_voices ()
{
  system_voices =
  [ "1", "S", "A", "T", "B", "SI", "SII", "TI", "TII",
    "LH", "RH", "LHI", "LHII", "RHI", "RHII", "Ped" ];
}

define abc_template_base ()
{
  eob ();
  push_spot (); % abc_finish_template () will pop the spot
  insert ("X: \n");
  insert ("T: \n");
  insert ("C: \n");
  insert ("M: 4/4\n");
  insert ("L: 1/4\n");
  insert ("Q: 1/4 = 60\n");
}

define abc_finish_template ()
{
  call ("redraw");
  beep ();
  abc_key ();
  insert ("\n%\n");
  abc_set_meter ();
  pop_spot ();
  () = right (3);
  if (TRUE == note_mode)
    toggle_note_mode ();
}

% -----

define abc_template_solo ()
{
  abc_template_base ();
  vinsert ("V: %s clef=treble name=\"\"\n", system_voices [0]);
  abc_finish_template ();
  abc_system = "solo";
}

define abc_line_solo ()
{
  vinsert ("[V: %s] ", system_voices [0]);
}

% -----

define abc_template_duet ()
{
  abc_template_base ();
  vinsert ("%%%%staves [%s %s]\n", system_voices [1], system_voices [2]);
  vinsert ("V: %s clef=treble name=\"\" sname=\"\"\n",
           system_voices [1]);
  vinsert ("V: %s clef=bass   name=\"\" sname=\"\"\n",
           system_voices [2]);
  abc_finish_template ();
  abc_system = "duet";
}

define abc_line_duet ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n%%\n",
           system_voices [1], system_voices [2]);
}

% -----

define abc_template_trio ()
{
  abc_template_base ();
  vinsert ("%%%%staves [%s %s %s]\n",
           system_voices [1],
           system_voices [2],
           system_voices [3]);
  vinsert ("V: %s clef=treble name=\"\"  sname=\"A\"\n",
           system_voices [1]);
  vinsert ("V: %s clef=treble name=\"\" sname=\"T\"\n",
           system_voices [2]);
  vinsert ("V: %s clef=bass   name=\"\"  sname=\"B\"\n",
           system_voices [3]);
  abc_finish_template ();
  abc_system = "trio";
}

define abc_line_trio ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n[V: %s] \nw: \n%%\n",
           system_voices [1], system_voices [2], system_voices [3]);
}

% -----

define abc_template_satb_voices ()
{
  vinsert ("V: %s clef=treble   name=\"\" sname=\"S\"\n",
           system_voices [1]);
  vinsert ("V: %s clef=treble   name=\"\" sname=\"A\"\n",
           system_voices [2]);
  vinsert ("V: %s clef=treble-8 name=\"\" sname=\"T\"\n",
           system_voices [3]);
  vinsert ("V: %s clef=bass     name=\"\" sname=\"B\"\n",
           system_voices [4]);
}

define abc_line_satb ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n" + 
           "[V: %s] \nw: \n[V: %s] \nw: \n%%\n",
           system_voices [1], system_voices [2],
           system_voices [3], system_voices [4]);
}

define abc_template_satb ()
{
  abc_template_base ();
  vinsert ("%%%%staves [(%s %s) (%s %s)]\n",
           system_voices [1], system_voices [2],
           system_voices [3], system_voices [4]);
  abc_template_satb_voices ();
  abc_finish_template ();
  abc_system = "satb";
}

% -----

define abc_template_quintet ()
{
  abc_template_base ();
  vinsert ("%%%%staves [SI SII A T B]\n",
           system_voices [5], system_voices [6], system_voices [2],
           system_voices [3], system_voices [4]);
  vinsert ("V: %s clef=treble   name=\"\" sname=\"%s\"\n",
           system_voices [5], system_voices [5]);
  vinsert ("V: %s clef=treble   name=\"\" sname=\"%s\"\n",
           system_voices [6], system_voices [6]);
  vinsert ("V: %s clef=treble   name=\"\" sname=\"%s\"\n",
           system_voices [2], system_voices [2]);
  vinsert ("V: %s clef=treble-8 name=\"\" sname=\"%s\"\n",
           system_voices [3], system_voices [3]);
  vinsert ("V: %s clef=bass     name=\"\" sname=\"%s\"\n",
           system_voices [4], system_voices [4]);
  abc_finish_template ();
  abc_system = "quintet";
}

define abc_line_quintet ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n",
           system_voices [5], system_voices [6]);
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n",
           system_voices [2], system_voices [3]);
  vinsert ("[V: %s] \nw: \n%%\n", system_voices [4]);
}

% -----

define abc_template_sextet ()
{
  abc_template_base ();
  vinsert ("%%%%staves [%s %s %s %s %s %s]\n",
           system_voices [5], system_voices [6], system_voices [2],
           system_voices [7], system_voices [8], system_voices [4]);
  vinsert ("V: %s  clef=treble   name=\"\" sname=\"%s\"\n",
           system_voices [5], system_voices [5]);
  vinsert ("V: %s clef=treble   name=\"\" sname=\"%s\"\n",
           system_voices [6], system_voices [6]);
  vinsert ("V: %s   clef=treble   name=\"\" sname=\"%s\"\n",
           system_voices [2], system_voices [2]);
  vinsert ("V: %s  clef=treble-8 name=\"\" sname=\"%s\"\n",
           system_voices [7], system_voices [7]);
  vinsert ("V: %s clef=treble-8 name=\"\" sname=\"%s\"\n",
           system_voices [8], system_voices [8]);
  vinsert ("V: %s   clef=bass     name=\"\" sname=\"%s\"\n",
           system_voices [4], system_voices [4]);
  abc_finish_template ();
  abc_system = "sextet";
}

define abc_line_sextet ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n",
           system_voices [5], system_voices [6]);
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n",
           system_voices [2], system_voices [7]);
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n%%\n",
           system_voices [8], system_voices [4]);
}

% -----

define abc_template_piano_2 ()
{
  abc_template_base ();
  vinsert ("%%%%staves {%s %s}\n", system_voices [10], system_voices [9]);
  vinsert ("V: %s clef=treble name=\"Piano\"\nV: %s clef=bass\n",
          system_voices [10], system_voices [9]);
  abc_finish_template ();
  abc_system = "piano2";
}

define abc_line_piano_2 ()
{
  vinsert ("[V: %s] \n[V: %s] \n%%\n",
           system_voices [10], system_voices [9]);
}

% -----

define abc_template_piano_4 ()
{
  abc_template_base ();
  vinsert ("%%%%staves {(%s %s) (%s %s)}\n",
           system_voices [13], system_voices [14],
           system_voices [11], system_voices [12]);
  vinsert ("V: %s clef=treble name=\"Piano\"\nV: %s clef=treble\n",
          system_voices [13], system_voices [14]);
  vinsert ("V: %s clef=bass\nV: %s clef=bass\n",
          system_voices [11], system_voices [12]);
  abc_finish_template ();
  abc_system = "piano4";
}

define abc_line_piano_4 ()
{
  vinsert ("[V: %s] \n[V: %s] \n[V: %s] \n[V: %s] \n%%\n",
           system_voices [13], system_voices [14],
           system_voices [11], system_voices [12]);
}

% -----

define abc_template_solo_piano_2 ()
{
  abc_template_base ();
  vinsert ("%%%%staves %s {%s %s}\n",
           system_voices [0],system_voices [10], system_voices [9]);
  vinsert ("V: %s clef=treble name=\"\"\n",
           system_voices [0]);
  vinsert ("V: %s clef=treble name=\"Piano\"\nV: %s clef=bass\n",
          system_voices [10], system_voices [9]);
  abc_finish_template ();
  abc_system = "solo_piano2";
}

define abc_line_solo_piano_2 ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \n[V: %s] \n%%\n",
           system_voices [0], system_voices [10], system_voices [9]);
}

% -----

define abc_template_solo_piano_4 ()
{
  abc_template_base ();
  vinsert ("%%%%staves %s {(%s %s) (%s %s)}\n",
           system_voices [0],
           system_voices [13], system_voices [14],
           system_voices [11], system_voices [12]);
  vinsert ("V: %s   clef=treble name=\"\"\n",
           system_voices [0]);
  vinsert ("V: %s clef=treble name=\"Piano\"\nV: %s clef=treble\n",
          system_voices [13], system_voices [14]);
  vinsert ("V: %s clef=bass\nV: %s clef=bass\n",
          system_voices [11], system_voices [12]);
  abc_finish_template ();
  abc_system = "solo_piano4";
}

define abc_line_solo_piano_4 ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \n[V: %s] \n[V: %s] \n[V: %s] \n%%\n",
           system_voices [0], system_voices [13], system_voices [14],
           system_voices [11], system_voices [12]);
}

% -----

define abc_template_satb_piano_2 ()
{
  abc_template_base ();
  vinsert ("%%%%staves [%s %s %s %s] {%s %s}\n",
           system_voices [1], system_voices [2], system_voices [3],
           system_voices [4], system_voices [10], system_voices [9]);
  abc_template_satb_voices ();
  vinsert ("V: %s clef=treble name=\"Piano\"\nV: %s clef=bass\n",
           system_voices [10], system_voices [9]);
  abc_finish_template ();
  abc_system = "satb_piano2";
}

define abc_line_satb_piano_2 ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n", 
           system_voices [1], system_voices [2]);
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n", 
           system_voices [3], system_voices [4]);
  vinsert ("[V: %s] \n[V: %s]\n%%\n",
           system_voices [10], system_voices [9]);
}

% -----

define abc_template_satb_piano_4 ()
{
  abc_template_base ();
  vinsert ("%%%%staves [%s %s %s %s] {(%s %s) (%s %s)}\n",
           system_voices [1], system_voices [2], system_voices [3],
           system_voices [4], system_voices [13], system_voices [14],
           system_voices [11], system_voices [12]);
  abc_template_satb_voices ();
  vinsert ("V: %s clef=treble name=\"Piano\"\nV: %s clef=treble\n" +
           "V: %s clef=bass\nV: %s clef=bass\n", 
           system_voices [13], system_voices [14],
           system_voices [11], system_voices [12]);
  abc_finish_template ();
  abc_system = "satb_piano4";
}

define abc_line_satb_piano_4 ()
{
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n", 
           system_voices [1], system_voices [2]);
  vinsert ("[V: %s] \nw: \n[V: %s] \nw: \n", 
           system_voices [3], system_voices [4]);
  vinsert ("[V: %s] \n[V: %s] \n[V: %s] \n[V: %s] \n%%\n",
           system_voices [13], system_voices [14],
           system_voices [11], system_voices [12]);
}

% -----

define abc_template_organ ()
{
  abc_template_base ();
  vinsert ("%%%%staves {(%s %s) (%s %s)} Ped\n",
           system_voices [13], system_voices [14],
           system_voices [11], system_voices [12], system_voices [15]);
  vinsert ("V: %s  clef=treble name=\"Organ\"\nV: %s clef=treble\n",
           system_voices [13], system_voices [14]);
  vinsert ("V: %s  clef=bass\nV: %s clef=bass\n",
           system_voices [11], system_voices [12]);
  vinsert ("V: %s  clef=bass name=\"%s\"\n", 
           system_voices [15], system_voices [15]);
  abc_finish_template ();
  abc_system = "organ";
}

define abc_line_organ ()
{
  vinsert ("[V: %s] \n[V: %s] \n[V: %s] \n[V: %s] \n",
           system_voices [13], system_voices [14],
           system_voices [11], system_voices [12]);
  vinsert ("[V: %s] \n%%\n", system_voices [15]);
}

% -----

% this one accepts a variable number of arguments
define abc_change_voices ()
{
  variable v;
  _stk_reverse (_NARGS);
  loop (_NARGS) {
    v = ();
    system_voices [v] = read_mini (sprintf ("Voice %d?", v),
                                     system_voices [v],
                                     system_voices [v]);
  } % loop
}

define abc_customise_system ()
{
  variable tmp;
  
  !if (strcmp ("none", abc_system)) {
    abc_system =
      read_with_completion (abc_templates_en,
			    "System type (press TAB)?",
			    "", "", 's');
  }
  
  switch (abc_system)
  { case "solo":        abc_change_voices (0); }
  { case "duet":        abc_change_voices (1, 2); }
  { case "trio":        abc_change_voices (1, 2, 3); }
  { case "satb":        abc_change_voices (1, 2, 3, 4); }
  { case "quintet":     abc_change_voices (5, 6, 2, 3, 4); }
  { case "sextet":      abc_change_voices (5, 6, 2, 7, 8, 4); }
  { case "piano2":      abc_change_voices (10, 9); }
  { case "piano4":      abc_change_voices (11, 12, 13, 14); }
  { case "solo_piano2": abc_change_voices (0, 10, 9); }
  { case "solo_piano4": abc_change_voices (0, 11, 12, 13, 14); }
  { case "satb_piano2": abc_change_voices (1, 2, 3, 4, 10, 9); }
  { case "satb_piano4": abc_change_voices (1, 2, 3, 4, 11, 12, 13, 14); }
  { case "organ":       abc_change_voices (11, 12, 13, 14, 15); } 

}

define abc_insert_system ()
{
  variable ns, tmp;
  
  !if (strcmp ("none", abc_system)) {
    abc_system =
      read_with_completion (abc_templates_en,
			    "System type (press TAB)?",
			    "", "", 's');
  }
  push_spot ();
  switch (abc_system)
  { case "solo":        abc_line_solo (); }
  { case "duet":        abc_line_duet (); }
  { case "trio":        abc_line_trio (); }
  { case "satb":        abc_line_satb (); }
  { case "quintet":     abc_line_quintet (); }
  { case "sextet":      abc_line_sextet (); }
  { case "piano2":      abc_line_piano_2 (); }
  { case "piano4":      abc_line_piano_4 (); }
  { case "solo_piano2": abc_line_solo_piano_2 (); }
  { case "solo_piano4": abc_line_satb_piano_4 (); }
  { case "satb_piano2": abc_line_satb_piano_2 (); }
  { case "satb_piano4": abc_line_satb_piano_4 (); }
  { case "organ":       abc_line_organ (); }
  pop_spot ();
  eol ();
  if (FALSE == note_mode)
    toggle_note_mode ();
  number_of_bars = 0;
}

%}}}

% ----- MIDI and postscript conversion %{{{

static variable
  midi_convert_cmd1 = "", 
  midi_convert_cmd2 = "",
  parsebuf = "*cmd line*",
  ps_pid = -1,
  midi_pid = -1;

define abc_midify_and_parse ()
{
  variable tmp, buf, warning, line, col,
    gotoline = -1;
  
  abc_guess_rhythm ();
  buf = whatbuf ();
  % rebuild the command line
  sw2buf (parsebuf);
  erase_buffer ();
  insert (midi_convert_cmd2);
  bol ();
  skip_chars ("0-9.A-Za-z"); % skip the program name
  insert (" " + dircat (abc_file_dir, out_file));
  bol ();
  push_mark ();
  eol ();
  tmp = bufsubstr ();
  pop_mark_0 ();
  sw2buf (abc_compile_buffer);
  erase_buffer ();
  if (TRUE == selection)
    tmp = tmp + get_tune_number + " -o " + 
    dircat (abc_file_dir, abc_midi_file);
  if (0 != tempo)
    tmp = tmp + " -Q " + string (tempo);
  () = run_shell_cmd (tmp);
  bob ();
  if (0 == fsearch ("writing MIDI")) { % critical errors occurred 
    message ("Conversion to MIDI failed!");
    beep ();
  }
  else
    message ("Conversion to MIDI succeded!");
    
  set_buffer_modified_flag (0);
  delbuf (whatbuf());
  sw2buf (buf);

} % abc_midify_and_parse ()

% -----

define abc_convert_to_midi (verbose)
{
  abc_preprocess (midi_convert_cmd1, " -MIDI ", TRUE);
  !if (strcmp (midi_convert_cmd2, ""))
    % only the name of the MIDI converter + its switches
    midi_convert_cmd2 = sprintf ("%s %s", abc_compile_midi_cmd,
                                 % dircat (abc_file_dir, out_file),
                                 abc_compile_midi_flags);
%  if (verbose)
%    midi_convert_cmd2 = read_mini ("Convert command:", Null_String,
%                                   midi_convert_cmd2);
  
%  if (TRUE == selection)
    abc_midi_file = extract_element (abc_file, 0, '.') +
    get_tune_number () + ".mid";
%  else % default name using the X: index number
%    abc_midi_file = extract_element (abc_file, 0, '.') +
%    "1" + ".mid";
  
  abc_midify_and_parse ();
}

static variable
  psbuf =   "*viewer*",
  midibuf = "*player*";

define abc_view_ps ()
{
  variable flags, tmp, cmd, buf = whatbuf ();
  
  % % delete previous viewer buffer
  % if (0 != bufferp (psbuf))
  %   delbuf (psbuf);
  
  tmp = dircat (abc_file_dir, abc_ps_file);
  save_buffer ();
  abc_convert_to_ps (TRUE);
  tmp = dircat (abc_file_dir, abc_ps_file);
  cmd = sprintf ("%s %s %s %s",
  		 abc_view_cmd, abc_viewer_flags, tmp, devnull);
   
#ifdef WIN32
  if (0 != system (cmd))
  % if (0 != msw_system (cmd, 0, 0))
  % THE FOLLOWING WILL NOT WORK:
  % sw2buf (psbuf);
  % pid = open_process (cmd, 0);
  % process_query_at_exit (pid, 0);
  % sw2buf (buf);
  % if (-1 == pid)
#else
  if (0 != system (cmd + "&"))
#endif
    error ("Unable to start viewer.");
  else
    flush ("PostScript viewer launched.");
}

define abc_stop_playing ();
define count_tunes ();

define abc_listen_to_midi ()
{
  variable tmp, cmd, flags, buf = whatbuf ();
  
  if (0 == strlen (abc_midi_file))
    abc_midi_file = extract_element (abc_file, 0, '.') + 
    string (get_tune_number) + ".mid";
  
  tmp = dircat (abc_file_dir, abc_midi_file);
  save_buffer ();
  flush ("Please wait...");
  abc_convert_to_midi (FALSE);
  !if (strcmp (abc_midi_file, "???"))
    abc_midi_file = get_tune_number ();
  else
    abc_midi_file = dircat (abc_file_dir, abc_midi_file);
  
  count_tunes ();
  if ( (FALSE == selection) and (num_of_tunes > 1) )
    abc_midi_file = read_mini
    ("Listen to file:", Null_String, abc_midi_file);
#ifdef UNIX
  abc_stop_playing ();
  sw2buf (buf);
#endif
  cmd = sprintf ("%s %s %s", abc_player_cmd, abc_player_flags, abc_midi_file);

#ifdef IBMPC_SYSTEM
  if (0 != system (cmd))
#else
  if (0 != system (cmd + "&"))
#endif
    error ("Unable to start player.");
  % abc_midi_file = "";
  flush ("MIDI player started.");
}

define abc_help ()
{
  variable file;
  switch (abc_language)
    { case "en": 
      file = expand_jedlib_file ("jedabc.txt");
    }
    { case "it": 
      file = expand_jedlib_file ("jedabc-it.txt");
    }
  () = read_file (file);
  pop2buf (whatbuf ());
  most_mode ();
  call ("one_window");
  set_readonly (1);
}

%}}}

% ----- program options %{{{

define abc_abcpp_satb ()
{
  % fix SATB
  insert ("#define \"V: S\" \"V: 1\"\n");
  insert ("#define \"V: A\" \"V: 2\"\n");
  insert ("#define \"V: T\" \"V: 3\"\n");
  insert ("#define \"V: B\" \"V: 4\"\n");
  insert ("#define \"S A T B\" \"1 2 3 4\"\n");
}

define abc_abcpp_ssattb ()
{
  insert ("#define \"V: SII\" \"V: 2\"\n");
  insert ("#define \"V: SI\"  \"V: 1\"\n");
  insert ("#define \"V: A\"   \"V: 3\"\n");
  insert ("#define \"V: TII\" \"V: 5\"\n");
  insert ("#define \"V: TI\"  \"V: 4\"\n");
  insert ("#define \"V: B\"   \"V: 6\"\n");
  insert ("#define \"SI SII A TI TII B\" \"1 2 3 4 5 6\"\n");
}

define abc_abcpp_doremi ()
{
  insert ("#doremi\n");
}

define abc_abcpp_inc (what)
{
  vinsert ("#include <%s.abp>\n", what);
}

% ----- abcm2ps options -----

define abc_abcm2ps_page_all ()
{
  insert ("%\n");
  insert ("%%pageheight 29.7cm % 11in\n%%pagewidth 21cm % 8.5in\n");
  insert ("%%topmargin 1cm\n%%botmargin 1cm\n");
  insert ("%%leftmargin 1.8cm\n%%rightmargin 1.8cm\n%%indent 0\n");
  insert ("%%staffwidth 17.4cm\n%%landscape 0\n");
  insert ("%%header \"$T\t$F\t$P\"\n%%footer \"$T\t$F\t$P\"\n");
  insert ("%\n");
}

% -----

define abc_abcm2ps_page (menu)
{
  menu_append_item (menu, "%%pageheight",
                    "insert (\"%%pageheight 29.7cm % 11in\\n\")");
  menu_append_item (menu, "%%pagewidth",
                    "insert (\"%%pagewidth 21cm % 8.5in\\n\")");
  menu_append_item (menu, "%%topmargin",
                    "insert (\"%%topmargin 1cm\\n\")");
  menu_append_item (menu, "%%botmargin",
                    "insert (\"%%botmargin 0\\n\")");
  menu_append_item (menu, "%%leftmargin",
                    "insert (\"%%leftmargin 1.5cm\\n\")");
  menu_append_item (menu, "%%rightmargin",
                    "insert (\"%%rightmargin 1.5cm\\n\")");
  menu_append_item (menu, "%%indent",
                    "insert (\"%%indent 0\\n\")");
  menu_append_item (menu, "%%staffwidth",
                    "insert (\"%%staffwidth 18cm\\n\")");
  menu_append_item (menu, "%%landscape",
                    "insert (\"%%landscape 0\\n\")");
  menu_append_item (menu, "%%header",
		    "insert (\"%%header \\d034$T\\t$F\\t$P\\d034\\n\")");
  menu_append_item (menu, "%%footer",
                    "insert (\"%%footer \\d034$T\\t$F\\t$P\\d034\\n\")");
  menu_append_item (menu, "&Full set", "abc_abcm2ps_page_all");
}

define abc_abcm2ps_font_all ()
{
  insert ("%\n");
  insert ("%%setfont-0\n");
  insert ("%%setfont-1 Times-Roman 12\n%%setfont-2 Times-Italic 14\n");
  insert ("%%setfont-3 Helvetica 12\n%%setfont-4 Helvetica-Bold 12\n");
  insert ("%%titlefont Times-Roman 20\n%%subtitlefont Times-Roman 16\n");
  insert ("%%annotationfont Times-Bold 12\n");
  insert ("%%composerfont Times-Italic 14\n%%partsfont Times-Roman 15\n");
  insert ("%%tempofont Times-Bold 15\n%%gchordfont Helvetica 12\n");
  insert ("%%infofont Times-Italic 14\n%%repeatfont Times-Roman 12 \n");
  insert ("%%textfont Times-Roman 16\n");
  insert ("%%vocalfont Times-Bold 13\n%%wordsfont Times-Roman 16\n");
  insert ("%%headerfont Times-Roman 12\n%%footerfont Times-Roman 12\n");
  insert ("%%titlecaps 0\n%%titleleft 0\n");
  insert ("%\n");
}

define abc_abcm2ps_font (menu)
{
  menu_append_item (menu, "%%setfont-0",
                    "insert (\"%%setfont-0\\n\")");
  menu_append_item (menu, "%%setfont-1",
                    "insert (\"%%setfont-1 Times-Roman 12\\n\")");
  menu_append_item (menu, "%%setfont-2",
                    "insert (\"%%setfont-2 Times-Italic 14\\n\")");
  menu_append_item (menu, "%%setfont-3",
                    "insert (\"%%setfont-3 Helvetica 12\\n\")");
  menu_append_item (menu, "%%setfont-4",
                    "insert (\"%%setfont-4 Helvetica-Bold 12\\n\")");
  menu_append_item (menu, "%%titlefont",
                    "insert (\"%%titlefont Times-Roman 20\\n\")");
  menu_append_item (menu, "%%subtitlefont",
                    "insert (\"%%subtitlefont Times-Bold 12\\n\")");
  menu_append_item (menu, "%%annotationfont",
                    "insert (\"%%annotationfont Times-Bold 12\\n\")");
  menu_append_item (menu, "%%composerfont",
                    "insert (\"%%composerfont Times-Italic 14\\n\")");
  menu_append_item (menu, "%%partsfont",
                    "insert (\"%%partsfont Times-Roman 15\\n\")");
  menu_append_item (menu, "%%tempofont",
                    "insert (\"%%tempofont Times-Bold 15\\n\")");
  menu_append_item (menu, "%%gchordfont",
                    "insert (\"%%gchordfont Helvetica 12\\n\")");
  menu_append_item (menu, "%%infofont",
                    "insert (\"%%infofont Times-Italic 14\\n\")");
  menu_append_item (menu, "%%repeatfont",
                    "insert (\"%%repeatfont Times-Roman 12\\n\")");
  menu_append_item (menu, "%%textfont",
                    "insert (\"%%textfont Times-Roman 16\\n\")");
  menu_append_item (menu, "%%vocalfont",
                    "insert (\"%%vocalfont Times-Bold 13\\n\")");
  menu_append_item (menu, "%%wordsfont",
                    "insert (\"%%wordsfont Times-Roman 16\\n\")");
  menu_append_item (menu, "%%headerfont",
                    "insert (\"%%headerfont Times-Roman 12\\n\")");
  menu_append_item (menu, "%%footerfont",
                    "insert (\"%%footerfont Times-Roman 12\\n\")");
  menu_append_item (menu, "%%titlecaps",
                    "insert (\"%%titlecaps 0\\n\")");
  menu_append_item (menu, "%%titleleft",
                    "insert (\"%%titleleft 0\\n\")");
  menu_append_item (menu, "&Full set", "abc_abcm2ps_font_all");
}

define abc_abcm2ps_spaces_all ()
{
  insert ("%\n");
  insert ("%%topspace 0.8cm\n%%titlespace 0.2cm\n%%subtitlespace 0.1cm\n");
  insert ("%%composerspace 0.2cm\n%%musicspace 0.2cm\n%%partsspace 0.3cm\n");
  insert ("%%vocalspace 23pt\n%%wordsspace 0.5cm\n%%textspace 0.5cm\n");
  insert ("%%infospace 0\n%%staffsep 46pt\n%%sysstaffsep 36pt\n");
  insert ("%%barsperstaff 5\n%%parskipfac 0.4\n%%lineskipfac 1.1\n");
  insert ("%%stretchstaff 1\n%%stretchlast 0\n%%maxshrink 0.65\n");
  insert ("%%slurheight 0.5\n");
  insert ("%%notespacingfactor 1.414\n%%scale 0.75\n");
  insert ("%\n");
}

define abc_abcm2ps_spaces (menu)
{
  menu_append_item (menu, "%%topspace",
                    "insert (\"%%topspace 0.8cm\\n\")");
  menu_append_item (menu, "%%titlespace",
                    "insert (\"%%titlespace 0.2cm\\n\")");
  menu_append_item (menu, "%%subtitlespace",
                    "insert (\"%%subtitlespace 0.1cm\\n\")");
  menu_append_item (menu, "%%composerspace",
                    "insert (\"%%composerspace 0.2cm\\n\")");
  menu_append_item (menu, "%%musicspace",
                    "insert (\"%%musicspace 0.2cm\\n\")");
  menu_append_item (menu, "%%partsspace",
                    "insert (\"%%partsspace 0.3cm\\n\")");
  menu_append_item (menu, "%%vocalspace",
                    "insert (\"%%vocalspace 23pt\\n\")");
  menu_append_item (menu, "%%wordsspace",
                    "insert (\"%%wordsspace 0.5cm\\n\")");
  menu_append_item (menu, "%%textspace",
                    "insert (\"%%textspace 0.5cm\\n\")");
  menu_append_item (menu, "%%infospace",
                    "insert (\"%%infospace 0\\n\")");
  menu_append_item (menu, "%%staffsep",
                    "insert (\"%%staffsep 46pt\\n\")");
  menu_append_item (menu, "%%sysstaffsep",
                    "insert (\"%%sysstaffsep 36pt\\n\")");
  menu_append_item (menu, "%%barsperstaff",
                    "insert (\"%%barsperstaff 5\\n\")");
  menu_append_item (menu, "%%parskipfac",
                    "insert (\"%%parskipfac 0.4\\n\")");
  menu_append_item (menu, "%%lineskipfac",
                    "insert (\"%%lineskipfac 1.1\\n\")");
  menu_append_item (menu, "%%stretchstaff",
                    "insert (\"%%stretchstaff 1\\n\")");
  menu_append_item (menu, "%%stretchlast",
                    "insert (\"%%stretchlast 0\\n\")");
  menu_append_item (menu, "%%maxshrink",
                    "insert (\"%%maxshrink 0.65\\n\")");
  menu_append_item (menu, "%%slurheight",
                    "insert (\"%%slurheight\\n\")");
  menu_append_item (menu, "%%notespacingfactor",
                    "insert (\"%%notespacingfactor 1.414\\n\")");
  menu_append_item (menu, "%%scale",
                    "insert (\"%%scale 0.75\\n\")");
  menu_append_item (menu, "&Full set", "abc_abcm2ps_spaces_all");
}

define abc_abcm2ps_text_all ()
{
  insert ("%\n");
  insert ("%%text \n%%center \n%%begintext obeylines | fill | justify\n");
  insert ("%%endtext\n%%sep\n%%sep 1cm 2cm 10cm\n%%vskip 1cm\n%%newpage\n");
  insert ("%\n");
}

define abc_abcm2ps_text (menu)
{
  menu_append_item (menu, "%%text",
                    "insert (\"%%text \")");
  menu_append_item (menu, "%%center",
                    "insert (\"%%center \")");
  menu_append_item (menu, "%%begintext",
                    "insert (\"%%begintext obeylines | fill | justify\\n\")");
  menu_append_item (menu, "%%endtext",
                    "insert (\"%%endtext\\n\")");
  menu_append_item (menu, "%%sep",
                    "insert (\"%%sep\\n\")");
  menu_append_item (menu, "%%sep a b c",
                    "insert (\"%%sep 1cm 2cm 10cm\\n\")");
  menu_append_item (menu, "%%vskip",
                    "insert (\"%%vskip 1cm\\n\")");
  menu_append_item (menu, "%%newpage",
                    "insert (\"%%newpage\\n\")");
  menu_append_item (menu, "&Full set", "abc_abcm2ps_text_all");
}

define abc_abcm2ps_misc_all ()
{
  insert ("%\n");
  insert ("%%autoclef 1\n%%continueall 0\n%%encoding 1\n");
  insert ("%%exprabove 0\n%%exprbelow 1\n%%flatbeams 0\n");
  insert ("%%freegchord 0\n%%graceslurs 0\n%%infoline 0\n");
  insert ("%%musiconly 0\n%%oneperpage 0\n%%partsbox 0\n");
  insert ("%%postscript \n%%printtempo 1\n%%squarebreve 1\n");
  insert ("%%straightflags 0\n%%vocalabove 0\n%%withxrefs 0\n");
  insert ("%%writehistory 0\n");
  insert ("%\n");
}

define abc_abcm2ps_misc (menu)
{
  menu_append_item (menu, "%%EPS",
                    "insert (\"%%EPS \\n\")");
  menu_append_item (menu, "%%autoclef",
                    "insert (\"%%autoclef 1\\n\")");
  menu_append_item (menu, "%%continueall",
                    "insert (\"%%continueall 0\\n\")");
  menu_append_item (menu, "%%encoding",
                    "insert (\"%%encoding 0\\n\")");
  menu_append_item (menu, "%%exprabove",
                    "insert (\"%%exprabove 0\\n\")");
  menu_append_item (menu, "%%exprbelow",
                    "insert (\"%%exprbelow 1\\n\")");
  menu_append_item (menu, "%%flatbeams",
                    "insert (\"%%flatbeams 0\\n\")");
  menu_append_item (menu, "%%freegchord",
                    "insert (\"%%freegchord 0\\n\")");
  menu_append_item (menu, "%%graceslurs",
                    "insert (\"%%graceslurs 0\\n\")");
  menu_append_item (menu, "%%infoline",
                    "insert (\"%%infoline 0\\n\")");
  menu_append_item (menu, "%%musiconly",
                    "insert (\"%%musiconly 0\\n\")");
  menu_append_item (menu, "%%oneperpage",
                    "insert (\"%%oneperpage 0\\n\")");
  menu_append_item (menu, "%%partsbox",
                    "insert (\"%%partsbox 0\\n\")");
  menu_append_item (menu, "%%postscript",
                    "insert (\"%%postscript \")");
  menu_append_item (menu, "%%printtempo",
                    "insert (\"%%printtempo 1\\n\")");
  menu_append_item (menu, "%%squarebreve",
                    "insert (\"%%squarebreve 1\\n\")");
  menu_append_item (menu, "%%straightflags",
                    "insert (\"%%straightflags 0\\n\")");
  menu_append_item (menu, "%%vocalabove",
                    "insert (\"%%vocalabove 0\\n\")");
  menu_append_item (menu, "%%withxrefs",
                    "insert (\"%%withxrefs 0\\n\")");
  menu_append_item (menu, "%%writehistory",
                    "insert (\"%%writehistory 0\\n\")");
  menu_append_item (menu, "&Full set", "abc_abcm2ps_misc_all");
}

define abc_abcm2ps_measures_all ()
{
  insert ("%\n");
  insert ("%%measurefirst 1\n%%measurenb -1\n");
  insert ("%%measurebox 0\n");
  insert ("%%setbarnb \n%%staffbreak \n");
  insert ("%\n");
}

define abc_abcm2ps_measures (menu)
{
  menu_append_item (menu, "%%measurefirst",
                    "insert (\"%%measurefirst 1\\n\")");
  menu_append_item (menu, "%%measurenb",
                    "insert (\"%%measurenb -1\\n\")");
  menu_append_item (menu, "%%measurebox",
                    "insert (\"%%measurebox 0\\n\")");
  menu_append_item (menu, "%%setbarnb",
                    "insert (\"%%setbarnb \n\\n\")");
  menu_append_item (menu, "%%staffbreak",
                    "insert (\"%%staffbreak \\n\")");
  menu_append_item (menu, "&Full set", "abc_abcm2ps_measures_all");
}

define abc_abcm2ps_columns ()
{
  insert ("%\n");
  insert ("%%multicol start\n%%leftmargin 1cm\n%%rightmargin 10cm\n");
  insert ("%%text \n");
  insert ("%%multicol new\n%%leftmargin 12cm\n%%rightmargin 1cm\n");
  insert ("%%text \n%%multicol end\n");
  insert ("%\n");
}

define abc_list_fields (menu)
{
  menu_append_item (menu, "&A: area", "insert (\"A:\")");
  menu_append_item (menu, "&B: book", "insert (\"B:\")");
  menu_append_item (menu, "&C: composer", "insert (\"C:\")");
  menu_append_item (menu, "&D: discography", "insert (\"D:\")");
  menu_append_item (menu, "&d: deco line", "insert (\"d:\")");
  menu_append_item (menu, "&F: file name", "insert (\"F:\")");
  menu_append_item (menu, "&G: group", "insert (\"G:\")");
  menu_append_item (menu, "&H: history", "insert (\"H:\")");
  menu_append_item (menu, "&I: information", "insert (\"I:\")");
  menu_append_item (menu, "&K: key", "insert (\"K:\")");
  menu_append_item (menu, "&L: note length", "insert (\"L:\")");
  menu_append_item (menu, "&M: meter", "insert (\"M:\")");
  menu_append_item (menu, "&N: notes", "insert (\"N:\")");
  menu_append_item (menu, "&O: origin", "insert (\"O:\")");
  menu_append_item (menu, "&P: parts", "insert (\"P:\")");
  menu_append_popup (menu, "&Q: tempo");
  $2 = sprintf ("%s.&Q: tempo", menu);
  menu_append_item ($2, "Q: 40   largo: 40-60", "insert (\"Q: 40\")");
  menu_append_item ($2, "Q: 60   larghetto: 60-66", "insert (\"Q: 60\")");
  menu_append_item ($2, "Q: 66   adagio: 66-76", "insert (\"Q: 66\")");
  menu_append_item ($2, "Q: 76   andante: 76-108", "insert (\"Q: 76\")");
  menu_append_item ($2, "Q: 108  moderato: 108-120", "insert (\"Q: 108\")");
  menu_append_item ($2, "Q: 120  allegro: 120-168", "insert (\"Q: 120\")");
  menu_append_item ($2, "Q: 168  presto: 168-200", "insert (\"Q: 168\")");
  menu_append_item ($2, "Q: 200  prestissimo: 168-200",
                    "insert (\"Q: 200\")");  
  menu_append_item (menu, "&R: rhythm", "insert (\"R:\")");
  menu_append_item (menu, "&S: source", "insert (\"S:\")");
  menu_append_item (menu, "&T: title", "insert (\"T:\")");
  menu_append_item (menu, "&U: user defined", "insert (\"U:\")");
  menu_append_item (menu, "&W: words at end", "insert (\"W:\")");
  menu_append_item (menu, "&w: words", "insert (\"w:\")");
  menu_append_item (menu, "&X: reference number", "insert (\"X:\")");
  menu_append_item (menu, "&Z: transcription notes", "insert (\"Z:\")");
}

define abc_note_features (menu)
{
  menu_append_item (menu, "'   one octave higher", "key_apostrophe");
  menu_append_item (menu, ",   one octave lower", "key_comma");
  menu_append_item (menu, "_   flat", "key_flat");
  menu_append_item (menu, "__  double flat", "key_flat (); key_flat");
  menu_append_item (menu, "^   sharp", "key_sharp");
  menu_append_item (menu, "^^  double sharp", "key_sharp (); key_sharp");
  menu_append_item (menu, "=   natural", "key_natural");
}

define abc_decorations (menu)
{
  menu_append_item (menu, "!trill!", "insert (\"!trill!\")");
  menu_append_item (menu, "!lowermordent!", "insert (\"!lowermordent!\")");
  menu_append_item (menu, "!uppermordent!", "insert (\"!uppermordent!\")");
  menu_append_item (menu, "!mordent!", "insert (\"!mordent!\")");
  menu_append_item (menu, "!pralltriller!", "insert (\"!pralltriller!\")");
  menu_append_item (menu, "!accent!", "insert (\"!accent!\")");
  menu_append_item (menu, "!emphasis!", "insert (\"!emphasis!\")");
  menu_append_item (menu, "!fermata!", "insert (\"!fermata!\")");
  menu_append_item (menu, "!invertedfermata!",
                    "insert (\"!invertedfermata!\")");
  menu_append_item (menu, "!0-5!", "insert (\"!0-5!\")");
  menu_append_item (menu, "!+!", "insert (\"!+!\")");
  menu_append_item (menu, "!wedge!", "insert (\"!wedge!\")");
  menu_append_item (menu, "!open!", "insert (\"!open!\")");
  menu_append_item (menu, "!thumb!", "insert (\"!thumb!\")");
  menu_append_item (menu, "!snap!", "insert (\"!snap!\")");
  menu_append_item (menu, "!turn!", "insert (\"!turn!\")");
  menu_append_item (menu, "!roll!", "insert (\"!roll!\")");
  menu_append_item (menu, "!breath!", "insert (\"!breath!\")");
  menu_append_item (menu, "!shortphrase!", "insert (\"!shortphrase!\")");
  menu_append_item (menu, "!mediumphrase!", "insert (\"!mediumphrase!\")");
  menu_append_item (menu, "!longphrase!", "insert (\"!longphrase!\")");
  menu_append_item (menu, "!segno!", "insert (\"!segno!\")");
  menu_append_item (menu, "!coda!", "insert (\"!coda!\")");
  menu_append_item (menu, "!D.S.!", "insert (\"!D.S.!\")");
  menu_append_item (menu, "!D.C.!", "insert (\"!D.C.!\")");
  menu_append_item (menu, "!fine!", "insert (\"!fine!\")");
  menu_append_item (menu, "!crescendo(!", "insert (\"!crescendo(!\")");
  menu_append_item (menu, "!crescendo)!", "insert (\"!crescendo)!\")");
  menu_append_item (menu, "!diminuendo(!", "insert (\"!diminuendo(!\")");
  menu_append_item (menu, "!diminuendo)!", "insert (\"!diminuendo)!\")");
  menu_append_item (menu, "!p!", "insert (\"!p!\")");
  menu_append_item (menu, "!pp!", "insert (\"!pp!\")");
  menu_append_item (menu, "!ppp!", "insert (\"!ppp!\")");
  menu_append_item (menu, "!pppp!", "insert (\"!pppp!\")");
  menu_append_item (menu, "!mf!", "insert (\"!mf!\")");
  menu_append_item (menu, "!sfz!", "insert (\"\")");
  menu_append_item (menu, "!f!", "insert (\"!f!\")");
  menu_append_item (menu, "!ff!", "insert (\"!ff!\")");
  menu_append_item (menu, "!fff!", "insert (\"!fff!\")");
  menu_append_item (menu, "!ffff!", "insert (\"!ffff!\")");
  menu_append_item (menu, "!repeatbar!", "insert (\"!repeatbar!\")");
  menu_append_item (menu, "!repeatbar!", "insert (\"!repeatbar!\")");
  menu_append_item (menu, "!upbow!", "insert (\"!upbow!\")");
  menu_append_item (menu, "!downbow!", "insert (\"!downbow!\")");
}

define abc_predefined_fields (menu)
{
  menu_append_item (menu, "~ = !roll!", "insert (\"~\")");
  menu_append_item (menu, "T = !trill!", "insert (\"T\")");
  menu_append_item (menu, "H = !fermata!", "insert (\"H\")");
  menu_append_item (menu, "L = !emphasis!", "insert (\"L\")");
  menu_append_item (menu, "M = !lowermordent!", "insert (\"M\")");
  menu_append_item (menu, "P = !uppermordent!", "insert (\"P\")");
  menu_append_item (menu, "S = !segno!", "insert (\"S\")");
  menu_append_item (menu, "O = !coda!", "insert (\"O\")");
  menu_append_item (menu, "u = !upbow!", "insert (\"u\")");
  menu_append_item (menu, "v = !downbow!", "insert (\"v\")");
}

define abc_bars (menu)
{
  menu_append_item (menu, "|  bar line", "key_bar");
  menu_append_item (menu, "|] thin-thick", "key_bar (); insert (\"]\")");
  menu_append_item (menu, "|| thin-thin", "key_bar (); insert (\"|\")");
  menu_append_item (menu, "[| thick-thin", "insert (\"[\"); key_bar");
  menu_append_item (menu, "|: start of repeated section",
                    "key_bar (); insert (\":\")");
  menu_append_item (menu, ":| end of repeated section",
                    "insert (\":\"); key_bar");
  menu_append_item (menu, ":: start/end of two repeated sections",
                    "insert (\"::\")");
}

define abc_lyrics (menu)
{
  menu_append_item (menu, "-  break syllable", "insert (\"-\")");
  menu_append_item (menu, "|  advance to next bar", "insert (\"|\")");
  menu_append_item (menu, "_  hold syllable for an extra note",
                    "insert (\"_\")");
  menu_append_item (menu, "*  skip note", "insert (\"*\")");
  menu_append_item (menu, "~  join syllables", "insert (\"~\")");
  menu_append_item (menu, "\\- insert '-'", "insert (\"\\-\")");
  menu_append_item (menu, "\\  continue line", "insert (\"\\\\\")");
  menu_append_item (menu, "~1  insert number", "insert (\"~1\")");
}

define abc_make_font_list (menu)
{
  menu_append_item (menu, "AvantGarde-Book",
                    "insert (\"AvantGarde-Book\")");
  menu_append_item (menu, "AvantGarde-BookOblique",
                    "insert (\"AvantGarde-BookOblique\")");
  menu_append_item (menu, "AvantGarde-Demi",
                    "insert (\"AvantGarde-Demi\")");
  menu_append_item (menu, "AvantGarde-DemiOblique",
                    "insert (\"AvantGarde-DemiOblique\")");
  menu_append_item (menu, "Bookman-Demi",
                    "insert (\"Bookman-Demi\")");
  menu_append_item (menu, "Bookman-DemiItalic",
                    "insert (\"Bookman-DemiItalic\")");
  menu_append_item (menu, "Bookman-Light",
                    "insert (\"Bookman-Light\")");
  menu_append_item (menu, "Bookman-LightItalic",
                    "insert (\"Bookman-LightItalic\")");
  menu_append_item (menu, "Courier-Bold","insert (\"Courier-Bold\")");
  menu_append_item (menu, "Courier-BoldOblique",
                    "insert (\"Courier-BoldOblique\")");
  menu_append_item (menu, "Courier",
                    "insert (\"Courier\")");
  menu_append_item (menu, "Courier-Oblique",
                    "insert (\"Courier-Oblique\")");
  menu_append_item (menu, "Helvetica-Bold",
                    "insert (\"Helvetica-Bold\")");
  menu_append_item (menu, "Helvetica-BoldOblique",
                    "insert (\"Helvetica-BoldOblique\")");
  menu_append_item (menu, "Helvetica-NarrowBold",
                    "insert (\"Helvetica-NarrowBold\")");
  menu_append_item (menu, "Helvetica-NarrowBoldOblique",
                    "insert (\"Helvetica-NarrowBoldOblique\")");
  menu_append_item (menu, "Helvetica",
                    "insert (\"Helvetica\")");
  menu_append_item (menu, "Helvetica-Oblique",
                    "insert (\"Helvetica-Oblique\")");
  menu_append_item (menu, "Helvetica-Narrow",
                    "insert (\"Helvetica-Narrow\")");
  menu_append_item (menu, "Helvetica-NarrowOblique",
                    "insert (\"Helvetica-NarrowOblique\")");
  menu_append_item (menu, "NewCenturySchlbk-Bold",
                    "insert (\"NewCenturySchlbk-Bold\")");
  menu_append_item (menu, "NewCenturySchlbk-BoldItalic",
                    "insert (\"NewCenturySchlbk-BoldItalic\")");
  menu_append_item (menu, "NewCenturySchlbk-Italic",
                    "insert (\"NewCenturySchlbk-Italic\")");
  menu_append_item (menu, "NewCenturySchlbk-Roman",
                    "insert (\"NewCenturySchlbk-Roman\")");
  menu_append_item (menu, "Palatino-Bold",
                    "insert (\"Palatino-Bold\")");
  menu_append_item (menu, "Palatino-BoldItalic",
                    "insert (\"Palatino-BoldItalic\")");
  menu_append_item (menu, "Palatino-Italic",
                    "insert (\"Palatino-Italic\")");
  menu_append_item (menu, "Palatino-Roman",
                    "insert (\"Palatino-Roman\")");
  menu_append_item (menu, "Symbol",
                    "insert (\"Symbol\")");
  menu_append_item (menu, "Times-Bold",
                    "insert (\"Times-Bold\")");
  menu_append_item (menu, "Times-BoldItalic",
                    "insert (\"Times-BoldItalic\")");
  menu_append_item (menu, "Times-Italic",
                    "insert (\"Times-Italic\")");
  menu_append_item (menu, "Times-Roman",
                    "insert (\"Times-Roman\")");
  menu_append_item (menu, "ZapfChancery-MediumItalic",
                    "insert (\"ZapfChancery-MediumItalic\")");
  menu_append_item (menu, "ZapfDingbats",
                    "insert (\"ZapfDingbats\")");
}

% ----- abc2midi options

define abc_abc2midi_options (menu)
{
  menu_append_item (menu, "%%MIDI channel 1-16",
                    "insert (\"%%MIDI channel 1-16\")");
  menu_append_item (menu, "%%MIDI program 1-16 1-128",
                    "insert (\"%%MIDI program 1-16 1-128\")");
  menu_append_item (menu, "%%MIDI beat 105 95 80 1",
                    "insert (\"%%MIDI beat 105 95 80 1\")");
  menu_append_item (menu, "%%MIDI beatstring fppmpmp",
                    "insert (\"%%MIDI beatstring fppmpmp\")");
  menu_append_item (menu, "%%MIDI transpose 1",
                    "insert (\"%%MIDI transpose 1\")");
  menu_append_item (menu, "%%MIDI rtranspose 1",
                    "insert (\"%%MIDI rtranspose 1\")");
  menu_append_item (menu, "%%MIDI c 60",
                    "insert (\"%%MIDI c 60\")");
  menu_append_item (menu, "%%MIDI grace 1/8",
                    "insert (\"%%MIDI grace 1/8\")");
  menu_append_item (menu, "%%MIDI chordname maj7 0 4 7 11",
                    "insert (\"%%MIDI chordname maj7 0 4 7 11\")");
  menu_append_item (menu, "%%MIDI gchord fzczfzcz",
                    "insert (\"%%MIDI gchord fzczfzcz\")");
  menu_append_item (menu, "%%MIDI chordprog 1-128",
                    "insert (\"%%MIDI chordprog 1-128\")");
  menu_append_item (menu, "%%MIDI bassprog 1-128",
                    "insert (\"%%MIDI bassprog 1-128\")");
  menu_append_item (menu, "%%MIDI chordvol 50",
                    "insert (\"%%MIDI chordvol 50\")");
  menu_append_item (menu, "%%MIDI bassvol 50",
                    "insert (\"%%MIDI bassvol 50\")");
  menu_append_item (menu, "%%MIDI gchordon",
                    "insert (\"%%MIDI gchordon\")");
  menu_append_item (menu, "%%MIDI gchordoff",
                    "insert (\"%%MIDI gchordoff\")");
  menu_append_item (menu, "%%MIDI drum d2zdd 35 38 38  100 50 50",
                    "insert (\"%%MIDI drum d2zdd 35 38 38  100 50 50\")");
  menu_append_item (menu, "%%MIDI control [bass/chord] 7 50",
                    "insert (\"%%MIDI control [bass/chord] 7 50\")");
  menu_append_item (menu, "%%MIDI pitchbend [bass/chord] <hb> <lb>",
                    "insert (\"%%MIDI pitchbend [bass/chord] <hb> <lb>\")");
  menu_append_item (menu, "%%MIDI nobarlines",
                    "insert (\"%%MIDI nobarlines\")");
  menu_append_item (menu, "%%MIDI barlines",
                    "insert (\"%%MIDI barlines\")");
  menu_append_item (menu, "%%MIDI ratio 3 1",
                    "insert (\"%%MIDI ratio 3 1\")");
}

% -----

define abc_midi_instruments (menu)
{
  % Piano
  menu_append_popup (menu, "&Piano");
  $3 = sprintf ("%s.&Piano", menu);
  menu_append_item ($3, "1. Acoustic Grand", "insert (\"0\")");
  menu_append_item ($3, "2. Bright Acoustic", "insert (\"1\")");
  menu_append_item ($3, "3. Electric Grand", "insert (\"2\")");
  menu_append_item ($3, "4. Honky-Tonk", "insert (\"3\")");
  menu_append_item ($3, "5. Electric Piano 1", "insert (\"4\")");
  menu_append_item ($3, "6. Electric Piano 2", "insert (\"5\")");
  menu_append_item ($3, "7. Harpsichord", "insert (\"6\")");
  menu_append_item ($3, "8. Clavinet", "insert (\"7\")");
  % Chromatic Percussion
  menu_append_popup (menu, "&Chromatic Percussion");
  $3 = sprintf ("%s.&Chromatic Percussion", menu);
  menu_append_item ($3, " 9. Celesta", "insert (\"8\")");
  menu_append_item ($3, "10. Glockenspiel", "insert (\"9\")");
  menu_append_item ($3, "11. Music Box", "insert (\"10\")");
  menu_append_item ($3, "12. Vibraphone", "insert (\"11\")");
  menu_append_item ($3, "13. Marimba", "insert (\"12\")");
  menu_append_item ($3, "14. Xylophone", "insert (\"13\")");
  menu_append_item ($3, "15. Tubular Bells", "insert (\"14\")");
  menu_append_item ($3, "16. Dulcimer", "insert (\"15\")");
  % Organ
  menu_append_popup (menu, "&Organ");
  $3 = sprintf ("%s.&Organ", menu);
  menu_append_item ($3, "17. Drawbar Organ", "insert (\"16\")");
  menu_append_item ($3, "18. Percussive Organ", "insert (\"17\")");
  menu_append_item ($3, "19. Rock Organ", "insert (\"18\")");
  menu_append_item ($3, "20. Church Organ", "insert (\"19\")");
  menu_append_item ($3, "21. Reed Organ", "insert (\"20\")");
  menu_append_item ($3, "22. Accordion", "insert (\"21\")");
  menu_append_item ($3, "23. Harmonica", "insert (\"22\")");
  menu_append_item ($3, "24. Tango Accordion", "insert (\"23\")");
  menu_append_popup (menu, "&Guitar");
  $3 = sprintf ("%s.&Guitar", menu);
  menu_append_item ($3, "25. Nylon String Guitar", "insert (\"24\")");
  menu_append_item ($3, "26. Steel String Guitar", "insert (\"25\")");
  menu_append_item ($3, "27. Electric Jazz Guitar", "insert (\"26\")");
  menu_append_item ($3, "28. Electric Clean Guitar", "insert (\"27\")");
  menu_append_item ($3, "29. Electric Muted Guitar", "insert (\"28\")");
  menu_append_item ($3, "30. Overdriven Guitar", "insert (\"29\")");
  menu_append_item ($3, "31. Distortion Guitar", "insert (\"30\")");
  menu_append_item ($3, "32. Guitar Harmonics", "insert (\"31\")");
  menu_append_popup (menu, "&Bass");
  $3 = sprintf ("%s.&Bass", menu);
  menu_append_item ($3, "33. Acoustic Bass", "insert (\"32\")");
  menu_append_item ($3, "34. Electric Bass (finger)", "insert (\"33\")");
  menu_append_item ($3, "35. Electric Bass (pick)", "insert (\"34\")");
  menu_append_item ($3, "36. Fretless Bass", "insert (\"35\")");
  menu_append_item ($3, "37. Slap Bass 1", "insert (\"36\")");
  menu_append_item ($3, "38. Slap Bass 2", "insert (\"37\")");
  menu_append_item ($3, "39. Synth Bass 1", "insert (\"38\")");
  menu_append_item ($3, "40. Synth Bass 2", "insert (\"39\")");
  menu_append_popup (menu, "&Solo Strings");
  $3 = sprintf ("%s.&Solo Strings", menu);
  menu_append_item ($3, "41. Violin", "insert (\"40\")");
  menu_append_item ($3, "42. Viola", "insert (\"41\")");
  menu_append_item ($3, "43. Cello", "insert (\"42\")");
  menu_append_item ($3, "44. Contrabass", "insert (\"43\")");
  menu_append_item ($3, "45. Tremolo Strings", "insert (\"44\")");
  menu_append_item ($3, "46. Pizzicato Strings", "insert (\"45\")");
  menu_append_item ($3, "47. Orchestra Strings", "insert (\"46\")");
  menu_append_item ($3, "48. Timpani", "insert (\"47\")");
  menu_append_popup (menu, "&Ensemble");
  $3 = sprintf ("%s.&Ensemble", menu);
  menu_append_item ($3, "49. Strings Ensemble 1", "insert (\"48\")");
  menu_append_item ($3, "50. Strings Ensemble 2", "insert (\"49\")");
  menu_append_item ($3, "51. SynthStrings 1", "insert (\"50\")");
  menu_append_item ($3, "52. SynthStrings 2", "insert (\"51\")");
  menu_append_item ($3, "53. Choir Aahs", "insert (\"52\")");
  menu_append_item ($3, "54. Choir Oohs", "insert (\"53\")");
  menu_append_item ($3, "55. Synth Voice", "insert (\"54\")");
  menu_append_item ($3, "56. Orchestra Hit", "insert (\"55\")");
  menu_append_popup (menu, "B&rass");
  $3 = sprintf ("%s.B&rass", menu);
  menu_append_item ($3, "57. Trumpet", "insert (\"56\")");
  menu_append_item ($3, "58. Trombone", "insert (\"57\")");
  menu_append_item ($3, "59. Tuba", "insert (\"58\")");
  menu_append_item ($3, "60. Muted Trumpet", "insert (\"59\")");
  menu_append_item ($3, "61. French Horn", "insert (\"60\")");
  menu_append_item ($3, "62. Brass Section", "insert (\"61\")");
  menu_append_item ($3, "63. SynthBrass 1", "insert (\"62\")");
  menu_append_item ($3, "64. SynthBrass 2", "insert (\"63\")");
  menu_append_popup (menu, "Ree&d");
  $3 = sprintf ("%s.Ree&d", menu);
  menu_append_item ($3, "65. Soprano Sax", "insert (\"65\")");
  menu_append_item ($3, "66. Alto Sax", "insert (\"64\")");
  menu_append_item ($3, "67. Tenor Sax", "insert (\"66\")");
  menu_append_item ($3, "68. Baritone Sax", "insert (\"67\")");
  menu_append_item ($3, "69. Oboe", "insert (\"68\")");
  menu_append_item ($3, "70. English Horn", "insert (\"69\")");
  menu_append_item ($3, "71. Bassoon", "insert (\"70\")");
  menu_append_item ($3, "72. Clarinet", "insert (\"71\")");
  menu_append_popup (menu, "P&ipe");
  $3 = sprintf ("%s.P&ipe", menu);
  menu_append_item ($3, "73. Piccolo", "insert (\"72\")");
  menu_append_item ($3, "74. Flute", "insert (\"73\")");
  menu_append_item ($3, "75. Recorder", "insert (\"74\")");
  menu_append_item ($3, "76. Pan FLute", "insert (\"75\")");
  menu_append_item ($3, "77. Blown Bottle", "insert (\"76\")");
  menu_append_item ($3, "78. Shakuhachi", "insert (\"77\")");
  menu_append_item ($3, "79. Whistle", "insert (\"78\")");
  menu_append_item ($3, "80. Ocarina", "insert (\"79\")");
  menu_append_popup (menu, "Synth &Lead");
  $3 = sprintf ("%s.Synth &Lead", menu);
  menu_append_item ($3, "81. Lead 1 (square)", "insert (\"80\")");
  menu_append_item ($3, "82. Lead 1 (sawtooth)", "insert (\"81\")");
  menu_append_item ($3, "83. Lead 1 (calliope)", "insert (\"82\")");
  menu_append_item ($3, "84. Lead 1 (chiff)", "insert (\"83\")");
  menu_append_item ($3, "85. Lead 1 (charang)", "insert (\"84\")");
  menu_append_item ($3, "86. Lead 1 (voice)", "insert (\"85\")");
  menu_append_item ($3, "87. Lead 1 (fifths)", "insert (\"86\")");
  menu_append_item ($3, "88. Lead 1 (bass+lead)", "insert (\"87\")");
  menu_append_popup (menu, "Synth P&ad");
  $3 = sprintf ("%s.Synth P&ad", menu);
  menu_append_item ($3, "89. Pad 1 (new age)", "insert (\"88\")");
  menu_append_item ($3, "90. Pad 2 (warm)", "insert (\"89\")");
  menu_append_item ($3, "91. Pad 3 (polysynth)", "insert (\"90\")");
  menu_append_item ($3, "92. Pad 4 (choir)", "insert (\"91\")");
  menu_append_item ($3, "93. Pad 5 (bowed)", "insert (\"92\")");
  menu_append_item ($3, "94. Pad 6 (metallic)", "insert (\"93\")");
  menu_append_item ($3, "95. Pad 7 (halo)", "insert (\"94\")");
  menu_append_item ($3, "96. Pad 8 (sweep)", "insert (\"95\")");
  menu_append_popup (menu, "S&ynth Effects");
  $3 = sprintf ("%s.S&ynth Effects", menu);
  menu_append_item ($3, " 97. FX 1 (rain)", "insert (\"96\")");
  menu_append_item ($3, " 98. FX 1 (soundtrack)", "insert (\"97\")");
  menu_append_item ($3, " 99. FX 1 (crystal)", "insert (\"98\")");
  menu_append_item ($3, "100. FX 1 (atmosphere)", "insert (\"99\")");
  menu_append_item ($3, "101. FX 1 (brightness)", "insert (\"100\")");
  menu_append_item ($3, "102. FX 1 (goblins)", "insert (\"101\")");
  menu_append_item ($3, "103. FX 1 (echoes)", "insert (\"102\")");
  menu_append_item ($3, "104. FX 1 (sci-fi)", "insert (\"103\")");
  menu_append_popup (menu, "Et&hnic");
  $3 = sprintf ("%s.Et&hnic", menu);
  menu_append_item ($3, "105. Sitar", "insert (\"104\")");
  menu_append_item ($3, "106. Banjo", "insert (\"105\")");
  menu_append_item ($3, "107. Shamisen", "insert (\"106\")");
  menu_append_item ($3, "108. Koto", "insert (\"107\")");
  menu_append_item ($3, "109. Kalimba", "insert (\"108\")");
  menu_append_item ($3, "110. Bagpipe", "insert (\"109\")");
  menu_append_item ($3, "111. Fiddle", "insert (\"110\")");
  menu_append_item ($3, "112. Shanai", "insert (\"111\")");
  menu_append_popup (menu, "Percussi&ve");
  $3 = sprintf ("%s.Percussi&ve", menu);
  menu_append_item ($3, "113. Tinkle Bell", "insert (\"112\")");
  menu_append_item ($3, "114. Agogo", "insert (\"113\")");
  menu_append_item ($3, "115. Steel Drums", "insert (\"114\")");
  menu_append_item ($3, "116. Woodblock", "insert (\"115\")");
  menu_append_item ($3, "117. Taiko Drum", "insert (\"116\")");
  menu_append_item ($3, "118. Melodic Tom", "insert (\"117\")");
  menu_append_item ($3, "119. Synth Drum", "insert (\"118\")");
  menu_append_item ($3, "120. Reverse Cymbal", "insert (\"119\")");
  menu_append_popup (menu, "S&ounds Effects");
  $3 = sprintf ("%s.S&ounds Effects", menu);
  menu_append_item ($3, "121. Guitar Fret Noise", "insert (\"120\")");
  menu_append_item ($3, "122. Breath Noise", "insert (\"121\")");
  menu_append_item ($3, "123. Seashore", "insert (\"122\")");
  menu_append_item ($3, "124. Bird Tweet", "insert (\"123\")");
  menu_append_item ($3, "125. Telephone Ring", "insert (\"124\")");
  menu_append_item ($3, "126. Helicopter", "insert (\"125\")");
  menu_append_item ($3, "127. Applause", "insert (\"126\")");
  menu_append_item ($3, "128. Gunshot", "insert (\"127\")");
  menu_append_popup (menu, "Percussions &1");
  $3 = sprintf ("%s.Percussions &1", menu);
  menu_append_item ($3, "35. B,,, Acoustic Bass Drum", "insert (\"34\")");
  menu_append_item ($3, "36. C,,  Bass Drum 1", "insert (\"35\")");
  menu_append_item ($3, "37. ^C,, Side Stick", "insert (\"36\")");
  menu_append_item ($3, "38. D,,  Acoustic Snare", "insert (\"37\")");
  menu_append_item ($3, "39. ^D,, Hand Clap", "insert (\"38\")");
  menu_append_item ($3, "40. E,,  Electric Snare", "insert (\"39\")");
  menu_append_item ($3, "41. F,,  Low Floor Tom", "insert (\"40\")");
  menu_append_item ($3, "42. ^F,, Closed Hi-Hat", "insert (\"41\")");
  menu_append_item ($3, "43. G,,  High Floor Tom", "insert (\"42\")");
  menu_append_item ($3, "44. ^G,, Pedal Hi-Hat", "insert (\"43\")");
  menu_append_item ($3, "45. A,,  Low Tom", "insert (\"44\")");
  menu_append_item ($3, "46. ^A,, Open Hi-Hat", "insert (\"45\")");
  menu_append_item ($3, "47. B,,  Low-Mid Tom", "insert (\"46\")");
  menu_append_item ($3, "48. C,   Hi-Mid Tom", "insert (\"47\")");
  menu_append_item ($3, "49. ^C,  Crash Cymbal 1", "insert (\"48\")");
  menu_append_item ($3, "50. D,   High Tom", "insert (\"49\")");
  menu_append_popup (menu, "Percussions &2");
  $3 = sprintf ("%s.Percussions &2", menu);
  menu_append_item ($3, "51. ^D,  Ride Cymbal 1", "insert (\"50\")");
  menu_append_item ($3, "52. E,   Chinese Cymbal", "insert (\"51\")");
  menu_append_item ($3, "53. F,   Ride Bell", "insert (\"52\")");
  menu_append_item ($3, "54. ^F,  Tambourine", "insert (\"53\")");
  menu_append_item ($3, "55. G,   Splash Cymbal", "insert (\"54\")");
  menu_append_item ($3, "56. ^G,  Cowbell", "insert (\"55\")");
  menu_append_item ($3, "57. A,   Crash Cymbal 2", "insert (\"56\")");
  menu_append_item ($3, "58. ^A,  Vibraslap", "insert (\"57\")");
  menu_append_item ($3, "59. B,   Ride Cymbal 2", "insert (\"58\")");
  menu_append_item ($3, "60. C    Hi Bongo", "insert (\"59\")");
  menu_append_item ($3, "61. ^C   Low Bongo", "insert (\"60\")");
  menu_append_item ($3, "62. D    Mute Hi Conga", "insert (\"61\")");
  menu_append_item ($3, "63. ^D   Open Hi Conga", "insert (\"62\")");
  menu_append_item ($3, "64. E    Low Conga", "insert (\"63\")");
  menu_append_item ($3, "65. F    High Timbale", "insert (\"64\")");
  menu_append_item ($3, "66. ^F   Low Timbale", "insert (\"65\")");
  menu_append_popup (menu, "Percussions &3");
  $3 = sprintf ("%s.Percussions &3", menu);
  menu_append_item ($3, "67. G    High Agogo", "insert (\"66\")");
  menu_append_item ($3, "68. ^G   Low Agogo", "insert (\"67\")");
  menu_append_item ($3, "69. A    Cabasa", "insert (\"68\")");
  menu_append_item ($3, "70. ^A   Maracas", "insert (\"69\")");
  menu_append_item ($3, "71. B    Short Whistle", "insert (\"70\")");
  menu_append_item ($3, "72. c    Long Whistle", "insert (\"71\")");
  menu_append_item ($3, "73. ^c   Short Guiro", "insert (\"72\")");
  menu_append_item ($3, "74. d    Long Guiro", "insert (\"73\")");
  menu_append_item ($3, "75. ^d   Claves", "insert (\"74\")");
  menu_append_item ($3, "76. e    Hi Wood Block", "insert (\"75\")");
  menu_append_item ($3, "77. f    Low Wood Block", "insert (\"76\")");
  menu_append_item ($3, "78. ^f   Mute Cuica", "insert (\"77\")");
  menu_append_item ($3, "79. g    Open Cuica", "insert (\"78\")");
  menu_append_item ($3, "80. ^g   Mute Triangle", "insert (\"79\")");
  menu_append_item ($3, "81. a    Open Triangle", "insert (\"80\")");
}

% -----

define abc_extract_voice ()
{
  variable line, voice, new_file, output_line, key_found, tmp, over = FALSE;
  voice = read_mini ("Name of the voice to extract?", Null_String, "1");
  new_file = path_sans_extname (dircat (abc_file_dir, abc_file)) +
    "-" + voice + ".abc";
  
  () = system (abc_xvoice_cmd + " -v" + voice + " " + 
               dircat (abc_file_dir, abc_file) + " " + new_file);
  flush ("Voice written to new file " + new_file);
}

private define is_comment_line ()
{
  variable found = FALSE;
  push_spot ();
  bol ();
  if ('%' == what_char ())
    found = TRUE;
  if (found) {
    () = right (1);
    if ('%' == what_char ()) % it's a %% command
      found = FALSE;
  }
  pop_spot ();
  return found;
}

% -----

define abc_reformat_bars ()
{
  variable
    i = 0,
    col = 0,
    max_bar_col = 0,
    nb = 0,
    num_bars = 9999;
  
  push_spot ();
  
  % check if there are comment lines above and below
  while ( (0 == is_comment_line ()) and (0 == bobp ()) )
    i = up (1);
  if (0 == i) { % top of buffer reached, no %\n line found
    beep ();
    flush ("Can't find delimiting comment lines!");
    pop_spot ();
    return;
  }
  pop_spot ();
  push_spot ();
  while ( (0 == is_comment_line ()) and (0 == eobp ()) )
    i = down (1);
  if (0 == i) { % bottom of buffer reached, no %\n line found
    beep ();
    flush ("Can't find delimiting comment lines!");
    pop_spot ();
    return;
  }
  pop_spot ();
  push_spot ();  
  
  % go up to the first line starting in %
  while (0 == is_comment_line ())
    () = up (1);
  () = down (1);
  
  % find the least number of bars to reformat
  while (0 == is_comment_line ()) {
    bol ();
    !if (ffind ("w:")) { % skip w: lines
      nb = 0;
      while ( () == ffind ("|")) {
        nb++;
        () = right (1);
      }
      if ( (nb < num_bars) and (0 != nb) ) % beware of fields!
        num_bars = nb;
    }
    () = down (1);
  }
  () = up (1);
  
  for (i = 0; i < num_bars; i++) {
    
    % go up to the first line starting in %
    while (0 == is_comment_line ())
      () = up (1);
    
    % find the rightmost measure bar
    do {
      () = down (1);
      bol ();
      !if (ffind ("w:")) { % skip w: lines
        % skip previously aligned bars
        loop (i) {
          () = ffind ("|");
          !if (eolp ())
            () = right (1);
        }
        () = ffind ("|");
        col = what_column ();
        if (col > max_bar_col)
          max_bar_col = col;
      } % !if
    } while (0 == is_comment_line ()); % next % line reached
    
    % go back
    () = up (1);
    while (0 == is_comment_line ())
      () = up (1);
    () = down (1);
    
    % now align the bars
    while (0 == is_comment_line ()) {
      bol ();
      !if (ffind ("w:")) { % skip w: lines
        % skip previously aligned bars
        loop (i) {
          () = ffind ("|");
          !if (eolp ())
            () = right (1);
        }
        if (ffind ("|"))
          insert_spaces (max_bar_col - what_column ());
      }
      () = down (1);
    }
    
    % go back
    () = up (1);
  
  } % for
  
  pop_spot ();
}

% -----

define abc_renumber_tunes ()
{
  variable i, number = "1", found = FALSE;
  push_spot ();
  number = read_mini ("Start renumbering from...?", "1", "1");
  bob ();
  i = integer (number);
  do {
    found = re_fsearch ("^X:"); % get index number
    if (FALSE == found)
      break;
    () = right (2);
    call ("kill_line");
    insert (string (i));
    i = i + 1;
  } while (found != FALSE);
  pop_spot ();
  flush ("Tunes renumbered.");
}

% -----

% latin1 or utf8 conversion

if (0 == strcmp (abc_encoding, "latin1"))
  evalfile ("abc-latin1");
else
  evalfile ("abc-utf8");

% -----

% UNIX only!

define abc_stop_playing ()
{
#ifdef UNIX
  variable
    pid,
    cmd = "/bin/ps uax | grep " + abc_player_cmd + 
     " | grep " + "'" + abc_midi_file + "'" + " | awk '{print $2}' ";
  setbuf ("*tmp*");
  () = run_shell_cmd (cmd);
  bob ();
  pid = get_word ();
  % maybe just "pkill " + abc_player_cmd would do
  () = run_shell_cmd ("kill -9 " + pid);
  set_buffer_modified_flag (0);
  delbuf ("*tmp*");
#else
  % will it work?
  msw_system (abc_player_cmd + " NUL", 0, 0);
#endif
  flush ("MIDI player stopped.");
}

% let's declare these functions; load them only if needed
define research_search_function ();
define re_replace_function ();
define replace_with_query ();

define abc_remove_spaces ()
{
  variable
    regexp = "[ ]*|[ ]*",
    repl = "|";
  
  push_spot ();
  require ("regexp");
  bol ();
  push_mark ();
  () = down (1);
  narrow_to_region ();
  bob ();
  replace_with_query (&research_search_function, regexp, repl, 0, 
                      &re_replace_function);
  widen_region ();
  pop_spot ();
}

% -----

define abc_customise (var)
{
  variable tmp, value;
  tmp = sprintf ("New value (now \"%s\"):", string (eval (var)));
  value = (read_mini (tmp, "", ""));
  !if (strcmp (var, "beep_duration"))
    tmp = string (value);
  else
    tmp = "\"" + string (value) + "\"";
  
  if ( (0 == strcmp (var, "abc_compile_ps_flags")) and
       (is_substr (tmp, "-O")) )
    error ("You can't specify -O, sorry.");
       
  eval (var + "=" + tmp);
}

define set_ps_converter_flags ()
{
  variable tmp;
  push_spot ();
  bob ();
  if (bol_fsearch ("%%abcm2ps")) {
    % skip_chars ("2:a-z");
    () = right (9);
    push_mark ();
    eol ();
    tmp = bufsubstr ();
    if (is_substr (tmp, "-O"))
      error ("PostScript converter flags contain '-O': ignored.");
    else {
      pop_mark_0 ();
      abc_compile_ps_flags = tmp;
      vmessage ("PostScript converter flags set.");
    }
  }
  pop_spot ();
}

%}}}

% ----- quick reference %{{{

static variable ref_buffer = "*ABC reference*";

define abc_reference ()
{
  sw2buf (ref_buffer);
  insert 
    ("ABC Quick Reference. " + 
     "Press 'q' to exit.\n\n");
  set_buffer_modified_flag (0);
  bob ();
}

define abc_quick_reference ()
{
  variable mode = "browsing";
  !if (keymap_p (mode))
    make_keymap (mode);
  definekey ("delbuf(whatbuf())", "q", mode);
  abc_reference ();
  sw2buf (ref_buffer);
  set_readonly (1);
  use_keymap (mode);
  set_mode (mode, 0);
}

%}}}

% ----- indexing %{{{

static variable line_mark,
  tune_index = "*index*",
  index_prev_line = 0;

private define update_index_hook ()
{
  variable start;
  line_mark = create_line_mark (color_number ("menu_selection"));
  eol ();
  () = left (1);
  define_word ("0-9");
  bskip_word ();
  push_mark ();
  skip_word ();
  start = bufsubstr ();
  pop_mark_0 ();
  call ("other_window");
  bob ();
  () = down (integer (start) + window_info ('r') / 2);
  call ("other_window");
}

define abc_extract ()
{
  variable tmp, start;
  index_prev_line = what_line;
  % extract the X: number
  bol ();
  define_word ("0-9");
  skip_word ();
  push_mark ();
  bskip_word ();
  abc_file_index = bufsubstr (); % X:
  tmp = extract_element (abc_file, 0, '.') + abc_file_index;
  abc_ps_file = tmp + ".ps";
  abc_midi_file = tmp + ".mid";
  
  % FIX HERE
  
  % abc_compile_ps_flags = sprintf ("-O %s -e %s", abc_ps_file, index);
  % abc_compile_midi_flags = sprintf ("%s -o %s", index, abc_midi_file);
  
  pop_mark_0 ();
  eol ();
  () = left (1);
  define_word ("0-9");
  bskip_word ();
  push_mark ();
  skip_word ();
  start = bufsubstr ();
  pop_mark_0 ();
  % when the user has selected a tune, restrict editing to 
  % that tune using narrow ()
  sw2buf (abc_file);
  call ("one_window");
  goto_line (integer (start));
  push_mark ();
  while (strlen (line_as_string ()))
    () = down (1);
  % FIXME:
  % what if there no empty line at end of file?
  narrow ();
  selection = TRUE;
  bob ();
  delbuf (tune_index);
  % abc_add_file_menu ();
  abc_mode ();
}

define abc_make_list ()
{
  variable kline, mline, rline,
    index, start, metre, rhythm, title, key, found = TRUE;
  
  bob ();
  sw2buf (tune_index);
  set_readonly (0);
  erase_buffer ();
  sw2buf (abc_file);
  
  do { % while (found != FALSE)
    % get index
    found = re_fsearch ("^X:");
    if (FALSE == found)
      break;
    start = what_line;
    () = right (2);
    define_word ("0-9");
    skip_word ();
    push_mark ();
    bskip_word ();
    index = bufsubstr ();
    pop_mark_0 ();
    () = down (1);
    bol ();
    % get title
    if (FALSE != bol_fsearch ("T:")) {
      () = right (2);
      skip_chars (" \t");
      push_mark ();
      eol ();
      title = bufsubstr ();
      pop_mark_0 ();
    }
    else
      title = "(no title)";
    
    if (strlen(title) > 35)
      title = substr (title, 1, 35) + "...";

    push_spot ();
    () = bol_fsearch ("K:");
    kline = what_line;
    pop_spot ();
    
    % first of all, get the content of the M: field
    % we'll use it as default value if R: is missing
    
    push_spot ();
    metre = "";
    if (FALSE != bol_fsearch ("M:")) { % found
      mline = what_line;
      if (mline < kline) { % it belongs to current tune
        () = right (2);
        skip_chars (" \t");
        push_mark ();
        eol ();
        metre = bufsubstr ();
        pop_mark_0 ();
      }
      else
	metre = "(no metre)";
    }
    else
      metre = "(no metre)";
    pop_spot (); % come back from M:
    
    % get rhythm
    % if an R: field is found, but its line is > than the line of
    % the next K: field, then R: belongs to another tune. Take note.

    push_spot ();
    if (FALSE != bol_fsearch ("R:")) { % found
      rline = what_line;
      if (rline < kline) { % it belongs to current tune
        () = right (2);
        skip_chars (" \t");
        push_mark ();
        eol ();
	% what about, say, "bourr\`ee \`a 2 temps"?
	% skip_chars ("0-9A-Za-z");
        rhythm = bufsubstr ();
        pop_mark_0 ();
      }
      else
	rhythm = "(no rhythm)";
    }
    else
      rhythm = "(no rhythm)";
    
    if (strlen(rhythm) > 15)
      rhythm = substr (rhythm, 1, 15);
    
    pop_spot (); % come back from R:
    
    % if we have no rhythm, let's use metre
    
    !if (strcmp (rhythm, "(no rhythm)"))
      rhythm = metre;
    
    % get key
    () = bol_fsearch ("K:");
    () = right (2);
    skip_chars (" \t");
    push_mark ();
    % eol ();
    skip_chars ("A-Za-z");
    key = bufsubstr ();
    pop_mark_0 ();
    sw2buf (tune_index);
    eob ();
    insert ("X:" + index);
    insert_spaces (6 - strlen (index));
    insert (rhythm);
    insert_spaces (16 - strlen (rhythm));
    insert ("K:" + key);
    insert_spaces (6 - strlen (key));
    insert (title + " (" + string (start) + ")\n");
    sw2buf (abc_file);
  } while (found != FALSE);
  
  bob ();
  sw2buf (tune_index);
  bob ();
  set_status_line (" " + string (num_of_tunes) +
                   " tunes. <Return> = Select;" +
                   " 'v' = View PS; 'l' = Listen to MIDI", 0);
  set_buffer_modified_flag (0);
}

define abc_quit_index_mode ()
{
  delbuf (tune_index);
  sw2buf (abc_file);
  call ("one_window");
  abc_mode ();
}

static variable index_mode_entered = FALSE;

define abc_index_mode ()
{
  variable mode = "abc_index";
  !if (keymap_p (mode))
     make_keymap (mode);
  create_syntax_table (mode);
  () = define_keywords (mode, "K:X:", 2);
  define_syntax ("KX:", '0', mode);
  definekey ("abc_quit_index_mode", "q", mode);
  definekey ("abc_extract", "\r", mode);
  definekey ("abc_extract (); abc_view_ps (); abc_index_mode", "v", mode);
  definekey ("abc_extract (); abc_view_ps (); abc_index_mode", "V", mode);
  definekey ("abc_extract (); abc_view_ps (); abc_index_mode", Key_F10, mode);
  definekey ("abc_extract (); abc_listen_to_midi (); abc_index_mode",
             "l", mode);
  definekey ("abc_extract (); abc_listen_to_midi (); abc_index_mode",
             "L", mode);
  definekey ("abc_extract (); abc_listen_to_midi (); abc_index_mode",
             Key_F12, mode);
  definekey ("abc_stop_playing", "z", mode);
  definekey ("abc_stop_playing", "Z", mode);
  (abc_file, abc_file_dir,,) = getbuf_info (); % current buffer
  % if we're coming back to the index from a previously
  % selected tune, then make an index of the entire file.
  if (TRUE == selection) {
    widen ();
    selection = FALSE;
  }
  abc_make_list ();
  if (0 != index_prev_line)
    goto_line (index_prev_line);
  
  call ("split_window");
  call ("other_window");
  loop (window_info ('r') / 2)
    enlargewin ();
  call ("other_window");
  sw2buf (abc_file);
  call ("other_window");
  sw2buf (tune_index);
  
  sw2buf (tune_index);
  set_buffer_hook ("update_hook", &update_index_hook);
  set_readonly (1);
  use_keymap (mode);
  use_syntax_table (mode);
  set_mode (mode, 0);
  index_mode_entered = TRUE;
  set_buffer_hook ("mouse_2click", "abc_extract");
  run_mode_hooks ("abc_index_mode_hook");
}

define count_tunes ()
{
  push_spot ();
  bob ();
  num_of_tunes = 0;
  while (0 != bol_fsearch ("X:")) {
    () = down (1);
    num_of_tunes++;
  }
  pop_spot ();
}

%}}}

% ----- menu %{{{

private define init_menu (menu)
{
  % templates
  menu_append_popup (menu, "&Templates");
  $1 = sprintf ("%s.&Templates", menu);
  menu_append_item ($1, "&1. Single Voice","abc_template_solo()");
  menu_append_item ($1, "&2. Duet", "abc_template_duet");
  menu_append_item ($1, "&3. Trio", "abc_template_trio");
  menu_append_item ($1, "&4. SATB", "abc_template_satb");
  menu_append_item ($1, "&5. Quintet", "abc_template_quintet");
  menu_append_item ($1, "&6. Sextet", "abc_template_sextet");
  menu_append_item ($1, "&7. Piano (2 voices)", "abc_template_piano_2");
  menu_append_item ($1, "&8. Piano (4 voices)", "abc_template_piano_4");
  menu_append_item ($1, "&9. Soloist + Piano (2 voices)",
                    "abc_template_solo_piano_2");
  menu_append_item ($1, "&A. Soloist + Piano (4 voices)",
                    "abc_template_solo_piano_4");
  menu_append_item ($1, "&B. SATB + Piano (2 voices)",
                    "abc_template_satb_piano_2");
  menu_append_item ($1, "&C. SATB + Piano (4 voices)",
                    "abc_template_satb_piano_4");
  menu_append_item ($1, "&D. Organ", "abc_template_organ");
  menu_append_item ($1, "Customise &Template", "abc_customise_system");
  % abcpp options
  menu_append_popup (menu, "&abcpp Options");
  $1 = sprintf ("%s.&abcpp Options", menu);
  menu_append_item ($1, "&1. #defines for SATB", "abc_abcpp_satb");
  menu_append_item ($1, "&2. #defines for SSATTB", "abc_abcpp_ssattb");
  menu_append_item ($1, "&3. #doremi", "abc_abcpp_doremi");
  menu_append_item ($1, "&4. #include <fancyheader.abp>",
                    "abc_abcpp_inc (\"fancyheader\")");
  % abcm2ps parameters
  menu_append_popup (menu, "abc&m2ps Options");
  $1 = sprintf ("%s.abc&m2ps Options", menu);
  menu_append_popup ($1, "&Page Format");
  $2 = sprintf ("%s.&Page Format", $1);
  abc_abcm2ps_page ($2);
  menu_append_popup ($1, "&Font Settings");
  $2 = sprintf ("%s.&Font Settings", $1);
  abc_abcm2ps_font ($2);
  menu_append_popup ($1, "&Spacings");
  $2 = sprintf ("%s.&Spacings", $1);
  abc_abcm2ps_spaces ($2);
  menu_append_popup ($1, "&Text");
  $2 = sprintf ("%s.&Text", $1);
  abc_abcm2ps_text ($2);
  menu_append_popup ($1, "&Measures");
  $2 = sprintf ("%s.&Measures", $1);
  abc_abcm2ps_measures ($2);
  menu_append_popup ($1, "M&isc");
  $2 = sprintf ("%s.M&isc", $1);
  abc_abcm2ps_misc ($2);
  menu_append_popup ($1, "Font &List");
  $2 = sprintf ("%s.Font &List", $1);
  abc_make_font_list ($2);
  menu_append_item ($1, "&Columns", "abc_abcm2ps_columns");
  % abc2midi options
  menu_append_popup (menu, "abc2m&idi Options");
  $1 = sprintf ("%s.abc2m&idi Options", menu);
  menu_append_popup ($1, "MIDI &Instruments");
  $2 = sprintf ("%s.MIDI &Instruments", $1);
  abc_midi_instruments ($2);
  abc_abc2midi_options ($1);
  % JedABC options
  menu_append_popup (menu, "JedABC &Options");
  $1 = sprintf ("%s.JedABC &Options", menu);
  menu_append_item ($1, "Toggle &Note Mode", "toggle_note_mode");
  menu_append_item ($1, "Toggle &Beeps On/Off", "toggle_do_beeps");
  menu_append_item ($1, "Toggle &Autobar Mode", "toggle_autobar_mode");
  menu_append_item ($1, "Toggle &Formatting", "toggle_bar_reformat");
  menu_append_item ($1, "Toggle &latin1 Font", "toggle_latin1");
  menu_append_item ($1, "Make &Index", "abc_index_mode");
  menu_append_item ($1, "Ren&umber Tunes", "abc_renumber_tunes");
  menu_append_item ($1, "Set &Meter", "abc_set_meter");
  menu_append_item ($1, "&Do-re-mi to c-d-e", "abc_doremi2cde");
  menu_append_item ($1, "&Reformat Measures", "abc_reformat_bars");
  menu_append_item ($1, "Remove Redundant S&paces", "abc_remove_spaces");
  menu_append_item ($1, "Compose &K: Field", "abc_key");
  menu_append_item ($1, "Insert &System", "abc_insert_system");
  menu_append_item ($1, "Insert &Tuplet", "key_opentuplet");
  % ABC notation
  menu_append_popup (menu, "ABC &Notation");
  $1 = sprintf ("%s.ABC &Notation", menu);
  menu_append_popup ($1, "ABC &Fields");
  $2 = sprintf ("%s.ABC &Fields", $1);
  abc_list_fields ($2);
  menu_append_popup ($1, "&Notes");
  $2 = sprintf ("%s.&Notes", $1);
  abc_note_features ($2);
  menu_append_popup ($1, "&Decorations");
  $2 = sprintf ("%s.&Decorations", $1);
  abc_decorations ($2);
  menu_append_popup ($1, "&Predefined Decorations");
  $2 = sprintf ("%s.&Predefined Decorations", $1);
  abc_predefined_fields ($2);
  menu_append_popup ($1, "&Bars");
  $2 = sprintf ("%s.&Bars", $1);
  abc_bars ($2);
  menu_append_popup ($1, "&Lyrics");
  $2 = sprintf ("%s.&Lyrics", $1);
  abc_lyrics ($2);
  % Customisation
  menu_append_popup (menu, "&Customise Options");
  $1 = sprintf ("%s.&Customise Options", menu);
  menu_append_item ($1, "&1. Language", 
		    "abc_customise (\"abc_language\")");
  menu_append_item ($1, "&2. Preprocessor",
		    "abc_customise (\"abc_preprocess_cmd\")");
  menu_append_item ($1, "&3. PS Converter",
		    "abc_customise (\"abc_compile_ps_cmd\")");
  menu_append_item ($1, "&4. Voice Extraction",
		    "abc_customise (\"abc_xvoice_cmd\")");
  menu_append_item ($1, "&5. PS Converter Flags",
		    "abc_customise (\"abc_compile_ps_flags\")");
  menu_append_item ($1, "&6. MIDI Converter",
		    "abc_customise (\"abc_compile_midi_cmd\")");
  menu_append_item ($1, "&7. MIDI Converter Flags",
		    "abc_customise (\"abc_compile_midi_flags\")");
  menu_append_item ($1, "&8. Viewer",
		    "abc_customise (\"abc_view_cmd\")");
  menu_append_item ($1, "&9. Viewer Flags",
		    "abc_customise (\"abc_viewer_flags\")");
  menu_append_item ($1, "&A. Player",
		    "abc_customise (\"abc_player_cmd\")");
  menu_append_item ($1, "&B. Player Flags",
		    "abc_customise (\"abc_player_flags\")");
  menu_append_item ($1, "&C. Beep Duration",
		    "abc_customise (\"beep_duration\")");
  % the rest
  menu_append_separator (menu);
  menu_append_item (menu, "Convert to &Postscript",
                    "abc_convert_to_ps (1)");
  menu_append_item (menu, "Convert to &MIDI", "abc_convert_to_midi (1)");
  menu_append_item (menu, "&View Postscript", "abc_view_ps");
  menu_append_item (menu, "&Listen to Midi", "abc_listen_to_midi");
% #ifdef UNIX
  menu_append_item (menu, "&Stop Playing", "abc_stop_playing");
% #endif
  menu_append_item (menu, "E&xtract Voice", "abc_extract_voice");
  % menu_append_item (menu, "Transp&ose Tune", "abc_transpose");
  menu_append_separator (menu);
  menu_append_item (menu, "&Help on JedABC", "abc_help");
}

%}}}

% ----- syntax table %{{{

create_syntax_table (mode);
!if (keymap_p (mode)) 
  make_keymap (mode);

() = define_keywords (mode, "ABCDEFGabcdefg", 1);
() = define_keywords (mode, "A:B:C:D:DOF:FAG:H:I:K:L:LAM:MIN:O:P:Q:R:" + 
                      "RES:SIT:U:V:W:X:Z:dofalamiresiw:", 2);
() = define_keywords (mode, "SOLsol", 3);

define_syntax ("%", "", '%', mode);        % comments
define_syntax ("([{!", ")]}!", '(', mode); % pairs
define_syntax ('"', '"', mode);            % strings
define_syntax ('!', '"', mode);            % decorations
define_syntax ('\\', '\\', mode);          % escape character
define_syntax ("a-zA-Z:", 'w', mode);      % identifiers
define_syntax ("0-9<>/", '0', mode);       % "numbers" - duration
define_syntax (",'_^=|", ',', mode);       % delimiters
define_syntax ('#', '#', mode);            % abcpp directives
set_syntax_flags (mode, 4);

#ifdef HAS_DFA_SYNTAX
%%% DFA_CACHE_BEGIN %%%
private define setup_dfa_callback (name)
{
  dfa_enable_highlight_cache ("abc.dfa", name);
  dfa_define_highlight_rule ("^#", "PQpreprocess", name);
  dfa_define_highlight_rule ("^%%", "PQpreprocess", name);
  dfa_define_highlight_rule ("%.*$", "comment", name);
  
  % the TAB character
  % dfa_define_highlight_rule ("\"[\t]+[A-Za-z$]+\.", "string", name);
  
  % fields
  dfa_define_highlight_rule ("^[ \t]*[A-J,L-S,U,W,Y,Zh-u,w-z]+:[^%]*",
                             "normal", name);
  % inline fields
  dfa_define_highlight_rule ("\\[[A-J,L-U,W,Y,Z]+:.*\\]",
                             "Qoperator", name);
  % some fields are more equal than others...
  dfa_define_highlight_rule ("^[ \t]*X:[^%]*", "number", name);
  dfa_define_highlight_rule ("^[ \t]*T:[^%]*", "string", name);
  dfa_define_highlight_rule ("^[ \t]*K:[^%]*", "number", name);
  dfa_define_highlight_rule ("\\[K:[^%]*\\]", "Qnumber", name);
  dfa_define_highlight_rule ("^[ \t]*V:[^%]*", "number", name);
  dfa_define_highlight_rule ("\\[V:[^%]*\\]", "Qnumber", name);
  % strings and symbols
  dfa_define_highlight_rule ("![^!]*!", "string", name);
  dfa_define_highlight_rule ("\\+[^!]*\\+", "string", name); % duh
  dfa_define_highlight_rule ("\"[^\"]*\"", "string", name);
  dfa_define_highlight_rule ("[H-W]", "string", name);
  % notes
  dfa_define_highlight_rule ("[a-g'\\^]", "keyword", name);
  dfa_define_highlight_rule ("[A-G,_]", "keyword1", name);
  dfa_define_highlight_rule ("[\\-\\(\\)=\\[\\]\\.xyZz]", "operator", name);
  dfa_define_highlight_rule ("[\\|: ]+", "delimiter", name);
  % length
  dfa_define_highlight_rule ("[1-9<>/]", "number", name);

  dfa_build_highlight_table (name);
}
dfa_set_init_callback (&setup_dfa_callback, "abc");
%%% DFA_CACHE_END %%%
#endif

%}}}

% ----- keys %{{{

require ("keydefs");

define define_abc_keys ()
{
  definekey ("call (\"self_insert_cmd\")", "^I", mode);
  definekey_reserved ("key_opentuplet",          "(", mode);
  definekey_reserved ("toggle_autobar_mode",     "A", mode);
  definekey_reserved ("toggle_do_beeps",         "B", mode);
  undefinekey (Key_F9, mode);
  undefinekey (Key_F10, mode);
  undefinekey (Key_F11, mode);
  undefinekey (Key_F12, mode);
  definekey_reserved ("abc_convert_to_ps (1)",   "CP", mode);
  definekey ("abc_convert_to_ps (1)", Key_F9, mode);
  definekey_reserved ("abc_convert_to_midi (1)", "CM", mode);
  definekey ("abc_convert_to_midi (1)", Key_F11, mode);
  definekey_reserved ("abc_doremi2cde",          "D", mode);
  definekey_reserved ("toggle_bar_reformat",     "F", mode);
  definekey_reserved ("abc_index_mode",          "I", mode);
  definekey_reserved ("abc_key",                 "K", mode);
  definekey_reserved ("abc_listen_to_midi",      "l", mode);
  definekey ("abc_listen_to_midi", Key_F12, mode);
  definekey_reserved ("abc_set_meter",           "M", mode);
  definekey_reserved ("toggle_note_mode",        "N", mode);
  definekey_reserved ("toggle_latin1",             "L", mode);
  % definekey_reserved ("abc_transpose",           "O", mode);
  definekey_reserved ("abc_customise",           "O", mode);
  definekey_reserved ("abc_remove_spaces",       "P", mode);
  definekey_reserved ("abc_reformat_bars",       "R", mode);
  definekey_reserved ("abc_insert_system",       "S", mode);
  definekey_reserved ("abc_template_solo (); toggle_bar_reformat",
                      "T1", mode);
  definekey_reserved ("abc_template_duet",       "T2", mode);
  definekey_reserved ("abc_template_trio",       "T3", mode);
  definekey_reserved ("abc_template_satb",       "T4", mode);
  definekey_reserved ("abc_template_quintet",    "T5", mode);
  definekey_reserved ("abc_template_sextet",     "T6", mode);
  definekey_reserved ("abc_template_piano_2",    "T7", mode);
  definekey_reserved ("abc_template_piano_4",    "T8", mode);
  definekey_reserved ("abc_template_solo_piano_2", "T9", mode);
  definekey_reserved ("abc_template_solo_piano_4", "TA", mode);
  definekey_reserved ("abc_template_satb_piano_2", "TB", mode);
  definekey_reserved ("abc_template_satb_piano_4", "TC", mode);
  definekey_reserved ("abc_template_organ",      "TD", mode);
  definekey_reserved ("abc_customise_system",    "TT", mode);
  definekey_reserved ("abc_renumber_tunes",      "u", mode);
  definekey_reserved ("abc_view_ps",             "V", mode);
  definekey ("abc_view_ps", Key_F10, mode);
  definekey_reserved ("abc_extract_voice",       "X", mode);
#ifdef UNIX
  definekey_reserved ("abc_stop_playing",        "z", mode);
#endif
}


%}}}

%!%+
%\function{abc_mode}
%\synopsis{abc_mode}
%\usage{Void abc_mode ();}
%\description
% This mode is designed to facilitate the task of editing ABC files.
% It calls the function \var{abc_mode_hook} if it is defined. I suggest
% that Unix users append this entry in .jedrc:
%#v+
% define abc_mode_hook ()
% { setbuf_info (getbuf_info () | 0x400); }
%#v-
% This will cause Jed to save with CR/LF line endings, which helps
% DOS/Windows users read the abc file.
% 
% The default key bindings for this mode include:
%#v+
%    ^CA -> toggle_autobar_mode    toggles autobar mode on/off
%    ^CB -> toggle_do_beeps        toggles note playing on/off
%    ^CCM -> abc_convert_to_midi   converts the tune to MIDI
%    ^CCP -> abc_convert_to_ps     converts the tune to Postscript
%    ^CD -> abc_doremi2cde         type 'd', 'r', 'm'... to insert c,d,e...
%    ^CF -> toggle_bar_reformat    toggles bar formatting on/off
%    ^CI -> abc_index_mode         creates index of tunes
%    ^CK -> abc_key                helps you compose the K: field
%    ^CL -> abc_listen_to_midi     listen to the music as Midi
%    ^CM -> abc_set_meter          sets the M: field
%    ^CN -> toggle_note_mode       switches from header to note
%    ^CR -> abc_reformat_bars      reformat measure bars in a system
%    ^CS -> abc_insert_system      inserts an empty system
%    ^CT<1..D> -> abc_template*    writes one of the 13 available templates
%    ^CV -> abc_view_ps            view the music as Postscript
%    ^CX -> abc_extract_voice      extracts a voice to file
%#v-
% If you use IDE mode instead of Emacs mode, ^Z replaces ^C; in CUA mode,
% use ^E.
% Variables that affect this mode include:
%#v+
%    abc_compile_ps_cmd   = "abcm2ps";
%    abc_view_cmd         = "gv";
%    abc_compile_midi_cmd = "abc2midi";
%    abc_player_cmd       = "timidity";
%    beep_duration        = 100;
%#v-
% Usage: start your abc file choosing a template, then press ^CN to enable
% music mode and hear the notes as you type them. If you're used to 
% do-re-mi instead of a-b-c, press ^CD to convert automatically the notes.
% Disable note beeps, if you wish, with ^CB. To hear the notes of 
% higher/lower octaves correctly, press ^C, to invert the placement of 
% "'" or ',' + note.  Finally, convert the tune to PostScript and/or MIDI
% and view/listen to the output.
% 
% To change the default applications that abc mode uses, redefine the
% variables in your .jedrc with lines like these in your .jedrc
% (before the line that loads abc mode):
%#v+
% variable abc_compile_ps_cmd   = "jaabc2ps -o";
% variable abc_view_cmd         = "kghostview";
% variable abc_compile_midi_cmd = "abcMIDI";
% variable abc_player_cmd       = "xplaymidi";
% variable beep_duration        = 50;
%#v-
% Windows users will want to use the helper program beep.exe, whose sources 
% are embedded in abc.sl.
%!%-

static variable path_set = FALSE;

define abc_mode ()
{
  variable tmp;

#ifdef WIN32
  if (FALSE == path_set) {
    path_set = TRUE;
    tmp = getenv ("PATH");
    putenv ("PATH=c:\\abc\\bin;c:\\gstools\\gsview;" + tmp);
  }
#endif

  % first of all, check if programs are available
  check_programs ();
  
  % get some info about this file
  (abc_file, abc_file_dir,,) = getbuf_info ();
  set_mode (mode, 4);
  set_buffer_undo (1);
  use_syntax_table (mode);
  use_keymap (mode);
  ADD_NEWLINE=1;
  set_comment_info ("% ", "", 1);
  
  % is this a plain ABC or an ABC-Plus file?
  tmp = extract_element (abc_file, 1, '.');
  !if (strcmp (strlow(tmp), "abp"))
    is_abcplus = TRUE;
  
  % does it contain abcpp directives?
  push_spot (); % we may come back from index mode
  bob ();
  if (bol_fsearch ("#"))
    is_abcplus = TRUE;
  pop_spot ();
  
  % any hints for abcm2ps?
  set_ps_converter_flags ();
  
  define_abc_keys ();
  mode_set_mode_info (mode, "init_mode_menu", &init_menu);
  undefinekey (_Reserved_Key_Prefix + "'", mode);
  definekey ("abc_find_next_error", _Reserved_Key_Prefix + "'", mode);
  run_mode_hooks ("abc_mode_hook");
  
  % customisation
  if (TRUE == abc_start_with_doremi)
    abc_doremi2cde ();
  if (FALSE == abc_start_with_autobar)
    inversion = FALSE;
  if (FALSE == abc_start_with_beep)
    do_beeps = FALSE;
  if (FALSE == abc_start_with_chars)
    insert_chars = FALSE;
  toggle_characters ();
  
  % is this a collection?
  count_tunes ();
  if ( (num_of_tunes < 2) and (FALSE == selection) ) {
    % try to find out as much as possible about the last tune
    bob ();
    if (bol_fsearch ("K:") != 0) {
      % a tune already exists
      abc_guess_key ();
      abc_set_meter ();
    }
  }
  else
    if (FALSE == index_mode_entered)
      abc_index_mode ();
}

% ----- End of file abc.sl
  
