#!/bin/perl
$timestamp="Time-stamp: <1999-05-09 14:24:10 rocky>";
#    vl1-v2-decode.pl decodes/displays parts of the contents of a Yamaha
#    VL1 file which may be a single voice, a bank of 16 voices or a
#    set of banks.  This code handles only version 2-format files.
#    Copyright (C) 1996-99 R. Bernstein
#
#    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#
# Some conventions. The global variable $buf contains the raw data
# read in from a file. Some parsed data is contained in array @ret.
#
# Routines starting with "get_" read from $buf and return some value. 
# Routines that start with "print_" may cause some sort of output.
#
# Arrays ending in _cvt in convert integers in $buf to (hopefully)
# more meaningful names.
#

initialize();
process_options();
process_command_args();
exit 0;

sub initialize {

  $VL_ver = 'VL1 Version 2 format';
  use File::Basename;
  $lib = dirname($0);
  push (@INC, $lib);
  require "VL.pl";
  require "instrument.pl";
  vl_init();
  inst_init();
  
  ($prog = $0) =~ s/.*\///;

  # $c_minus_4 = 16.351625; # C-4 in Hertz
  
  @QED_assign_com_cvt = (
    'Breath Attack Time'  ,  'Breath Attack Gain'  , 
    'Touch EG Time'       ,  'Touch EG Gain'       ,  'Portamento Time',
    'Element 1 Detune'    ,  'Element 2 Detune'    ,  
    'Element 1 Note Shift',  'Element 2 Note Shift' ,  
    'Element 1 Random Pitch',  'Element 2 Random Pitch',
    'Element 1 Portamento on',  'Element 2 Portamento on',
    'Element 1 Level'       ,  'Element 2 Level',  
    'Element 1 Pan Left'    ,  'Element 1 Pan Right',
    'Element 2 Pan Left'    ,  'Element 2 Pan Right',
    'Pitch Change Mode'     ,  'Pitch Change Pitch 1',
    'Pitch Change Fine 1'   ,  'Pitch Change Out 1' ,
    'Pitch Change Pitch 2'  ,  'Pitch Change Fine 2',
    'Pitch Change Out 2'    ,  'Pitch Change Wet/Dry',
    'No assign (27)'        ,  'No assign (28)'     ,
    'Feedback Delay Return' ,    'Feedback Delay Lch Delay Time',
    'Feedback Delay Lch Level',  'Feedback Delay Rch Delay Time',
    'Feedback Delay Rch Level',  'Feedback Delay Cch Delay Time',
    'Feedback Delay Cch Level',  'Feedback FB Delay Time'       ,
    'Feedback FB Gain'        ,  'FBD High'     ,
    'Reverb Return'     , 'Reverb Time'         ,  'Reverb High Control',
    'Reverb Diffusion'  , 'Reverb Init Delay'   ,  'Reverb Treble',
    'Reverb Bass'       , 'Reverb Feel'         ,  'Reverb Time Boost',
     );

  @QED_assign_element_cvt = (
   'Pressure Curve'         , 'Pressure Depth', 
   'Emboucher Upper Depth'  , 'Emboucher Lower Depth',
   'Pitch Upper Depth'      ,
   'Pitch Lower Depth'      , 'Vibrato Depth'     ,
   'Tonguing Depth'         , 'Tonguing Curve'    ,
   'Amplitude Curve'        , 'Amplitude Depth'   ,
   'Scream Value'           , 'Scream Curve'      ,    'Scream Depth',  
   'Breath Noise Value'     , 'Breath Noise Curve',    'Breath Noise Depth',
   'Growl Value'            , 'Growl Curve'       ,    'Growl Depth'   ,
   'Throat Value'           , 'Throat Curve'      ,    'Throat Depth',
   'Dynamic Filter Curve'   , 'Dynamic Filter Depth',   
   'Harmonic Enhancer Curve', 'Harmonic Enhancer Depth', 
   'Damping Curve'          , 'Damping Depth'     ,
   'Absorption Curve',      , 'Absorption Depth'  , 
   'Trigger Mode'           , 'Xfade Speed'       ,    'Interpolate Speed',
   'Breath Noise Level'     , 'Breath Noise HPF'  ,    'Breath Noise LPF',
   'Breath Noise Noise'     ,
   'Breath Noise KonRst'    , 'Breath Noise Slit Drive', 
   'Breath Noise Balance',
   'Throat Pitch'           , 'Throat Intensity'  ,    'Throat Amount',
   'Throat HPF'             , 'Throat LPF'        ,    'Driver Output',
   'Pipe/String Output'     , 'Tap Output'        ,    'Tap Sign',
   'Tap Location'           , 'Total Amplitude Level',
   'Excitation Level to Pipe/String',     'Excitation Level to Driver', 
   'Excitation LFO',                      'Excitation Pulse Width',
   'Excitation Vel Sens to Level',        'Excitation Vel Sens to LPF',
   'Excitation Vel to Pulse Width',
   'Harmonic Enhancer Carrier Signal',    'Harmonic Enhancer Carrier HPF', 
   'Harmonic Enhancer Carrier Overdrive', 'Harmonic Enhancer Carrier Level',
   'Harmonic Enhancer Mod. Signal'      , 'Harmonic Enhancer Mod. HPF',
   'Harmonic Enhancer Mod. Overdrive'   , 'Harmonic Enhancer Mod. Phase',
   'Harmonic Enhancer Mod. Index'       , 'Harmonic Enhancer Balance',
   'Dynamic Filter Mode'                , 'Dynamic Filter Input Gain',   
   'Dynamic Filter Cutoff Frequency'    , 'Dynamic Filter Resonance',
   'Dynamic Filter Balance'             , 'EQ Input Gain',
   'EQ HPF'                             , 'EQ LPF', 
   'EQ Band1 Frequency', 'EQ Band1 Q',  'EQ Band1 Level',   
   'EQ Band2 Frequency', 'EQ Band2 Q',  'EQ Band2 Level',
   'EQ Band3 Frequency', 'EQ Band3 Q',  'EQ Band3 Level',
   'EQ Band4 Frequency', 'EQ Band4 Q',  'EQ Band4 Level',
   'EQ Band5 Frequency', 'EQ Band5 Q',  'EQ Band5 Level',
   'EQ Post Boost',      'IE Density'    ,   'IE Dispersion', 
   'IE Roughness'  ,     'IE Wet Level',     'IE Level Balance', 
   'RSN Input Gain',     'RSN Delay1 Length','RSN DL2 Length',
   'RSN DL3 Length',     'RSN DL4 Length',   'RSN DL5 Length', 
   'RSN Decay Time',     'RSN LPF',          'RSN Conjunction',
   'RSN Diffusion',      'RSN Phase',        'RSN Wet Level',  
   'RSN Level Balance',  'IE&RSN Dry Level',
   'Env Pressure Attack Rate Offset',    'Env Pressure Release Rate Offset', 
   'Env Pressure Vel to Level', 'Env Pressure Vel to Rate',    
   'E&P EG Vel to Level'  ,    'E&P EG Vel to Rate' ,  'E&P EG Hold Time',
   'E&P EG Initial Level' ,    'E&P Attack Rate'   ,  'E&P Attack Level',
   'E&P EG Decay Rate' ,       'E&P EG Depth to Emb',
   'E&P EG Depth to Pitch' , 
   'VibEG Delay Time'   , 'VibEG Attack Rate',
   'VibEG Sustain Level', 'VibEG Depth'    ,  'Vib. Depth To Emb.',
   'Vibrato Depth To Pitch', 'Vibrato Offset',  'Vibrato Speed',
   'Vibrato Speed Shift'   , 'Vibrato Randomness', 
   'Growl Depth To Pressure',
   'Growl Depth To Breath Noise', 'Growl Offset', 'Growl Speed', 
   'Growl Randomness',   'Growl Speed Shift', 
   'A&F EG Vel Sens to Level',
   'A&F EG Vel Sens to Rate', 
   'A&F EG Attack1 Rate', 'A&F EG Attack1 Level', 'A&F EG Attack2 Rate', 
   'A&F EG Decay Rate'  , 'A&F EG Sustain Level', 'A&F EG Release Rate',
   'A&F EG Depth to Amp', 'A&F EG Depth to Filter' );   

  @time_cvt = ( 
    '5.00', '5.23', '5.45',  '5.71',  '5.94', '6.20',  '6.50',  '6.77', '7.07',
    '7.41', '7.74', '8.08',  '8.42',  '8.80', '9.17',  '9.59', '10.0' , '10.5',
   '10.9' ,'11.4' ,'11.9' , '12.4' , '13.0' ,'13.6',  '14.2' , '14.8' , '15.5',
   '16.2' ,'16.9' ,'17.6' , '18.4' , '19.2', '20.1',  '20.9' , '21.9' , '22.9',
   '23.9' ,'24.9' ,'26.0' , '27.2' , '28.4', '29.7',  '31.0' , '32.3' , '33.8',
   '35.3' ,'36.8' ,'38.5' , '40.2' , '41.9', '43.8',  '45.7' , '47.7' , '49.9',
   '52.1' ,'54.4' ,'56.8' , '59.3' , '62.0', '64.7',  '67.6',  '70.5' , '73.6',
   '76.9', '80.3' ,'83.9' , '87.6' , '91.5', '95.5',  '99.8',  '104',  '109',
   '114', '119',  '124',  '129',  '135', '141',  '147', '154',  '161',
   '168', '175',  '183',  '191',  '200', '208',  '218', '227',  '237',
   '248', '259',  '270',  '282',  '295', '308',  '321', '336',  '350',
   '366', '382',  '399',  '417',  '435', '454',  '475', '496',  '518',
   '540', '564',  '589',  '615',  '643', '671',  '701', '732',  '764',
   '798', '834',  '870',  '909',  '949', '991', '1.04','1.08', '1.13',
   '1.18','1.24',
		 );

    @reverb_time_cvt = ( 
    '0.05', '0.10', '0.15',  '0.20',  '0.25', '0.30',  '0.35', '0.40',  '0.45',
    '0.50', '0.55', '0.60',  '0.65',  '0.70', '0.75',  '0.80', '0.85',  '0.90',
    '0.95', '1.0' , '1.1' ,  '1.2' ,  '1.3' , '1.4' ,  '1.5' , '1.6' ,  '1.7' ,
    '1.8' , '1.9' , '2.0' ,  '2.1' ,  '2.2',  '2.3',   '2.4' , '2.5' ,  '2.6' ,
    '2.7' , '2.8' , '2.9' ,  '3.0' ,  '3.1',  '3.2',   '3.3' , '3.4' ,  '3.5' ,
    '3.6' , '3.7' , '3.8' ,  '3.9' ,  '4.0',  '4.1',   '4.2' , '4.3' ,  '4.4' ,
    '4.5' , '4.6' , '4.7' ,  '4.8' ,  '4.9',  '5.0',   '5.5',  '6.0' ,  '6.5' ,
    '7.0' , '7.5' , '8.0' ,  '8.5' ,  '9.0',  '9.5',  '10.0', '11.0' , '12.0' ,
   '13.0' ,'14.0' ,'15.0' , '16.0' , '17.0', '18.0',  '19.0', '20.0' , '25.0' ,
   '30.0' ,'35.0' ,'40.0' , '45.0' , '50.0', '55.0',  '60.0', '65.0' , '70.0' ,
   '75.0' ,'80.0' ,'85.0' , '90.0' , '95.0', '100.0',
			);

  @freq_time_cvt = ( 
    '0.057', '0.061', '0.063', '0.067', '0.071', '0.074', '0.078', '0.083',
    '0.087', '0.092', '0.096', '0.101', '0.107', '0.112', '0.118', '0.126',
    '0.132', '0.139', '0.146', '0.154', '0.162', '0.171', '0.179', '0.189', 
    '0.199', '0.210', '0.221', '0.232', '0.245', '0.258', '0.271', '0.286',
    '0.300', '0.317', '0.333', '0.352', '0.370', '0.389', '0.410', '0.432',
    '0.454', '0.479', '0.504', '0.530', '0.558', '0.588', '0.619', '0.652',
    '0.686', '0.723', '0.760', '0.801', '0.844', '0.887', '0.934', '0.984', 
    '1.04 ', '1.09 ', '1.15 ', '1.21 ', '1.27 ', '1.34 ', '1.41 ', '1.48 ',
    '1.56 ', '1.65 ', '1.73 ', '1.82 ', '1.92 ', '2.02 ', '2.13 ', '2.24 ',
    '2.36 ', '2.48 ', '2.62 ', '2.75 ', '2.90 ', '3.05 ', '3.21 ', '3.38 ',
    '3.56 ', '3.75 ', '3.95 ', '4.16 ', '4.38 ', '4.61 ', '4.85 ', '5.10 ', 
    '5.37 ', '5.66 ', '5.96 ', '6.27 ', '6.60 ', '6.95 ', '7.32 ', '7.72 ', 
    '8.11 ', '8.54 ', '8.99 ', '9.47 ', '9.97 ', '10.5 ', '11.0 ', '11.6 ',
    '12.2 ', '12.9 ', '13.6 ', '14.3 ', '15.0 ', '15.8 ', '16.7 ', '17.6 ',
    '18.5 ', '19.5 ', '20.5 ', '21.6 ', '22.7 ', '23.9 ', '25.2 ', '26.5 ',
    '27.9 ', '29.4 ', '30.9 ', '32.5 ', '34.3 ', '36.1 ', '38.0 ', '40.0 ',
		      );

  @delay_cvt = ( 
     ' 0.33', ' 0.35', ' 0.37', ' 0.39', ' 0.41', ' 0.43', ' 0.45', ' 0.47',
     ' 0.49', ' 0.52', ' 0.55', ' 0.57', ' 0.60', ' 0.63', ' 0.67', ' 0.70',
     ' 0.73', ' 0.77', ' 0.81', ' 0.85', ' 0.89', ' 0.94', ' 0.99', ' 1.04',
     ' 1.09', ' 1.15', ' 1.20', ' 1.26', ' 1.33', ' 1.40', ' 1.47', ' 1.54',
     ' 1.62', ' 1.70', ' 1.79', ' 1.88', ' 1.97', ' 2.07', ' 2.18', ' 2.29',
     ' 2.40', ' 2.52', ' 2.65', ' 2.78', ' 2.93', ' 3.07', ' 3.23', ' 3.39', 
     ' 3.56', ' 3.74', ' 3.93', ' 4.13', ' 4.34', ' 4.56', ' 4.79', ' 5.04', 
     ' 5.29', ' 5.56', ' 5.84', ' 6.13', ' 6.45', ' 6.77', ' 7.11', ' 7.47',
     ' 7.85', ' 8.25', ' 8.65', ' 9.06', ' 9.46', ' 9.86', '10.27', '10.67',
     '11.07', '11.48', '11.88', '12.29', '12.69', '13.09', '13.50', '13.90',
     '14.30', '14.71', '15.11', '15.51', '15.92', '16.32', '16.72', '17.13', 
     '17.53', '17.94', '18.34', '18.74', '19.15', '19.55', '19.95', '20.36',
     '20.76', '21.16', '21.57', '21.97', '22.38', '22.78', '23.18', '23.59', 
     '23.99', '24.39', '24.80', '25.20', '25.60', '26.01', '26.41', '26.81',  
     '27.22', '27.62', '28.03', '28.43', '28.83', '29.24', '29.64', '30.04', 
     '30.45', '30.85', '31.25', '31.66', '32.06', '32.46', '32.87',
		  );

  @stage_cvt = ( 4, 6, 8, 10, 12 );

    # Used for Xfade speed or Interpolation speed.
  @interp_xfade_cvt = ( 
     'fast', '0.09', '0.10', '0.11', '0.12', '0.13', '0.14', '0.15', 
     '0.17', '0.18', '0.20', '0.22', '0.24', '0.26', '0.28', '0.31',
     '0.33', '0.36', '0.40', '0.43', '0.47', '0.51', '0.56', '0.61', 
     '0.67', '0.73', '0.79', '0.87', '0.94', '1.03', '1.12', '1.22',
     '1.33', '1.46', '1.59', '1.73', '1.89', '2.06', '2.25', '2.45', 
     '2.67', '2.91', '3.18', '3.46', '3.78', '4.12', '4.49', '4.90',
     '5.34', '5.82', '6.35', '6.93', '7.56', '8.25', '8.98', '9.82',
     '10.7', '11.7', '12.7', '13.9', '15.2', '16.5', '18.0', '19.6', 
     '21.5', '23.3', '25.5', '27.9', '30.3', '33.3', '35.9', '39.6', 
     '43.3', '47.1', '55.7', '60.7', '66.6', '71.9', '80.3', '88.1', 
     '94.2', ' 105', ' 113', ' 124', ' 136', ' 143', ' 160', ' 182',
     '195 ', '210 ', '227 ', ' 248', ' 273', ' 303', ' 341', ' 390', 
		  );

  @noise_cvt = ( 'Normal'    , 'Harsh 1'   , 'Harsh 2'   , 'Lo Lips 1', 
		   'Lo Lips 2' , 'Mid Lips 1', 'Mid Lips 2', 'Hi Lips 1', 
		   'Hi Lips 2' , 'Jet'       , 'Lo Jet'    , 'Mid Jet'  , 
		   'Hi Jet   ' , 'Steam 1'   , 'Steam 2'   , 'Steam 3'  , 
		   'Steam 4'   , 'Plastic 1' , 'Plastic 2' , 'Dark Woody', 
		   'Metal 1'   , 'Metal 2'   , 'Rain'
		  );

  @tap_setting_cvt = ( 'Driving Point', 'Fixed', 'Key Track', 
			 'Fixed (straight)', 'Key Track (straight)' );

  @signal_select_cvt = ( 'Normal', 'Breath Noise Amp.', 
			   'Flow Rate Saturation', 'Beat', 'Slit/Friction',
		           'Reed' );

  @fmt_pitch_cvt = ('2.00', '1.98', '1.97', '1.95', '1.94', '1.92', 
		      '1.91', '1.89', '1.88', '1.86', '1.84', '1.83', 
		      '1.80', '1.78', '1.77', '1.75', '1.73', '1.72', 
		      '1.70', '1.69', '1.67', '1.66', '1.64', '1.63', 
		      '1.61', '1.59', '1.58', '1.56', '1.55', '1.53', 
		      '1.52', '1.50', '1.48', '1.47', '1.45', '1.44', 
		      '1.42', '1.41', '1.39', '1.38', '1.36', '1.34', 
		      '1.33', '1.31', '1.30', '1.28', '1.27', '1.25', 
		      '1.23', '1.22', '1.20', '1.19', '1.17', '1.16',
		      '1.14', '1.13', '1.11', '1.09', '1.08', '1.06', 
		      '1.05', '1.03', '1.02', '1.00', '0.98', '0.97',
		      '0.95', '0.94', '0.92', '0.91', '0.89', '0.88',
		      '0.86', '0.84', '0.83', '0.81', '0.80', '0.78',
		      '0.77', '0.75', '0.73', '0.72', '0.70', '0.69',
		      '0.67', '0.66', '0.64', '0.63', '0.61', '0.59', 
		      '0.58', '0.56', '0.55', '0.53', '0.52', '0.50',
		      '0.48', '0.47', '0.45', '0.44', '0.42', '0.41', 
		      '0.39', '0.38', '0.36', '0.34', '0.33', '0.31',
		      '0.30', '0.28', '0.27', '0.25', '0.23', '0.22',
		      '0.20', '0.19', '0.17', '0.16', '0.14', '0.13', 
		      '0.11', '0.09', '0.08', '0.06', '0.05', '0.03',
		      '0.02', '0.00');

  @overdrive_cvt = ('0.063', '0.065', '0.068', '0.071', '0.075', '0.078',
		      '0.081', '0.085', '0.088', '0.092', '0.097', '0.101',
		      '0.105', '0.110', '0.115', '0.120', '0.125', '0.131', 
		      '0.136', '0.143', '0.149', '0.155', '0.162', '0.169', 
		      '0.177', '0.185', '0.193', '0.202', '0.210', '0.220', 
		      '0.230', '0.239', '0.250', '0.261', '0.272', '0.285', 
		      '0.297', '0.311', '0.324', '0.338', '0.354', '0.369', 
		      '0.386', '0.403', '0.420', '0.439', '0.459', '0.479', 
		      '0.500', '0.522', '0.545', '0.569', '0.594', '0.621', 
		      '0.648', '0.677', '0.707', '0.738', '0.771', '0.805', 
		      '0.840', '0.878', '0.917', '0.957', '1.000', '1.044',
		      '1.090', '1.138', '1.188', '1.241', '1.296', '1.354',
		      '1.414', '1.476', '1.542', '1.610', '1.681', '1.755',
		      '1.833', '1.914', '1.999', '2.087', '2.180', '2.276',
		      '2.377', '2.482', '2.592', '2.707', '2.827', '2.952',
		      '3.083', '3.219', '3.361', '3.510', '3.666', '3.828',
		      '3.998', '4.174', '4.359', '4.552', '4.754', '4.964',
		      '5.184', '5.414', '5.653', '5.903', '6.165', '6.438',
		      '6.723', '7.021', '7.332', '7.656', '7.995', '8.349',
		      '8.718', '9.105', '9.507', '9.928', '10.37', '10.83',
		      '11.31', '11.81', '12.33', '12.88', '13.45', '14.04',
		      '14.66', '15.31' );

  @filter_mode_cvt = ('LPF', 'BPF', 'HPF', 'BEF');

  @cutoff_fixed_cvt=(' 26.9', ' 28.1', ' 29.3', ' 30.7', ' 31.9', ' 33.3',
		       ' 35.0', ' 36.4', ' 38.0', ' 39.8', ' 41.6', ' 43.5',
		       ' 45.3', ' 47.3', ' 49.3', ' 51.5', ' 54.0', ' 56.2',
		       ' 58.8', ' 61.5', ' 64.1', ' 66.9', ' 70.0', ' 73.0',
		       ' 76.3', ' 79.7', ' 83.4', ' 87.0', ' 90.9', ' 94.9',
		       ' 99.0', '103.0', '108.0', '112.0', '117.0', '123.0',
		       '128.0', '134.0', '140.0', '146.0', '153.0', '159.0',
		       '167.0', '174.0', '182.0', '190.0', '198.0', '207.0',
		       '216.0', '226.0', '236.0', '247.0', '258.0', '269.0',
		       '281.0', '294.0', '307.0', '321.0', '335.0', '350.0',
		       '366.0', '382.0', '399.0', '417.0', '436.0', '455.0',
		       '476.0', '497.0', '520.0', '543.0', '567.0', '593.0',
		       '620.0', '648.0', '677.0', '707.0', '739.0', '773.0',
		       '808.0', '845.0', '883.0', '923.0', '965.0', '1.01 k',
		       '1.06 k','1.10 k','1.14 k','1.21 k','1.26 k','1.32 k',
		       '1.38 k','1.45 k','1.51 k','1.58 k','1.66 k','1.74 k',
		       '1.82 k','1.90 k','1.99 k','2.09 k','2.19 k','2.29 k',
		       '2.40 k','2.52 k','2.64 k','2.77 k','2.90 k','3.05 k',
		       '3.20 k','3.36 k','3.53 k','3.71 k','3.91 k','4.11 k',
		       '4.33 k','4.57 k','4.82 k','5.10 k','5.39 k','5.71 k',
		       '6.07 k','6.46 k','6.89 k','7.39 k','7.97 k','8.67 k',
		       '9.62 k','11.9 k'
		       );

  @rez_cvt        = (' 1.00', ' 1.02', ' 1.04', ' 1.07', ' 1.09', ' 1.12',
		       ' 1.14', ' 1.16', ' 1.19', ' 1.22', ' 1.24', ' 1.27',
		       ' 1.30', ' 1.33', ' 1.36', ' 1.38', ' 1.42', ' 1.45',
		       ' 1.48', ' 1.51', ' 1.54', ' 1.58', ' 1.61', ' 1.65',
		       ' 1.68', ' 1.72', ' 1.76', ' 1.80', ' 1.84', ' 1.88',
		       ' 1.92', ' 1.96', ' 2.00', ' 2.05', ' 2.09', ' 2.14',
		       ' 2.18', ' 2.23', ' 2.28', ' 2.33', ' 2.38', ' 2.43',
		       ' 2.18', ' 2.23', ' 2.28', ' 2.33', ' 2.38', ' 2.43',
		       ' 2.49', ' 2.54', ' 2.60', ' 2.65', ' 2.71', ' 2.77',
		       ' 2.83', ' 2.89', ' 2.96', ' 3.02', ' 3.09', ' 3.15',
		       ' 3.22', ' 3.29', ' 3.37', ' 3.44', ' 3.52', ' 3.59',
		       ' 3.67', ' 3.75', ' 3.83', ' 3.92', ' 4.00', ' 4.09',
		       ' 4.18', ' 4.27', ' 4.36', ' 4.46', ' 4.56', ' 4.66',
		       ' 4.76', ' 4.86', ' 4.97', ' 5.08', ' 5.19', ' 5.30',
		       ' 5.42', ' 5.54', ' 5.66', ' 5.79', ' 5.91', ' 6.04',
		       ' 6.17', ' 6.31', ' 6.45', ' 6.59', ' 6.73', ' 6.88',
		       ' 7.03', ' 7.18', ' 7.34', ' 7.50', ' 7.67', ' 7.84',
		       ' 8.01', ' 8.18', ' 8.36', ' 8.54', ' 8.73', ' 8.92',
		       ' 9.12', ' 9.32', ' 9.52', ' 9.73', ' 9.94', '10.16',
		       '10.38', '10.61', '10.84', '11.08', '11.32', '11.57',
		       '11.83', '12.08', '12.35', '12.62', '12.90', '13.18',
		       '13.47', '13.76', '14.06', '14.37', '14.69', '15.00',
		       '15.33', '15.67'
		       );

  @Q_cvt           = ('0.50', '0.51', '0.53', '0.55', '0.56', '0.58',
			'0.60', '0.61', '0.63', '0.65', '0.67', '0.69',
			'0.71', '0.73', '0.75', '0.77', '0.80', '0.82',
			'0.84', '0.87', '0.89', '0.92', '0.95', '0.97',
			'1.00', '1.03', '1.06', '1.09', '1.13', '1.16',
			'1.19', '1.23', '1.27', '1.30', '1.34', '1.38',
			'1.42', '1.46', '1.51', '1.55', '1.60', '1.64',
			'1.69', '1.74', '1.79', '1.85', '1.90', '1.96',
			'2.02', '2.08', '2.14', '2.20', '2.26', '2.33',
			'2.40', '2.47', '2.54', '2.62', '2.69', '2.77',
			'2.86', '2.94', '3.03', '3.11', '3.21', '3.30',
			'3.40', '3.50', '3.60', '3.71', '3.82', '3.93',
			'4.05', '4.16', '4.29', '4.41', '4.54', '4.68',
			'4.82', '4.96', '5.11', '5.26', '5.41', '5.57',
			'5.73', '5.90', '6.08', '6.26', '6.44', '6.63',
			'6.83', '7.03', '7.24', '7.45', '7.67', '7.89',
			'8.12', '8.36', '8.61', '8.86', '9.13', '9.40',
			'9.67', '10.0', '10.3', '10.6', '10.9', '11.2',
			'11.5', '11.9', '12.2', '12.6', '12.9', '13.3',
			'13.7', '14.1', '14.5', '14.9', '15.4', '15.8',
			'16.3', '16.8', '17.3', '17.8', '18.3', '18.9',
			'19.4', '20.0'
			);

  @level_cvt       = (
		         0.0,  0.4,  0.8,  1.1,  1.5,
			 1.9,  2.3,  2.6,  3.0,  3.4,  3.8,
			 4.1,  4.5,  4.9,  5.3,  5.6,  6.0,
			 6.4,  6.8,  7.1,  7.5,  7.9,  8.3,
			 8.6,  9.0,  9.4,  9.8, 10.1, 10.5,
			10.9, 11.3, 11.6, 12.0, 12.4, 12.8,
			13.1, 13.5, 13.9, 14.3, 14.6, 15.0,
			15.4, 15.8, 16.1, 16.5, 16.9, 17.3,
			17.6, 18.0, 18.4, 18.8, 19.1, 19.5,
			19.9, 20.3, 20.6, 21.0, 21.4, 21.8,
			22.1, 22.5, 22.9, 23.3, 23.6, 24.0
			);

  @equalizer_band  = (
	        '  40.0', '  41.5', '  43.3', '  45.5', '  47.3', '  49.4',
                '  51.9', '  54.0', '  56.5', '  58.9', '  61.6', '  64.4',
		'  67.1', '  70.1', '  73.2', '  76.6', '  80.0', '  83.6',
		'  87.3', '  91.2', '  95.2', '  99.5', ' 104.0', ' 108.0',
		' 113.0', ' 118.0', ' 123.0', ' 129.0', ' 134.0', ' 140.0',
		' 147.0', ' 153.0', ' 160.0', ' 167.0', ' 175.0', ' 183.0',
		' 191.0', ' 199.0', ' 208.0', ' 217.0', ' 227.0', ' 237.0',
		' 248.0', ' 259.0', ' 270.0', ' 282.0', ' 295.0', ' 308.0',
		' 321.0', ' 336.0', ' 351.0', ' 367.0', ' 383.0', ' 400.0',
		' 418.0', ' 436.0', ' 455.0', ' 476.0', ' 497.0', ' 519.0',
		' 542.0', ' 566.0', ' 592.0', ' 618.0', ' 646.0', ' 674.0',
		' 704.0', ' 736.0', ' 768.0', ' 802.0', ' 838.0', ' 875.0',
		' 914.0', ' 955.0', ' 997.0', '1.04 k', '1.09 k', '1.14 k',
		'1.19 k', '1.24 k', '1.30 k', '1.35 k', '1.41 k', '1.48 k',
		'1.54 k', '1.61 k', '1.68 k', '1.76 k', '1.83 k', '1.92 k',
		'2.00 k', '2.09 k', '2.18 k', '2.28 k', '2.38 k', '2.49 k',
		'2.60 k', '2.71 k', '2.83 k', '2.96 k', '3.09 k', '3.23 k',
		'3.37 k', '3.52 k', '3.68 k', '3.84 k', '4.01 k', '4.19 k',
		'4.38 k', '4.57 k', '4.77 k', '4.99 k', '5.21 k', '5.44 k',
		'5.68 k', '5.93 k', '6.20 k', '6.47 k', '6.76 k', '7.06 k',
		'7.37 k', '7.70 k', '8.04 k', '8.40 k', '8.78 k', '9.16 k', 
		'9.57 k', '10.0 k'
		);

  @density_cvt  = (
        '0.146', '0.229', '0.271', '0.354', '0.396', '0.479', '0.604', '0.646',
	'0.771', '0.854', '0.896', '0.979', '1.104', '1.229', '1.271', '1.396',
	'1.479', '1.521', '1.646', '1.729', '1.854', '2.021', '2.104', '2.146',
	'2.229', '2.271', '2.354', '2.646', '2.729', '2.854', '2.896', '3.104',
	'3.146', '3.271', '3.396', '3.479', '3.604', '3.729', '3.771', '3.979',
	'4.021', '4.104', '4.146', '4.396', '4.646', '4.729', '4.771', '4.854',
        '4.979', '5.021', '5.229', '5.354', '5.479', '5.604', '5.646', '5.771',
        '5.854', '5.896', '6.104', '6.396', '6.497', '6.521', '6.604', '6.896',
	'7.021', '7.229', '7.271', '7.354', '7.479', '7.646', '7.771', '7.896',
	'7.979', '8.104', '8.271', '8.354', '8.521', '8.729', '8.771', '8.979',
	'9.021', '9.146', '9.229', '9.354', '9.521', '9.604', '9.646', '9.729',
	'9.979', '10.15', '10.23', '10.40', '10.48', '10.60', '10.85', '10.90',
        '11.27', '11.40', '11.60', '11.73', '11.85', '11.90', '12.02', '12.23',
        '12.35', '12.48', '12.52', '12.65', '12.77', '12.85', '12.90', '13.15',
	'13.35', '13.40', '13.48', '13.60', '13.73', '14.02', '14.60', '15.15',
	'15.77', '16.40', '16.90', '17.48', '18.40', '19.15', '19.85', '20.77'
	     );

  @boost_cvt = (
		  '-12.0', '-10.5', '-9.0', '-7.5', '-6.0', '4.5', '-3.0',
		  '-1.5',    '0.0',  '1.5',  '3.0',  '4.5', '6.0',  '7.5',
		   '9.0',   '10.5', '12.0'
		  );

  @dispersion_cvt = (
	  '4.028 m', '4.272 m', '4.517 m', '4.761 m', '5.005 m', '5.371 m', 
	  '5.615 m', '5.981 m', '6.348 m', '6.592 m', '7.080 m', '7.446 m',
	  '7.813 m', '8.301 m', '8.789 m', '9.277 m', '9.766 m', '10.25 m',
	  '10.86 m', '11.47 m', '12.08 m', '12.82 m', '13.55 m', '14.28 m',
	  '15.01 m', '15.87 m', '16.72 m', '17.70 m', '18.68 m', '19.78 m',
	  '20.87 m', '21.97 m', '23.19 m', '24.54 m', '25.88 m', '27.34 m',
	  '28.93 m', '30.52 m', '32.23 m', '33.94 m', '35.89 m', '37.84 m',
	  '40.04 m', '42.24 m', '44.56 m', '47.12 m', '49.68 m', '52.49 m',
	  '55.42 m', '58.47 m', '61.77 m', '65.19 m', '68.65 m', '72.63 m',
	  '76.66 m', '80.93 m', '85.45 m', '90.21 m', '95.21 m', '100.6 m',
	  '106.1 m', '112.1 m', '118.3 m', '124.9 m', '131.8 m', '139.2 m',
	  '146.9 m', '155.0 m', '163.7 m', '172.9 m', '182.4 m', '192.6 m',
	  '203.2 m', '214.6 m', '226.6 m', '239.1 m', '252.4 m', '266.6 m',
	  '281.4 m', '297.0 m', '313.6 m', '331.1 m', '349.5 m', '368.9 m',
	  '389.4 m', '411.4 m', '434.0 m', '458.1 m', '483.6 m', '510.5 m',
	  '538.9 m', '569.0 m', '600.6 m', '634.0 m', '669.3 m', '706.5 m',
	  '745.8 m', '787.4 m', '831.2 m', '877.4 m', '926.3 m', '977.9 m',
	    '1.032 ',  '1.090 ',  '1.150 ',  '1.214 ',  '1.282 ',  '1.353 ',
	    '1.429 ',  '1.508 ',  '1.592 ',  '1.681 ',  '1.774 ',  '1.873 ',
	    '1.977 ',  '2.087 ',  '2.203 ',  '2.326 ',  '2.445 ',  '2.592 ',
	    '2.736 ',  '2.888 ',  '3.049 ',  '3.219 ',  '3.398 ',  '3.587 ',
	    '3.786 ',  '4.000 '
		       );

  @tap_cvt = (
       '20.8 u', '22.1 u', '23.4 u', '24.7 u', '26.0 u', 
       '27.3 u', '28.6 u', '29.9 u', '31.2 u', '32.6 u', 
       '33.9 u', '35.2 u', '36.5 u', '37.8 u', '39.1 u', 
       '40.4 u', '41.7 u', '43.0 u', '45.6 u', '46.9 u',
       '48.2 u', '50.8 u', '52.1 u', '54.7 u', '57.3 u',
       '59.9 u', '62.5 u', '66.4 u', '69.0 u', '72.9 u',
       '76.8 u', '80.7 u', '84.6 u', '88.5 u', '93.8 u', 
       '99.0 u', '105 u',  '110 u',  '117 u',  '123 u', 
       '131 u', '139 u', '148 u', '156 u', '166 u', 
       '177 u', '187 u', '199 u', '212 u', '226 u',
       '240 u', '256 u', '273 u', '290 u', '309 u', 
       '330 u', '352 u', '376 u', '401 u', '428 u',
       '457 u', '488 u', '520 u', '557 u', '595 u', 
       '635 u', '678 u', '725 u', '774 u', '828 u',
       '885 u', '946 u', '1.01 m', '1.08 m', '1.16 m',
       '1.24 m', '1.33 m', '1.42 m', '1.52 m', '1.62 m',
       '1.74 m', '1.86 m', '1.99 m', '2.13 m', '2.27 m', 
       '2.43 m', '2.61 m', '2.79 m', '2.98 m', '3.19 m', 
       '3.42 m', '3.66 m', '3.92 m', '4.19 m', '4.49 m', 
       '4.80 m', '5.14 m', '5.50 m', '5.89 m', '6.31 m', 
       '6.75 m', '7.23 m', '7.74 m', '8.28 m', '8.87 m',
       '9.49 m', '10.2 m', '10.9 m', '11.7 m', '12.5 m',
       '13.4 m', '14.3 m', '15.3 m', '16.4 m', '17.6 m',
       '18.8 m', '20.1 m', '21.5 m', '23.1 m', '24.7 m',
       '26.4 m', '28.3 m', '30.3 m', '32.5 m', '34.8 m', 
       '37.2 m', '39.8 m', '42.7 m'
		);
}

sub print_signature {
  my($vl1_header) = get_str(0x0000, 16);
  my($rc) = 0;
  if ( $vl1_header =~ /$LM  0117/ ) {
    $vl1_version = 'VL1 Version 1 format';
  } elsif ( $vl1_header =~ /$LM 20117/ ) {
    $vl1_version = 'VL1 Version 2 format';
  } else {
    print "****Warning***** Perhaps $vl1_file is not a VL1 file!\n";
    $rc = 1;
  }
  if ($vl1_version ne 'VL1 Version 2 format') {
    print "****Warning***** $vl1_file is not Version 2 format!\n";
    $rc = 2;
  }
  printf "Header    : ";
  if ( $opt_v || $rc ) {
    printf "'%s' ($vl1_version)\n", $vl1_header;
  } else {
    printf "$vl1_version\n";
  }
  return $rc;
}

sub print_initial_edit_page {
  local(*address, $voice_num) = @_;
  debugaddr($address);
  $address = get_voices(1, $address, $voice_name_len);
  if ($num_voices > 1) {
    printf "Voice Loc : %s", $voice_loc[$voice_num];
    print " ($voice_num)" if $enhanced && $num_voices > 16;
    print "\n";
  }
  printf "Voice Name: '%s'\n", $voice_name[0];
  printf "Key Mode  : '%s'\n", $key_mode = get_key_mode(*address);
  printf "Voice Mode: '%s'\n", $voice_mode = get_voice_mode(*address);
  debugaddr($address);
  printf "E1 Name   : '%s'\n", get_name($address+0x141, 10);
  printf "E2 Name   : '%s'\n", get_name($address+0x709, 10)
    if $voice_mode eq 'Dual'; 
  print  $parm_separator;
  get_part_setting(*address);
}

#
# Print everything that starts EDIT/COM/
#
sub print_common {
  local(*address) = @_;
  print_common_misc(*address);
  print_common_effect(*address);
  $address += 2;
}

##############################################################################
#   Common Miscellaneous 
#   Print everything that starts EDIT/COM/MISC
##############################################################################

sub print_common_misc {
  local(*address) = @_;
  print_misc_setting(*address);
  print_misc_controller(*address);
  # Portamento is out of menu order, but in MIDI data format order.
  get_misc_portamento(*address);
  print_misc_element_pitch(*address);  
  print_misc_microtuning(*address);  
  print_misc_element_level_pan(*address);  
  print_misc_portamento(*address);  
  print_misc_quick_edit_assign(*address);  
}

# See page p. 35 of v.1 FR Version 1 (page 26 Version 2)
sub print_misc_setting {
  local(*address) = @_;
  debugaddr($address);

  my($assign)     = get_unsigned_byte(*address);
  print "COM/MISC/SETTING\n";
  print 'Assign Mode    = ';
  if ($assign == 0) {
    print "Bottom Note\n";
  } elsif ($assign == 1) {
    print "Top Note\n";
  } elsif ($assign == 2) {
    if ($key_mode eq 'Poly') {
      print "Top Note*\n";
    } else {
      print "Last Note\n";
    }
  } else {
    printf $unknown_str, $assign;
  }
  
  if ($key_mode eq 'Poly' && $assign == 2) {
    print 'Poly Expand    = ';
    if ($Poly_Expand_Mode == 0) {
      print "off\n";
    } else {
      print "$Pitch_Bend_Mode\n";
    }
  } else {
    print "Poly Expand    = Key mode is not POLY.\n" if !$terse;
  }

  # From p. 35 The "Bottom" and "Top" modes are only effective when 
  # the poly or unison mode is selected. 
  my($mono_or_unison) = ($key_mode eq 'Mono' || $key_mode eq 'Unison');
  if (!$terse || !$mono_or_unison) {
    print 'Pitchbend Mode = ';
    if ($Pitch_Bend_Mode == 0) {
      print "Normal\n";
    } elsif ($Pitch_Bend_Mode == 1) {
      print "Bottom\n";
    } elsif ($Pitch_Bend_Mode == 2) {
      print "Top\n";
    } else {
      printf $unknown_str, $Pitch_Bend_Mode;
    }
  }
  warn("Normal pitch bend assumed because when key mode is $key_mode\n")
    if $Pitch_Bend_Mode != 0 && $mono_or_unison;
  
  print "Sustain        = $Sustain\n" if nontersed($Sustain);
  print $parm_separator;
}

sub print_misc_controller {
  local(*address) = @_;
  debugaddr($address);
  my($attack_time)  = get_attack_time(*address);
  my($attack_gain)  = get_unsigned_byte(*address);
  my($touch_time)   = get_attack_time(*address);
  my($touch_gain)   = get_unsigned_byte(*address);
  printf "COM/MISC/CONTROLLER\n";
  printf "Breath Attack Time = %s\n", $attack_time;
  print  enhanced("              Gain = %3d", $attack_gain, 127), "\n";
  printf "Touch EG Time      = %s\n", $touch_time;
  print  enhanced("              Gain = %3d", $touch_gain, 127), "\n";
  printf "Polyphony Ctrl     = %s\n", $Polyphony_Control 
    if nontersed($Polyphony_Control);
  print $parm_separator;
}

sub get_misc_portamento {
  local(*address) = @_;
  debugaddr($address);
  $portamento_time_MIDI_ctrl  = get_on_off(*address);
  $portamento_mode            = get_signed_byte(*address);
  $portamento_time            = get_unsigned_byte_range(*address, 127);
  $elt_1_portamento           = get_on_off(*address);
  $elt_2_portamento           = get_on_off(*address);
}

# Page 37 of FR
sub print_misc_element_pitch {
  local(*address) = @_;
  debugaddr($address);
  local($element1_detune)       = get_signed_16_value(*address);
  local($element2_detune)       = get_signed_16_value(*address);
  local($element1_note_shift)   = get_signed_64_value(*address);
  local($element2_note_shift)   = get_signed_64_value(*address);
  local($element1_random_pitch) = get_unsigned_byte_range(*address, 7);
  local($element2_random_pitch) = get_unsigned_byte_range(*address, 7);
  
  # Don't print anything if in terse mode and nothing significant
  # is in effect.
  return if ( $terse 
	      && $element1_detune == 0 
	      && $element1_note_shift == 0
	      && $element1_random_pitch == 0 
	      && ($voice_mode ne 'Dual' 
		  || ($element1_detune == 0 
		      && $element1_note_shift == 0
		      && $element1_random_pitch == 0 )) );
  
  print "COM/MISC/ELEMENT PITCH\n";
  print_element_pitch($element1_detune, $element1_note_shift, 
		      $element1_random_pitch, '1');
  print_element_pitch($element2_detune, $element2_note_shift, 
		      $element2_random_pitch, '2')
    if ($opt_v || $voice_mode eq 'Dual' 
	|| ($voice_mode eq 'Single' && $key_mode eq 'Unison'));
  print $parm_separator;
}

sub print_element_pitch {
  my($detune, $note_shift, $random_pitch, $elt) = @_;
  return if ($detune == 0 && $note_shift == 0 && $random_pitch == 0 
	     && $terse);
  printf "Element%s ", $elt;
  print enhanced_cents("Detune = %2d", $detune, 2.2) if nontersed($detune);
  if (nontersed($note_shift)) {
    print  enhanced_pitch("Note Shift = %d", $note_shift, 0);
    printf " [%s]", note_offset_to_note($note_shift+24) if $enhanced;
  }
  printf "\n";
  if ($detune && 
      (($voice_mode ne 'Dual')
       || ($voice_mode eq 'Single' && $key_mode ne 'Unison'))) {
    if ($detune > 0) {
      info_msg("Detuning a single-element voice here merely raises a tad\n");
      info_msg("the overall pitch with respect to the master pitch.\n");
    } else {
      info_msg("Detuning a single-element voice here merely lowers a tad\n");
      info_msg("the overall pitch with respect to the master pitch.\n");
    }
  }
  printf "   Random Pitch = %d\n", $random_pitch if nontersed($random_pitch);
}

# Page 38 of FR
sub print_misc_element_level_pan {
    debugaddr($address);
    $element_level[1]   = get_unsigned_byte(*address);
    $element_level[2]   = get_unsigned_byte(*address);
    my($element1_pan_l) = get_signed_64_value(*address);
    my($element1_pan_r) = get_signed_64_value(*address);
    my($element2_pan_l) = get_signed_64_value(*address);
    my($element2_pan_r) = get_signed_64_value(*address);

    print "COM/MISC/ELEMENT LEVEL & PAN\n";
    print_element_level_pan($element_level[1], $element1_pan_l, 
			$element1_pan_r, '1');
    print_element_level_pan($element_level[2], $element2_pan_l, 
			$element2_pan_r, '2')
	if ($opt_v || $voice_mode eq 'Dual');
    print $parm_separator;
}

sub print_element_level_pan {
    local($level, $pan_l, $pan_r, $elt) = @_;
    printf enhanced("Element%%s Level  = %3d", $level, 127), $elt;
    print "\n";
    if ($level == 0) {
      warn_msg("Level is set to zero. May as well set voice mode to single.\n");
      return if $terse;
    }
    print enhanced("Pan Left        = %3d", $pan_l, -64);
    print enhanced(", Pan Right = %3d", $pan_r, 63), "\n";
}

# Page 42 of FR
sub print_misc_microtuning {
    local($element1_microtuning) = get_signed_byte(*address);
    local($element2_microtuning) = get_signed_byte(*address);

    # Don't print anything if in terse mode and nothing significant
    # is in effect.
    return if ( $terse 
	       && $element1_microtuning == 0 
	       && ( $voice_mode ne 'Dual' || $element1_microtuning == 0 ) );

    print "COM/MISC/MICROTUNING\n";
    print_microtuning($element1_microtuning, '1');
    print_microtuning($element2_microtuning, '2')
	if ($opt_v || $voice_mode eq 'Dual');
    print $parm_separator;
}

sub print_microtuning {
    local($tuning, $elt) = @_;
    local($str);
    local($scale);
    return if !nontersed($tuning);
    if ($tuning == 0) {
	$str = 'off';
    } elsif ($tuning <= 2) {
	$str = "I-$tuning: Internal Microtuning $tuning";
    } elsif ($tuning <= 2+12) {
	$str = sprintf "P-%d: Pure Major %s",  
	    $scale=$tuning-2, $scale_str[$scale-1]; 
    } elsif ($tuning <= 2+24) {
	$str = sprintf "P-%d: Pure Minor %s",
	    $scale=$tuning-(2+12), $scale_str[$scale-1]; 
    } elsif ($tuning <= 2+36) {
	$str = sprintf "P-%d: Mean Tone %s",   
	    $scale=$tuning-(2+24), $scale_str[$scale-1];
    } elsif ($tuning <= 2+48) {
	$str = sprintf "P-%d: Pythagorean %s", 
            $scale=$tuning-(2+36), $scale_str[$scale-1];
    } elsif ($tuning == 2+49) {
	$str = "P-49: Werkmeister";
    } elsif ($tuning == 2+50) {
	$str = "P-50: Kirnberger";
    } elsif ($tuning == 2+51) {
	$str = "P-51: Valloti & Young";
    } elsif ($tuning == 2+52) {
	$str = "P-52: 1/4 Shifted Equal";
    } elsif ($tuning == 2+53) {
	$str = "P-53: 1/4 Tone";
    } elsif ($tuning == 2+54) {
	$str = "P-54: 1/8 Tone";
    } elsif ($tuning == 2+55) {
	$str = "P-55: Zalzal";
    } elsif ($tuning == 2+56) {
	$str = "P-56: Farabi X";
    } elsif ($tuning == 2+57) {
	$str = "P-57: Ishfahan";
    } elsif ($tuning == 2+58) {
	$str = "P-58: Al-Kindi";
    } elsif ($tuning == 2+59) {
	$str = "P-59: Zirafkend";
    } elsif ($tuning == 2+60) {
	$str = "P-60: Hhidjazi";
    } elsif ($tuning == 2+61) {
	$str = "P-61: Rahawi";
    } elsif ($tuning == 2+62) {
	$str = "P-62: Hhosaini";
    } elsif ($tuning == 2+63) {
	$str = "P-63: African";
    } elsif ($tuning == 2+64) {
	$str = "P-64: Bagpipes";  
    } elsif ($tuning == 2+65) {
	$str = "P-65: Yugo Bag";
    } elsif ($tuning == 2+66) {
	$str = "P-66: Bulgar Bag";
    } elsif ($tuning == 2+67) {
	$str = "P-67: Byzantine";
    } elsif ($tuning == 2+68) {
	$str = "P-68: Tibet";
    } elsif ($tuning == 2+69) {
	$str = "P-69: Ch Flute";
    } elsif ($tuning == 2+70) {
	$str = "P-70: Ch Sheng";
    } elsif ($tuning == 2+71) {
	$str = "P-71: Dowland";
    } elsif ($tuning == 2+72) {
	$str = "P-72: Golden";
    } elsif ($tuning == 2+73) {
	$str = "P-73: PelogSlend";
    } elsif ($tuning == 2+74) {
	$str = "P-74: Singapore";
    } elsif ($tuning == 2+75) {
	$str = "P-75: Didymus Di";
    } elsif ($tuning == 2+76) {
	$str = "P-76: Aristoxene";
    } elsif ($tuning == 2+77) {
	$str = "P-77: Eratosthen";
    } elsif ($tuning == 2+78) {
	$str = "P-78: Phrygian";
    } elsif ($tuning == 2+79) {
	$str = "P-79: Indian 1";
    } elsif ($tuning == 2+80) {
	$str = "P-80: Indian 2";
    } elsif ($tuning == 2+81) {
	$str = "P-81: Hirajoshi1";
    } elsif ($tuning == 2+82) {
	$str = "P-82: Hirajoshi2";
    } elsif ($tuning == 2+83) {
	$str = "P-83: Korea";
    } elsif ($tuning == 2+84) {
	$str = "P-84: Thai";
    } else {
	$str = "Unknown: $tuning";
    }
    printf "Element$elt = $str\n";
}

# Page 43 of FR
sub print_misc_quick_edit_assign {
    (*address) = @_;
    debugaddr($address);
    local($QED1_Class)  = get_unsigned_byte(*address);
    local($QED1_Assign) = get_short_word(*address);
    local($QED2_Class)  = get_unsigned_byte(*address);
    local($QED2_Assign) = get_short_word(*address);
    # Don't print anything if in terse mode and nothing significant
    # is in effect.
    return if ( $terse 
	       && $QED1_Class == 0 
	       && ( $voice_mode ne 'Dual' || $QED2_Class == 0 ) );

    print "COM/MISC/QUICK EDIT ASSIGN\n";
    print_QED_assign($QED1_Class, $QED1_Assign, '1');
    print_QED_assign($QED2_Class, $QED2_Assign, '2');
    print $parm_separator;

}

sub print_QED_assign {
  local($class, $assign, $elt) = @_;
  return if !nontersed($class);
  my($debugstr) = raw_val($assign);
  local($str);
  if ($class == 0) {
    $str='off';
  } elsif ($class == 1) {
    # Todo Need to check that what's returned is "on"
    # Quick Editing something that's off doesn't do anything
    # And reports "No assign"
    if ($assign >= 0 && $assign <= $#QED_assign_com_cvt) {
      $str = sprintf "Com  : $debugstr%s", $QED_assign_com_cvt[$assign];
    } else {
      $str = sprintf "Com  : **invalid (%d)", $assign;
    }
  } elsif ($class == 2) {
    $str = convert_QED_assign($assign, "E1  ", $debugstr);
  } elsif ($class == 3) {
    $str = convert_QED_assign($assign, "E2  ", $debugstr);
    if ($voice_mode ne 'Dual') {
      $str= "$str\n***Error: only one element for this voice";
    }
  } elsif ($class == 4) {
    $str = convert_QED_assign($assign, 'both', $debugstr);
    if ($voice_mode ne 'Dual') {
      $str= "$str\n***Error: only one element for this voice";
    }
  } else {
    $str="**Error** unknown ($class)";
  }
  
  printf "QED$elt(CC%d) = %s\n", 15+$elt, $str;
}


##############################################################################
#   Common Effect
#   Print everything that starts EDIT/COM/EFFECT
##############################################################################
sub print_common_effect {
  local(*address) = @_;
  print_effect_setting(*address);
  print_effect_modulation(*address);
  print_feedback_delay(*address);
  print_reverberation(*address);
}

# See page 45 of FR
sub print_effect_setting {
  (*address) = @_;
  debugaddr($address);
  my($destination_effect)          = get_unsigned_byte(*address);
  my($effect_ctrl)                 = get_controller (*address); 
  $address += 13; # The next piece to get of info is out of sequence
  $feedback_delay_reverb_mode      = get_signed_byte(*address);
  $address -= 14; # Compensate for the implicit update
  
  print "COM/EFFECT/SETTING\n";
  print "Feedback Delay/Reverb. Mode = ";
  
  if ($feedback_delay_reverb_mode == 0) {
    print "Serial\n";
  } elsif ($feedback_delay_reverb_mode == 1) {
    print "Parallel\n";
  } else {
    printf "Unknown (%d)\n", $feedback_delay_reverb_mode;
  }
  if (nontersed($destination_effect)) {
    printf "Destination Effect = ";
    if ($destination_effect == 0) {
      print "off\n";
    } elsif ($destination_effect == 1) {
      print "MOD\n";
    } elsif ($destination_effect == 2) {
      print "FBD\n";
    } elsif ($destination_effect == 3) {
      print "REV";
      # print Send Level or "isn't used"
      print "\n";
    } else {
      printf $unknown_str, $destination_effect;
    }
  } 
  printf "Controller = $effect_ctrl\n" if nontersed($effect_ctrl);
  print  $parm_separator;
}

# Page 46 
sub print_effect_modulation {
  local(*address) = @_;
  debugaddr($address);
  my($mod_effect_type)  = get_unsigned_byte(*address);
  my($e1_onoff)         = get_on_off(*address);
  my($e2_onoff)         = get_on_off(*address);

  my($str);
  
  if (nontersed($mod_effect_type)) {
    printf "COM/EFFECT/MODULATION EFFECT\n";
    print raw_val($mod_effect_type);
    print "Modulation Effect Type = ";
    if ($mod_effect_type == 0) {
      print "off\n";
      $address += 10;
    } elsif ($mod_effect_type == 1) {
      print "Flanger\n";
      print_effect_flanger(*address, $e1_onoff, $e2_onoff);
    } elsif ($mod_effect_type == 2) {
      print "Pitch Change\n";
      print_effect_pitch(*address, $e1_onoff, $e2_onoff);
    } elsif ($mod_effect_type == 3) {
      print "Distortion\n";
      print_effect_distortion(*address, $e1_onoff, $e2_onoff);
      #decode pitch_change
    } elsif ($mod_effect_type == 4) {
      print "Chorus**\n";
      print_effect_chorus(*address, $e1_onoff, $e2_onoff);
    } elsif ($mod_effect_type == 5) {
      print "Phaser\n";
      print_effect_phaser(*address, $e1_onoff, $e2_onoff);
    } elsif ($mod_effect_type == 6) {
      print "Symphonic**\n";
      $address += 10;
    } elsif ($mod_effect_type == 7) {
      print "Celeste**\n";
      $address += 10;
    } elsif ($mod_effect_type == 8) {
      print "Dist+Flanger**\n";
      $address += 10;
    } elsif ($mod_effect_type == 9) {
      print "Dist+Wah**\n";
      $address += 10;
    } else {
      printf "Unknown (%d)\n", $mod_effect_type;
      $address += 10;
    }
    print $parm_separator;
  } else {
    $address += 10;
  }
}

# Page 47-48
sub print_effect_flanger {
    local(*address, $e1_onoff, $e2_onoff) = @_;
    debugaddr($address);
    my($flanger_wave)     = get_unsigned_byte(*address);
    my($flanger_freq)     = get_unsigned_byte(*address);
    my($flanger_depth)    = get_unsigned_byte(*address);
    my($flanger_delay)    = get_unsigned_byte(*address);
    my($flanger_phase)    = get_signed_16_value(*address);
    my($flanger_FBGain)   = get_signed_127_value(*address);
    my($flanger_high)     = get_signed_byte(*address);
    my($flanger_feel)     = get_unsigned_byte_range(*address, 10);
    my($flanger_wetdry)   = get_signed_byte(*address);

    my($all_off) = ($e1_onoff eq "off") && 
	           (($voice_mode ne 'Dual') || ($e2_onoff eq "off"));
    if ($all_off) {
      info_msg("All elements are off. You may as well turn off Modulation Effect.\n");
      return if $terse;
    }

    if ($flanger_wetdry) {
      info_msg("Wet/Dry balance zero. You may as well turn off Modulation Effect.\n");
      return if $terse;
    }
    printf "Element on/off          E1: %s", $e1_onoff;
    if ($voice_mode eq 'Dual') {
	printf "    E2: %s\n", $e2_onoff;
    } else {
	print "\n";
    }
    print "Wave  = ";
    if ($flanger_wave == 0) {
	print "Triangle        ";
    } elsif ($flanger_wave == 1) {
	print "Sine            ";
    } elsif ($flanger_wave == 2) {
	print "Random          ";
    } else {
	printf "unknown %3d    ", $flanger_wave;
    }
    print_freq($flanger_freq); print "\n";
    print_depth_delay($flanger_depth, $flanger_delay);
    printf "Phase = %4.1f degrees\n", phase_to_degrees($flanger_phase, 8);
    printf "FB Gain         = %3d%%  High = %2.1f\n", 
        $flanger_FBGain, high_cvt($flanger_high);
    printf "Analog Feel     = %3d\n",   $flanger_feel;
    printf "Wet/Dry Balance = %3d%%\n", $flanger_wetdry;
}

# Page 49-50
sub print_effect_pitch {
    local(*address, $e1_onoff, $e2_onoff) = @_;
    debugaddr($address);
    my($pitchchg_mode)    = get_unsigned_byte(*address);
    my($pitchchg_pitch1)  = get_signed_16_value(*address);
    my($pitchchg_fine1)   = get_signed_127_value(*address);
    my($pitchchg_out1)    = get_unsigned_byte(*address);
    my($pitchchg_pitch2)  = get_signed_16_value(*address);
    my($pitchchg_fine2)   = get_signed_127_value(*address);
    my($pitchchg_out2)    = get_unsigned_byte(*address);
    my($pitchchg_wetdry)  = get_unsigned_byte(*address);

    my($all_off) = ($e1_onoff eq "off") && 
	           (($voice_mode ne 'Dual') || ($e2_onoff eq "off"));
    if ($all_off) {
      info_msg("All elements are off. You may as well turn off the Modulation Effect.");
      return if $terse;
    }
    if (!pitchchg_wetdry) {
      info_msg("Wet/Dry balance zero. You may as well turn off Modulation Effect.\n");
      return if $terse;
    }

    printf "Element on/off          E1: %s", $e1_onoff;
    if ($voice_mode eq 'Dual') {
	printf "    E2: %s\n", $e2_onoff;
    } else {
	print "\n";
    }
    print_effect_mode($pitchchg_mode); print "\n";
    print_pitch_change('Left ', $pitchchg_pitch1, $pitchchg_fine1, 
		       $pitchchg_out1);
    printf "Wet/Dry balance = %3d%%\n", $pitchchg_wetdry;
    print_pitch_change('Right', $pitchchg_pitch2, $pitchchg_fine2, 
		       $pitchchg_out2);
    debugaddr($address);
}

# Version 2 Page 30
sub print_effect_chorus {
    local(*address) = @_;
    debugaddr($address);
    my($chorus_mode)     = get_unsigned_byte(*address);
    my($chorus_freq)     = get_unsigned_byte(*address);
    my($chorus_depth)    = get_unsigned_byte(*address);
    my($chorus_delay)    = get_unsigned_byte(*address);
    my($chorus_FBGain)   = get_signed_127_value(*address);
    my($chorus_high)     = get_unsigned_byte(*address);
    my($chorus_wetdry)   = get_unsigned_byte(*address);
    $address += 2; # reserve
    print_effect_mode($chorus_mode); print "\n";
    print_freq($chorus_freq); print "\n";
    print_depth_delay($chorus_depth, $chorus_delay);
}

# Version 2 Page 30
sub print_effect_phaser {
    local(*address, $e1_onoff, $e2_onoff) = @_;
    debugaddr($address);
    my($phaser_mode)     = get_unsigned_byte(*address);
    my($phaser_stage)    = get_unsigned_byte(*address);
    my($phaser_freq)     = get_unsigned_byte(*address);
    my($phaser_depth)    = get_unsigned_byte(*address);
    my($phaser_offset)   = get_unsigned_byte(*address);
    my($phaser_phase)    = get_signed_16_value(*address);
    my($phaser_FBGain)   = get_signed_127_value(*address);
    my($phaser_wetdry)   = get_unsigned_byte(*address);
    $address += 1; # reserve

    my($all_off) = ($e1_onoff eq "off") && 
	           (($voice_mode ne 'Dual') || ($e2_onoff eq "off"));
    if ($all_off) {
      warn_msg("All elements are off. You may as well turn off Modulation Effect\n.");
      return if $terse;
    }
    if (! $phaser_wetdry) {
      warn_msg("Wet/Dry balance zero. You may as well turn off Modulation Effect.\n");
      return if $terse;
    }
    
    printf "Element on/off          E1: %s", $e1_onoff;
    if ($voice_mode eq 'Dual') {
	printf "    E2: %s\n", $e2_onoff;
    } else {
	print "\n";
    }
    print_effect_mode($phaser_mode);
    print "      Stage = $stage_cvt[$phaser_stage]\n";
    print_freq($phaser_freq);
    printf "     Depth = %3d%%\n",  $phaser_depth;
    printf "Offset = %3d%%\n", $phaser_offset;
    printf "Phase           = %4.1f degrees\n", 
       phase_to_degrees($phaser_phase, 8);
    printf "FB Gain         = %3d%%\n", $phaser_FBGain;
    printf "Wet/Dry Balance = %3d%%\n", $phaser_wetdry;

}

sub print_effect_mode {
    my($mode) = @_;
    print "Mode            = ";
    if ($mode == 0) {
	print "Monaural";
    } elsif ($mode == 1) {
	print "Stereo  ";
    } else {
	printf "unknown %d", $mode;
    }
}

##############################################################################
#   Element Controller
#   Print everything that starts EDIT/E[12]/CTRL/
#   Section starting on page 64 of FR
##############################################################################

sub print_elt_controller {
    local($element, *where)=@_;
    $pressure_ctrl =
    print_dynamic_range      ("$element/CTRL/PRESSURE\n",          *where);
    print_embouchure         ("$element/CTRL/EMBOUCHURE\n",        *where);
    print_pitch              ("$element/CTRL/PITCH\n",             *where);
    print_vibrato            ("$element/CTRL/VIBRATO\n",           *where);
    print_dynamic_range      ("$element/CTRL/TONGUING\n",          *where);
    print_dynamic_range      ("$element/CTRL/AMPLITUDE\n",         *where);
    print_dynamic_range_value("$element/CTRL/SCREAM\n",            *where, *scream_ctrl);
    print_dynamic_range_value("$element/CTRL/BREATH NOISE\n",      *where, *breath_ctrl);
    $breath_depth = $ret[1];
    print_dynamic_range_value("$element/CTRL/GROWL\n",             *where, *growl_ctrl);
    print_dynamic_range_value("$element/CTRL/THROAT FORMANT\n",    *where, *throat_ctrl);
    print_dynamic_range      ("$element/CTRL/DYNAMIC FILTER\n",    *where);
    print_dynamic_range      ("$element/CTRL/HARMONIC ENHANCER\n", *where);
    print_dynamic_range      ("$element/CTRL/DAMPING\n",           *where);
    print_dynamic_range      ("$element/CTRL/ABSORPTION\n",        *where);
}

##############################################################################
#   Element Miscellaneous
#   Print everything that starts EDIT/E[12]/MISC/
#   Section starting on page 84 of Version 1 FR
##############################################################################

sub print_elt_miscellaneous {
  local($element, *where)=@_;
  print_elt_misc_setting("$element/MISC/SETTING", *where);
  print_elt_misc_breath("$element/MISC/BREATH NOISE", *where);
  print_elt_misc_throat("$element/MISC/THROAT FORMANT", *where);
  print_elt_misc_mixing("$element/MISC/MIXING", *where);
  print_elt_misc_amplitude("$element/MISC/AMPLITUDE", *where);
  print_elt_instrument("$element/INSTRUMENT", *where);
  print_elt_misc_excitation("$element/MISC/EXCITATION", *where);
}

# Section starting on page 85 of Version 1 FR
sub print_elt_misc_setting {
  local($message, *where)=@_;
  debugaddr($where);
  my($trigger_mode)  = get_voice_mode(*where);
  my($xfade_speed)   = get_unsigned_byte(*where);
  my($interp_speed)  = get_unsigned_byte(*where);
  
  $trigger_mode = 'Multi' if $trigger_mode eq 'Dual';
  
  print "$message\n";
  print "Trigger Mode      = $trigger_mode\n";
  print "Xfade Speed       = ", check_cvt(*interp_xfade_cvt, 
					  $xfade_speed, "%s msec\n", 
					  "$illegal_set_str\n");
  print "Interpolate Speed = ", check_cvt(*interp_xfade_cvt,
					  $interp_speed, "%s msec\n",
					  "$illegal_set_str\n");
  print $parm_separator;
}

# Section starting on page 86 of FR Version 1 
sub print_elt_misc_breath {
  local($message, *where)=@_;
  debugaddr($where);
  my($b_noise_level)   = get_unsigned_byte_range(*where, 127);
  my(@level_brks)      = get_brkpts(*where, *get_signed_64_value, 6);
  local(@b_noise_hpf)  = get_cutoff(*where, *lpf_cutoff_cvt); # yes LPF!
  local(@hpf_brks)     = get_brkpts(*where, *get_signed_64_value, 2);
  local(@b_noise_lpf)  = get_cutoff(*where, *lpf_cutoff_cvt);
  local(@lpf_brks)     = get_brkpts(*where, *get_signed_64_value, 2);
  my($b_noise_noise)   = get_noise(*where);
  my($key_reset)       = get_on_off(*where);
  my($slit_drive)      = get_unsigned_byte_range(*where, 32);
  my($ctrl_balance)    = get_signed_64_value(*where);
  
  print("$message\n");
  
  printf "%-17s = %3d  Noise = %s\n", 'Slit Drive',   $slit_drive, 
         $b_noise_noise;
  if ($b_noise_level == 0) {
    warn_msg("Breath noise level 0. No breath noise.\n");  
    if ($terse) {
      print $parm_separator;
      return;
    }
  } 

  printf "%-17s = %3d", 'Ctrl Balance', $ctrl_balance;
  print enhanced("", $ctrl_balance+64, 127), "\n";
  if (63 == $ctrl_balance) {
    info_msg("Breath control coming from preset value, not the controller.\n");
    if ($breath_ctrl ne 'off') {
      info_msg("You may as well turn off \"$breath_ctrl\" here.\n");
    }
  } elsif (-64 == $ctrl_balance) {
    info_msg("Breath control coming only from the controller.\n");
    if ($breath_depth == 0 && stop_if_no_ctrl($breath_ctrl, "", 1)) {
      return;
    }
  }
  print_filters(*b_noise_hpf, *b_noise_lpf, *hpf_brks, *lpf_brks);
  printf "%-15s = %s\n", 'Key On Reset', $key_reset;
  print_brks("Level", $b_noise_level, 127, @level_brks);
  
  print $parm_separator;
}

# Section starting on page 91 of FR Version 1, and page 44 of Version 2.
sub print_elt_misc_throat {
  local($message, *where)=@_;
  my($t_formant_track) = get_tracking(*where);
  my($t_formant_pitch) = get_signed_127_value(*where);
  my(@pitch_brks)      = get_brkpts(*where, *get_signed_127_value, 8);
  my($t_formant_intens)= get_signed_127_value(*where);
  my(@intens_brks)     = get_brkpts(*where, *get_signed_127_value, 4);
  my($t_formant_amount)= get_signed_64_value(*where);
  my(@amount_brks)     = get_brkpts(*where, *get_signed_64_value, 4);
  local(@t_formant_hpf)= get_cutoff(*where, *lpf_cutoff_cvt); # yes LPF!
  local(@hpf_brks)     = get_brkpts(*where, *get_unsigned_byte, 3);
  local(@t_formant_lpf)= get_cutoff(*where, *lpf_cutoff_cvt);
  local(@lpf_brks)     = get_brkpts(*where, *get_unsigned_byte, 3);
  
  print  "$message\n";
  print  "Pitch Tracking   = $t_formant_track\n";
  print  "Pitch            = ";
  if ($t_formant_track =~ /Fixed/i) {
    print  "**$t_formant_pitch\n";
  } elsif ($t_formant_track =~ /KeyTrack/) {
    print  val_to_oct($t_formant_pitch), "\n";
  } else {
    print  "**$t_formant_pitch (unknown pitch type)\n";
  }
  print_brks("", $t_formant_pitch, 127, @pitch_brks);
  print_brks("Amount           ", $t_formant_amount, 127, @amount_brks);
  print_brks("Intensity        ", $t_formant_intens, 127, @intens_brks);
  print_filters(*t_formant_hpf, *t_formant_lpf, *hpf_brks, *lpf_brks);
  
  print $parm_separator;
}

# Section starting on page 98 of FR Version 1, and page 44 of V2.
sub print_elt_misc_mixing {
  local($message, *where)=@_;
  debugaddr($where);
  my($driver_output)      = get_unsigned_byte_range(*where, 127);
  my(@driver_brks)        = get_brkpts(*where, *get_signed_64_value, 6);
  my($pipe_string_output) = get_unsigned_byte_range(*where, 127);
  my(@pipe_brks)          = get_brkpts(*where, *get_signed_64_value, 6);
  my($tap_output)         = get_unsigned_byte_range(*where, 127);
  my(@output_brks)        = get_brkpts(*where, *get_signed_64_value, 6);
  my($tap_sign)           = get_unsigned_byte(*where);
  my($tap_setting)        = get_tap_setting(*where);
  my($tap_location)       = get_unsigned_byte(*where);
  my(@loc_brks)           = get_brkpts(*where, *get_signed_64_value, 8);
  
  print  "$message\n";

  print_brks("Driver Output       ", $driver_output, 127, @driver_brks);
  
  printf  "Tap Sign = %s\n", $tap_sign ? '+' : '-';
  print_brks("Pipe/String Output  ", $pipe_string_output, 127, @pipe_brks);
  print_brks("Tap Output          ", $tap_output, 127, @output_brks);
  
  printf  "Tap Setting         = %s\n", $tap_setting;
  if ($tap_setting =~ /^Key Track/) {
    printf  "Tap Location        = %3.1f%%\n", 
    val_to_pct($tap_location, 127);
    print_brks("", $tap_location, 127, @loc_brks);
  } elsif ($tap_setting =~ /^Fixed/) {
    print_breaks("Tap Location        ", $tap_location, 127, *format_tap,
		 @loc_brks);
  }
  print $parm_separator;
  
}

# Section starting on page 44-45 of FR Version 2
sub print_elt_misc_excitation {
  local($message, *where)=@_;
  debugaddr($where);
  my($lvl_to_pipe_string) = get_signed_127_value(*where);
  my(@p_s_brks)           = get_brkpts(*where, *get_signed_127_value, 3);
  $where += 7; # reserve
  my($lvl_to_driver)      = get_signed_127_value(*where);
  my(@driver_brks)        = get_brkpts(*where, *get_signed_127_value, 3);
  local(@lpf_cutoff)      = get_cutoff(*where, *lpf_cutoff_cvt);
  my(@lpf_brks)           = get_brkpts(*where, *get_signed_64_value, 3);
  my($velo_sens_to_level) = get_unsigned_byte(*where);
  my($velo_sens_to_lpf)   = get_unsigned_byte(*where);
  my($velo_sens_to_pwidth)= get_signed_16_value(*where);
  my($pulse_width)        = get_unsigned_byte(*where);
  my(@excite_brks)        = get_brkpts(*where, *get_signed_64_value, 3);
  $where++;  # reserve
  
  print  "$message\n";
  printf  "Velocity Sensitivity to Level = %d\n", $velo_sens_to_level;
  print_brks("Level to Pipe/String ", $lvl_to_pipe_string, 127, @p_s_brks);
  printf  "Velocity Sensitivity to LPF = %d\n", $velo_sens_to_lpf;
  print_brks("Level to Driver      ", $level_to_driver, 127, @driver_brks);
  print_cutoff('L', $lpf_cutoff[0]);
  
  printf  "Pulse Width        = %4.1f%%\n", 
  val_to_pct($pulse_width, 128);
  print_brks("", $pulse_width, 128, @excite_brks);
  
  print $parm_separator;
}

##############################################################################
#   Element Modifier
#   Print everything that starts EDIT/E[12]/MODIFIER/
#   Section starting on page 106 of Version 1 FR
##############################################################################

sub print_elt_modifier {
  local($element, *where)=@_;
  print_elt_mod_harmonic_enhancer("$element/MODIFIER/HARMONIC ENHANCER", 
				  *where);
  print_elt_mod_dynamic_filter("$element/MODIFIER/DYNAMIC FILTER", 
			       *where);
  print_elt_mod_equalizer_aux("$element/MODIFIER/EQUALIZER AUXILIARY", 
			      *where);
  print_elt_mod_equalizer_band("$element/MODIFIER/EQUALIZER BAND", 
			       *where);
  print_elt_mod_impulse_expander("$element/MODIFIER/IMPULSE EXPANDER",  
				 *where);
  print_elt_mod_IE_RS("$element/MODIFIER/IE & RSN SETTING",  *where);
}

# Section starting on page 107 of FR Version 1
sub print_elt_mod_harmonic_enhancer {
  local($message, *where)=@_;
  debugaddr($where);
  
  my($HE_c_signal)      = get_unsigned_byte(*where);
  local(@HE_c_HPF)      = get_cutoff(*where, *hpf_cutoff_cvt);
  local(@HE_c_HPF_brks) = get_brkpts(*where, *get_signed_64_value, 4);
  my($HE_c_overdrive)   = get_signed_64_value(*where);
  local(@HE_c_od_brks)  = get_brkpts(*where, *get_signed_64_value, 4);
  my($HE_c_level)       = get_unsigned_byte_range(*where, 127);
  local(@HE_c_cl_brks)  = get_brkpts(*where, *get_signed_64_value, 6);
  
  my($HE_m_signal)      = get_unsigned_byte_range(*where, 127);
  local(@HE_m_HPF)      = get_cutoff(*where, *hpf_cutoff_cvt);
  local(@HE_m_HPF_brks) = get_brkpts(*where, *get_unsigned_byte, 2);
  my($HE_m_overdrive)   = get_signed_64_value(*where);
  local(@HE_m_od_brks)  = get_brkpts(*where, *get_signed_64_value, 2);
  my($HE_m_phase)       = get_unsigned_byte(*where);
  my($HE_m_index)       = get_unsigned_byte_range(*where, 127);
  local(@HE_m_ind_brks) = get_brkpts(*where, *get_signed_64_value, 4);
  my($HE_balance)       = get_signed_64_value(*where);
  local(@HE_bal_brks)   = get_brkpts(*where, *get_signed_64_value, 4);
  
  print  "$message\n";
  
  print_he_common('Carrier', $HE_c_signal, *HE_c_HPF, *HE_c_HPF_brks,
		  $HE_c_overdrive, *HE_c_od_brks, 
		  $HE_c_level,     *HE_c_cl_brks, 0, 0);
  
  print_he_common('Modulator', $HE_m_signal, *HE_m_HPF, *HE_m_HPF_brks,
		  $HE_m_overdrive, *HE_m_od_brks, 
		  $HE_m_index,     *HE_m_ind_brks,
		  $HE_balance,   *HE_bal_brks, $HE_m_phase);
  
  print $parm_separator;
}


# Section starting on page 115 of FR Version 1
sub print_elt_mod_dynamic_filter {
    local($message, *where)=@_;
    debugaddr($where);

    my($DF_Filter_Mode)   = get_signed_byte(*where);
    my($DF_Input_Gain)    = get_unsigned_byte_range(*where, 127);
    my($DF_Cutoff_Track)  = get_tracking(*where);
    my($DF_Cutoff_Freq)   = get_unsigned_byte(*where);
    my(@DF_Cutoff_brks)   = get_brkpts(*where, *get_signed_64_value, 3);
    my($DF_Resonance)     = get_unsigned_byte(*where);
    my(@DF_Resonance_brks) 
	                  = get_brkpts(*where, *get_signed_64_value, 3);
    my($DF_Balance)       = get_signed_64_value(*where);

    print  "$message\n";
    print  "Filter Mode     = ";
    print check_cvt(*filter_mode_cvt, $DF_Filter_Mode, "%s", $illegal_set_str);
    print enhanced("   Input Gain = %3d", $DF_Input_Gain, 127), "\n";
    printf "Balance         = %3d\n", $DF_Balance;
    print  "Cutoff Tracking = $DF_Cutoff_Track\n" ;
    print  "Cutoff Freq.    = ";
    if ($DF_Cutoff_Track =~ /Fixed/i) {
	my($msg) = check_cvt(*cutoff_fixed_cvt, $DF_Cutoff_Freq, "%s",
			    "$illegal_set_str\n");
	# Deal with output like these:
        #   **Illegal setting
	#   24.0 k
        #   (100) 5.84 k 
	#   735.2
	#   (11) 7.446
	if ($msg =~ /^\*\*Illegal setting/) {
	    $hz = -1;
	} elsif ($msg =~ /(\(\d+\) )?(\d+\.\d+) k/) {
	    $hz = $2 * 1000;
	} elsif ($msg =~ /(\(\d+\) )?(\d+\.\d+)/) {
	    $hz = $2;
	} else {
	    $hz = $msg;
	}
	if ($hz == -1) {
	    print $msg;
	} elsif ($enhanced) {
	    my($pitch) = note_offset_to_note(hz_to_pitch($hz));
	    print "${msg}Hz [$pitch]\n";
	} else {
	    print "${msg}Hz\n";
	}
	print_breakpoints("", @DF_Cutoff_brks);
    } else {
	print val_to_oct($DF_Cutoff_Freq), "\n";
	print_breaks("", $DF_Cutoff_Freq, 64, *val_to_oct, @DF_Cutoff_brks);
    }
    print_breaks("Resonance       ", $DF_Resonance, 127, 
		 *format_rez, @DF_Resonance_brks);

    print $parm_separator;
}

# Section starting on page 119 of FR Version 1
sub print_elt_mod_equalizer_aux {
    local($message, *where)=@_;
    debugaddr($where);

    my($EQ_Input_Gain)    = get_unsigned_byte(*where);
    local(@EQ_HPF)        = get_cutoff(*where, *hpf_cutoff_cvt);
    local(@EQ_HPF_brks)   = get_brkpts(*where, *get_signed_64_value, 4);
    local(@EQ_LPF)        = get_cutoff(*where, *lpf_cutoff_cvt);
    local(@EQ_LPF_brks)   = get_brkpts(*where, *get_signed_64_value, 4);
    my($EQ_Post_EQ_boost) = get_signed_16_value(*where);

    print  "$message\n";
    print   enhanced("Input Gain = %3d", $EQ_Input_Gain, 127), "\n";
    printf "Post EQ Boost = %s dB\n", 
       check_cvt(*boost_cvt, $EQ_Post_EQ_boost+8, "%s", $illegal_set_str);

    print_cutoff("H", $EQ_HPF[0]);
    print_breaks("",  $EQ_HPF[2], 127, *format_hpf_cutoff_val, @EQ_HPF_brks);

    print_cutoff("L", $EQ_LPF[0]);
    print_breaks("",  $EQ_LPF[2], 127, *format_lpf_cutoff_val, @EQ_LPF_brks);
    print $parm_separator;
}

# Section on pages 123-124 of FR Version 1 and the
# Section on pages 126-127 "   "      (page 51 Version 2)
sub print_elt_mod_IE_RS {
    local($message, *where)=@_;
    my(@DL);
    my(@rsn_lpf);
    debugaddr($where);

    $ie_wet_level  = get_unsigned_byte_range(*where, 127);
    $ie_level_bal  = get_signed_64_value(*where);
    $rsn_on_off    = get_on_off(*where);
    $rsn_input_gain= get_unsigned_byte_range(*where, 127);
    for (1..5) {
	push(@DL, get_short_word(*where) & 1023);
    }
    $rsn_decay_time= get_unsigned_byte(*where);
    push(@rsn_lpf,  get_cutoff(*where, *lpf_cutoff_cvt));
    $rsn_conjunction = get_signed_64_value(*where);
    $rsn_diffusion   = get_unsigned_byte_range(*where, 16);
    $rsn_phase       = get_signed_16_value(*where);

    $rsn_wet_level   = get_unsigned_byte_range(*where, 127);
    $rsn_level_bal   = get_signed_64_value(*where);
    $ie_rsn_dry_level= get_unsigned_byte_range(*where, 127);

    return if ($ie_on_off eq 'off' && $rsn_on_off eq 'off' && $terse);
    print  "$message\n";
    printf "IE = %s, RSN = %s\n", $ie_on_off, $rsn_on_off;
    if ($ie_on_off eq 'on') {
	printf "IE Wet Level = %3d, IE Level bal = %d\n", 
	   $ie_wet_level, $ie_level_bal;
    }
    
    if ($ie_on_off eq 'on' || $rsn_on_off eq 'on') {
	printf "IE & RSN Dry Level = %3d\n", $ie_rsn_dry_level;
    }

    if ($rsn_on_off eq 'on') {
	printf "RSN Wet Level = %3d, RSN Level bal = %d\n", 
	       $rsn_wet_level, $rsn_level_bal;
	printf "RSN Inp Gain = %d\n", $rsn_input_gain;
	for $i (0..4) {
	    printf "Delay line %d  = %6.3f msec  %d **\n", 
	          $i+1, ($DL[$i]+1) * 0.042, $DL[$i];
	}
	printf "Decay Time  = %ssec\n", 
	       check_cvt(*dispersion_cvt, $rsn_decay_time, "%s", 
			 "$illegal_set_str"); 
	print_cutoff('L', $rsn_lpf[0]);
	printf "Diffusion   = %d\n", $rsn_diffusion;
	printf "Phase       = %d\n", $rsn_phase;
	printf "Conjunction = %d\n", $rsn_conjunction;
    }

    print $parm_separator;
}

# Section starting on page 125 of FR Version 1
sub print_elt_mod_impulse_expander {
    local($message, *where)=@_;
    debugaddr($where);

    $ie_on_off     = get_on_off(*where);
    $ie_density    = get_unsigned_byte(*where);
    $ie_dispersion = get_unsigned_byte(*where);
    $ie_roughness  = get_unsigned_byte_range(*where, 16);

    print  "$message\n";
    printf "Density    = %s msec\n", 
      check_cvt(*density_cvt, $ie_density, "%s", $illegal_set_str);
    printf "Dispersion = %ssec\n", 
           check_cvt(*dispersion_cvt, $ie_dispersion, "%s", 
		     $illegal_set_str);
    printf "Roughness  = %d\n", $ie_roughness;

    print $parm_separator;
}

##############################################################################
#   Element Envelope
#   Print everything that starts EDIT/E[12]/ENV/
#   Section starting on page 128 of Version 1 FR
##############################################################################
sub print_elt_envelope {
    local($element, *address) = @_;
    print_env_pressure("$element/ENV/PRESSURE", *address);
    print_env_emb_pitch("$element/ENV/EMBOUCHER & PITCH", *address);
    print_env_vibrato("$element/ENV/VIBRATO", *address);
    print_env_growl("$element/ENV/GROWL", *address);
    print_env_amp_filter("$element/ENV/AMPLITUDE & FILTER", *address);
    #...
    # The element tuning goes here.
    $where += 0x01a6;
}

#   Section starting on page 129 of Version 1 FR
sub print_env_pressure {
  local($message, *address) = @_;
  debugaddr($address);
  $attack_rate_offset  = get_signed_16_value(*address);
  $release_rate_offset = get_signed_16_value(*address);
  $vel_sens_to_level   = get_unsigned_byte_range(*address, 16);
  $vel_sens_to_rate    = get_signed_16_value(*address);
  $address += 0x36;      # Reserved
  $env_mode            = get_unsigned_byte(*address);
  
  return if !nontersed($env_mode);
  
  print "$message\n";

  return if $env_mode && stop_if_no_ctrl($pressure_ctrl, "EG mode set. ", 1);
  
  if ($env_mode != 0 && !$vel_sens_to_level && !$vel_sens_to_rate) {
    warn_msg("Both Velocity Sense values 0. " 
	     . "EG Mode may as well be disabled.\n");
      if ($terse) {
	print $parm_separator;
	return;
      }
  }
  
  print "EG Mode                = ";
  if ($env_mode == 0) {
    print "Disable\n";
  } elsif ($env_mode == 1) {
    print "ADSR\n";
  } elsif ($env_mode == 2) {
    print "AR\n";
  } else {
    printf "$unknown_str, $env_mode\n";
  }
  
  printf "Attack Rate Offset     = %3d\n", $attack_rate_offset
    if nontersed($attack_rate_offset);
  printf "Release Rate Offset    = %3d\n", $release_rate_offset
    if nontersed($release_rate_offset);
  print enhanced("Velocity Sens To Level = %3d", $vel_sens_to_level, 16),
        "\n";
  print enhanced("              To Rate  = %3d", $vel_sens_to_rate, 16),
        "\n";
  
  print $parm_separator;
}

#   Section starting on page 130 of Version 1 FR, p. 52 Version 2
sub print_env_emb_pitch {
  local($message, *address) = @_;
  debugaddr($address);
  $vel_sens_to_level   = get_unsigned_byte_range(*address, 16);
  $vel_sens_to_rate    = get_signed_16_value(*address);
  $hold_time           = get_unsigned_byte_range(*address, 127);
  local(@hold_brks)    = get_brkpts(*address, *get_signed_64_value, 2);

  $initial_level       = get_signed_64_value(*address);
  local(@init_level_brks) 
                       = get_brkpts(*address, *get_signed_64_value, 2);
  $attack_rate         = get_unsigned_byte_range(*address, 127);
  local(@attack_rate_brks)
                       = get_brkpts(*address, *get_unsigned_byte, 2);
  $attack_level        = get_signed_64_value(*address);
  local(@attack_level_brks)
                       = get_brkpts(*address, *get_unsigned_byte, 2);
  $decay_rate          = get_unsigned_byte_range(*address, 127);
  local(@decay_rate_brks)
                       = get_brkpts(*address, *get_unsigned_byte, 2);
  $depth_to_emb        = get_unsigned_byte_range(*address, 64);
  $depth_to_pitch      = get_unsigned_byte_range(*address, 64);

  print "$message\n";

  if (!$initial_level) {
    warn_msg("Initial Level 0. No emboucher & pitch envelope.\n");
    if ($terse) {
      print $parm_separator;
      return;
    }
  }

  if (nontersed($vel_sens_to_level)) {
    print enhanced("Velocity Sens To Level= %3d", 
		   $vel_sens_to_level, 16), "\n";
  }
  
  if (nontersed($vel_sens_to_rate)) {
    print enhanced("Velocity Sens To Rate = %3d", 
		   $vel_sens_to_rate, 16), "\n";
  }
  

  print enhanced("Depth To Emb  = %3d", $depth_to_emb  , 64), "\n"
    if nontersed($depth_to_emb);

  print enhanced("Depth To Pitch= %3d", $depth_to_pitch, 64), "\n"
    if nontersed($depth_to_pitch);
  
  print_brks("Hold Time     ", $hold_time,    127, @hold_brks);
  print_brks("Initial Level ", $initial_level, 64, @init_level_brks);
  print_brks("Attack Rate   ", $attack_rate,  127, @attack_rate_brks);
  print_brks("Attack Level  ", $attack_level, 127, @attack_level_brks);

  if (nontersed($vel_sens_to_rate)) {
    if (nontersed($decay_rate)) {
      print_brks("Decay Rate    ",  $decay_rate, 127, @decay_rate_brks) 
    }
  }
  
  print $parm_separator;

}

#   Section starting on page 136 of Version 1 FR, p. 52 Version 2
sub print_env_vibrato {
  local($message, *address) = @_;
  debugaddr($address);

  $dl_time           = get_unsigned_byte_range(*address, 127);
  local(@dltime_brk) = get_brkpts(*address, *get_signed_64_value, 2);
  $at_rate           = get_unsigned_byte(*address, 127);
  local(@atrate_brk) = get_brkpts(*address, *get_signed_64_value, 2);
  $sustain_level     = get_unsigned_byte_range(*address, 127);
  $vib_depth         = get_unsigned_byte_range(*address, 127);
  local(@depth_brk)  = get_brkpts(*address, *get_signed_64_value, 2);
  $vib_depth_to_emb  = get_unsigned_byte_range(*address, 127);
  $vib_depth_to_pitch= get_unsigned_byte_range(*address, 127);
  $vib_offset        = get_signed_127_value(*address);
  $vib_speed         = get_unsigned_byte_range(*address, 127);
  local(@speed_brk)  = get_brkpts(*address, *get_signed_64_value, 2);
  $vib_speed_shift   = get_unsigned_byte_range(*address, 16);
  $vib_speed_random  = get_unsigned_byte_range(*address, 10);

  # Don't bother showing if no vibrato controller.
  return if $vib_ctrl eq 'off' && (0==$sustain_level) && $terse;

  print "$message\n";

  if (0==$vib_depth_to_pitch && 0==$vib_depth_to_emb) {
    warn_msg("Both pitch and emboucher depth zero. No Vibrato Effect.\n");;
    if ($terse) {
      print $parm_separator;
      return;
    }
  }
  
  if (0==$vib_depth) {
    warn_msg("Vibrato depth zero. No Vibrato Effect.\n");;
    if ($terse) {
      print $parm_separator;
      return;
    }
  }
  
  if (0==$sustain_level) {
    info_msg("Vibrato controlled only by controller: $vib_ctrl\n");
    return if stop_if_no_ctrl($vib_ctrl, "", 1);
  } 

  if (0 != $sustain_level || !$terse) {
    if (nontersed($dl_time)) {
      print enhanced("Delay Time     = %3d", $dl_time, 127), "\n";
      print_breakpoints("",   @dltime_brk);
    }

    if (nontersed($at_rate)) {
      print enhanced("Attack Rate     = %3d", $at_rate, 127), "\n";
      print_breakpoints("",   @atrate_brk);
    }
  }

  print enhanced("Sustain Level   = %3d", $sustain_level, 127), "\n";

  print enhanced("Vibrato Depth   = %3d", $vib_depth, 127), "\n";
  print_breakpoints("",   @depth_brk);

  if (nontersed($vib_depth_to_emb)){
    print enhanced("Depth to Emb    = %3d", $vib_depth_to_emb, 127);
    if (nontersed($vib_depth_to_pitch)) {
      print " ";
    } else {
      print "\n";
    }
  }
  if (nontersed($vib_depth_to_pitch)){
    print enhanced("Depth to Pitch  = %3d", $vib_depth_to_pitch, 127);
    print "\n";
  }

  if (nontersed($vib_offset)){
    print enhanced("Offset          = %3d", $vib_offset, 127);
    if (nontersed($vib_speed_random)) {
      print " ";
    } else {
      print "\n";
    }
  }
  if (nontersed($vib_speed_random)){
    print enhanced("Randomness      = %3d", $vib_speed_random, 127), "\n";
  }

  if (nontersed($vib_speed)){
    print enhanced("Speed           = %3d", $vib_speed, 127), "\n";
    print_breakpoints("",   @speed_brk);
  }
  if (nontersed($vib_speed_shift)) {
    print enhanced("Speed Shift     = %3d", $vib_speed_shift, 16), "\n";
  }

  print $parm_separator;
}

#   Section starting on page 141 of Version 1 FR, p. 52 Version 2
sub print_env_growl {
  local($message, *address) = @_;
  debugaddr($address);

  $gr_depth_to_press  = get_unsigned_byte_range(*address, 127);
  $gr_depth_to_bnoise = get_unsigned_byte_range(*address, 127);
  $gr_offset          = get_signed_127_value(*address);
  $gr_speed           = get_unsigned_byte_range(*address, 127);
  local(@growl_brk)   = get_brkpts(*address, *get_signed_64_value, 2);
  $gr_speed_random    = get_unsigned_byte_range(*address, 10);
  $gr_speed_shift     = get_unsigned_byte_range(*address, 16);
  $gr_vib_sync        = get_on_off(*address);

  print "$message\n";

  if (nontersed($gr_depth_to_press)){
    print enhanced("Depth to Pressure = %3d", $gr_depth_to_press, 127);
    if (nontersed($gr_depth_to_bnoise)) {
      print " ";
    } else {
      print "\n";
    }
  }
  if (nontersed($gr_depth_to_bnoise)){
    print enhanced("Depth to Breath Noise = %3d", $gr_depth_to_bnoise, 127);
    print "\n";
  }
  if (nontersed($gr_offset)){
    print enhanced("Offset          = %3d", $gr_offset, 127), "\n";
  }

  print "Vibrato Sync    = $gr_vib_sync\n";

  if (nontersed($gr_speed)){
    print enhanced("Speed           = %3d", $gr_speed, 127), "\n";
    print_breakpoints("",   @growl_brk);
  }

  if (nontersed($gr_speed_shift)) {
    print enhanced("Speed Shift     = %3d", $gr_speed_shift, 16), "\n";
  }

  if (nontersed($gr_speed_random)){
    print enhanced("Randomness      = %3d", $gr_speed_random, 127), "\n";
  }
  print $parm_separator;
}
