#$Id: instrument.pl,v 1.8 1999/06/01 03:04:08 rocky Exp $
#  instrument section for programs which work with the Yamaha VL
#  "patches" or voices or voice libraries.
#
#    Copyright (C) 1999 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.

sub inst_init {
  @input_gain_cvt = ( 
    '0.000', '0.000', '0.000', '0.001', '0.001', '0.002', '0.002', '0.003',
    '0.004', '0.005', '0.006', '0.007', '0.009', '0.010', '0.012', '0.014',
    '0.016', '0.018', '0.020', '0.022', '0.024', '0.027', '0.030', '0.032',
    '0.035', '0.038', '0.041', '0.044', '0.048', '0.051', '0.055', '0.059',
    '0.063', '0.066', '0.071', '0.075', '0.079', '0.084', '0.088', '0.093',
    '0.098', '0.103', '0.108', '0.113', '0.118', '0.124', '0.129', '0.135',
    '0.141', '0.147', '0.153', '0.159', '0.165', '0.171', '0.178', '0.185',
    '0.191', '0.198', '0.205', '0.212', '0.220', '0.227', '0.235', '0.242',
    '0.250', '0.258', '0.266', '0.274', '0.282', '0.291', '0.299', '0.308',
    '0.316', '0.325', '0.334', '0.343', '0.353', '0.362', '0.371', '0.381',
    '0.391', '0.400', '0.410', '0.420', '0.431', '0.441', '0.451', '0.462',
    '0.473', '0.483', '0.494', '0.505', '0.517', '0.518', '0.539', '0.551', 
    '0.563', '0.574', '0.586', '0.598', '0.610', '0.623', '0.635', '0.648', 
    '0.660', '0.673', '0.686', '0.699', '0.712', '0.725', '0.739', '0.752', 
    '0.766', '0.779', '0.793', '0.807', '0.821', '0.836', '0.850', '0.864', 
    '0.879', '0.894', '0.908', '0.923', '0.938', '0.954', '0.969', '0.984' 
  );

  @graham_arg_cvt = ( 
    '0.064', '0.065', '0.067', '0.068', '0.070', '0.071', '0.073', '0.074',
    '0.076', '0.078', '0.079', '0.081', '0.083', '0.085', '0.086', '0.088', 
    '0.090', '0.092', '0.094', '0.096', '0.098', '0.101', '0.103', '0.105', 
    '0.107', '0.110', '0.112', '0.115', '0.117', '0.120', '0.122', '0.125', 
    '0.128', '0.130', '0.133', '0.136', '0.139', '0.142', '0.145', '0.149', 
    '0.152', '0.155', '0.159', '0.162', '0.166', '0.169', '0.173', '0.177', 
    '0.181', '0.184', '0.189', '0.193', '0.197', '0.201', '0.206', '0.210', 
    '0.215', '0.219', '0.224', '0.229', '0.234', '0.239', '0.244', '0.250', 
    '0.255', '0.261', '0.267', '0.272', '0.278', '0.284', '0.291', '0.297', 
    '0.304', '0.310', '0.317', '0.324', '0.331', '0.338', '0.346', '0.353', 
    '0.361', '0.369', '0.377', '0.385', '0.394', '0.402', '0.411', '0.420', 
    '0.429', '0.439', '0.448', '0.458', '0.468', '0.478', '0.489', '0.500', 
    '0.511', '0.522', '0.533', '0.545', '0.557', '0.569', '0.581', '0.594', 
    '0.607', '0.620', '0.634', '0.648', '0.662', '0.677', '0.691', '0.701', 
    '0.722', '0.738', '0.754', '0.771', '0.787', '0.805', '0.822', '0.840', 
    '0.859', '0.878', '0.897', '0.916', '0.936', '0.957', '0.978', '1.000',
  );

  @driver_out_cvt = ( 
    '0.076', '0.078', '0.079', '0.081', '0.083', '0.085', '0.086', '0.088',
    '0.090', '0.092', '0.094', '0.096', '0.098', '0.101', '0.103', '0.105',
    '0.107', '0.110', '0.112', '0.115', '0.117', '0.120', '0.122', '0.125',
    '0.128', '0.130', '0.133', '0.136', '0.139', '0.142', '0.145', '0.149',
    '0.152', '0.155', '0.159', '0.162', '0.166', '0.169', '0.173', '0.177',
    '0.181', '0.184', '0.189', '0.193', '0.197', '0.201', '0.206', '0.210',
    '0.215', '0.219', '0.224', '0.229', '0.234', '0.239', '0.244', '0.250',
    '0.255', '0.261', '0.267', '0.272', '0.278', '0.284', '0.291', '0.297',
    '0.304', '0.310', '0.317', '0.327', '0.324', '0.331', '0.338', '0.346',
    '0.353', '0.361', '0.369', '0.377', '0.385', '0.394', '0.402', '0.411',
    '0.420', '0.429', '0.439', '0.448', '0.458', '0.468', '0.478', '0.489',
    '0.500', '0.511', '0.522', '0.533', '0.545', '0.557', '0.569', '0.581',
    '0.594', '0.607', '0.620', '0.634', '0.648', '0.662', '0.677', '0.691',
    '0.707', '0.722', '0.738', '0.754', '0.771', '0.787', '0.805', '0.822',
    '0.840', '0.859', '0.878', '0.897', '0.915', '0.936', '0.957', '1.000',
  );

  @rez_lag_time_cvt = ( 
    'fastest', '1.46m', '1.52m', '1.59m', '1.66m', '1.73m', '1.81m', '1.89m',
      '1.97m', '2.06m', '2.12m', '2.24m', '2.34m', '2.45m', '2.56m', '2.67m',
      '2.79m', '2.91m', '3.04m', '3.17m', '3.31m', '3.46m', '3.61m', '3.77m',
      '3.94m', '4.12m', '4.30m', '4.49m', '4.69m', '4.89m', '5.11m', '5.34m',
      '5.57m', '5.82m', '6.08m', '6.35m', '6.63m', '6.92m', '7.23m', '7.55m',
      '7.88m', '8.23m', '8.60m', '8.98m', '9.37m', '9.79m', '10.2m', '10.7m',
      '11.1m', '11.6m', '12.2m', '12.7m', '13.3m', '13.8m', '14.5m', '15.1m',
      '15.8m', '16.5m', '17.2m', '18.0m', '18.8m', '19.6m', '20.5m', '21.3m',
      '22.3m', '23.3m', '24.3m', '25.4m', '26.5m', '27.7m', '28.9m', '30.2m',
      '31.5m', '32.9m', '34.4m', '35.9m', '37.5m', '39.2m', '40.9m', '42.7m',
      '44.6m', '46.6m', '48.6m', '50.8m', '53.1m', '55.4m', '57.8m', '60.4m',
      '63.1m', '65.9m', '68.8m', '71.9m', '75.0m', '78.4m', '81.9m', '85.4m',
      '89.3m', '93.3m', '97.3m', ' 0.10', ' 0.11', ' 0.11', ' 0.12', ' 0.12',
      ' 0.13', ' 0.13', ' 0.14', ' 0.14', ' 0.15', ' 0.16', ' 0.16', ' 0.17',
      ' 0.18', ' 0.19', ' 0.19', ' 0.20', ' 0.21', ' 0.22', ' 0.23', ' 0.24',
      ' 0.25', ' 0.26', ' 0.28', ' 0.29', ' 0.30', ' 0.31', ' 0.33', ' 0.34',
  );

  @parent_cutoff_cvt = ( 
  ' 15.5', ' 16.2', ' 16.9', ' 17.7', ' 18.4', ' 19.3', ' 20.2', ' 21.0',
  ' 21.9', ' 23.0', ' 24.1', ' 25.1', ' 26.2', ' 27.3', ' 28.5', ' 29.8',
  ' 31.2', ' 32.5', ' 34.0', ' 35.5', ' 37.0', ' 38.7', ' 40.4', ' 42.4',
  ' 44.1', ' 46.1', ' 48.2', ' 50.3', ' 52.5', ' 54.9', ' 57.2', ' 59.2',
  ' 62.5', ' 65.2', ' 68.1', ' 71.2', ' 74.4', ' 77.7', ' 81.1', ' 84.7',
  ' 88.5', ' 92.5', ' 96.7', '100.0', '105.0', '110.0', '114.0', '120.0',
  '125.0', '131.0', '137.0', '143.0', '149.0', '156.0', '163.0', '170.0',
  '178.0', '186.0', '194.0', '203.0', '212.0', '221.0', '231.0', '242.0',
  '253.0', '264.0', '276.0', '289.0', '302.0', '315.0', '330.0', '345.0',
  '360.0', '377.0', '394.0', '412.0', '430.0', '450.0', '471.0', '492.0',
  '515.0', '538.0', '563.0', '589.0', '616.0', '645.0', '676.0', '706.0',
  '739.0', '773.0', '809.0', '847.0', '887.0', '929.0', '973.0', '1.02k',
  '1.07k', '1.12k', '1.17k', '1.23k', '1.29k', '1.35k', '1.42k', '1.48k',
  '1.56k', '1.63k', '1.72k', '1.80k', '1.89k', '1.99k', '2.09k', '2.20k',
  '2.31k', '2.43k', '2.56k', '2.69k', '2.84k', '2.99k', '3.16k', '3.33k',
  '3.52k', '3.72k', '3.93k', '4.16k', '4.41k', '4.68k', '4.97k', '5.30k'
  );

  @child_freq_res = ( 
  ' 30.5', ' 31.1', ' 31.8', ' 32.5', ' 33.2', ' 34.0', ' 34.7', ' 35.5',
  ' 36.2', ' 37.0', ' 37.8', ' 38.7', ' 39.5', ' 40.4', ' 41.3', ' 42.2',
  ' 43.1', ' 44.0', ' 45.0', ' 46.0', ' 47.0', ' 48.0', ' 49.1', ' 50.2',
  ' 51.3', ' 52.4', ' 53.5', ' 54.7', ' 55.9', ' 57.1', ' 58.4', ' 59.6',
  ' 60.9', ' 62.3', ' 63.6', ' 65.7', ' 66.5', ' 67.9', ' 69.4', ' 70.9',
  ' 72.5', ' 74.1', ' 75.7', ' 77.3', ' 79.0', ' 80.8', ' 82.5', ' 84.3',
  ' 86.2', ' 88.1', ' 90.0', ' 92.0', ' 94.0', ' 96.1', ' 98.2', '100.0',
  '102.0', '104.0', '107.0', '109.0', '111.0', '114.0', '116.0', '119.0',
  '121.0', '124.0', '127.0', '130.0', '132.0', '135.0', '138.0', '141.0',
  '144.0', '148.0', '151.0', '154.0', '158.0', '161.0', '165.0', '168.0',
  '172.0', '176.0', '180.0', '183.0', '187.0', '192.0', '196.0', '200.0',
   # To be continued...
 ) ;

}

sub print_elt_instrument {
  local($message, *where)=@_;
  $elt_name = get_name($where, 10); $where += 10;
  print_elt_inst_pipe_string("$message/PIPE or STRING", *where);
  print_elt_inst_driver("$message/DRIVER", *where);
  print_elt_inst_child_reed("$message/CHILD REED", *where);
  print_elt_inst_parent_reed("$message/PARENT REED", *where);
  print_elt_inst_pressure_control("$message/PRESSURE CONTROL", *where);
  print_elt_inst_emb_control("$message/EMBOUCHER CONTROL", *where);

  # $where += 211; # The remaining part of the instrument section
  $where += 42; # The remaining part of the instrument section
}

sub print_elt_inst_pipe_string {
  local($message, *address)=@_;
  debugaddr($address);
  my($pipe_delay_mode)      = get_choice(*address, ("Straight", "Conical"));
  my($pipe_short_len_mode)  = get_choice(*address, ("Absolute", "Relative"));
  my($pipe_short_len_ratio) = get_unsigned_255_value(*address);
  my(@short_length)         = get_brkpts(*address, *get_signed_255_value, 16);
  my($pipe_insertion)       = get_on_off(*address);
  my($str_horn1_len)        = get_unsigned_byte_range(*address, 127);
  my(@str_horn1_len)        = get_brkpts(*address, *get_signed_64_value, 8);
  my($ratio1_2)             = get_signed_64_value(*address);
  my(@ratio1_2)             = get_brkpts(*address, *get_signed_64_value, 6);
  my($str_horn2_con_len)    = get_short_word_range(*address, 1023);
  my(@str_horn2_con_len)    = get_brkpts(*address, *get_signed_255_value, 8);
  my($ratio2_to_con)        = get_signed_64_value(*address);
  my(@ratio2_to_con)        = get_brkpts(*address, *get_signed_64_value, 6);
  my($high_freq_abs_mode)   = get_choice(*address, ("Both (-12db/octave)", 
						    "One (-6db/octave)"));
  my(@pipe_absorb)          = get_cutoff(*address, *lpf_cutoff_cvt);
  my(@pipe_absorb_brks)     = get_brkpts(*address, *get_signed_64_value, 8);
  my($damping_decay_min)    = get_unsigned_byte_range(*address, 127);
  my($damping_decay_max)    = get_unsigned_byte_range(*address, 127);
  my(@decay_minmax_brks)    = get_brkpts(*address, *get_minmax_signed_64, 6);
  my($register_key_open)    = get_unsigned_byte_range(*address, 127);
  my($high_low_mode)        = get_unsigned_byte_range(*address, 127);
  my(@high_low_mode_brks)   = get_brkpts(*address, *get_signed_64_value, 4);

  print("$message\n");
  print "Straight Horn Insertion: $pipe_insertion" 
      if nontersed($pipe_insertion);
  if ($terse && $pipe_insertion eq 'on') {
    print "  Total Delay Mode: $pipe_delay_mode horn\n";
    print "Straight Horn1 Length  : $str_horn1_len\n";
    print_brks("", $str_horn1_len, 127, @str_horn1_len);
    print "Ratio 1 to 2           : $ratio1_2\n";
    print_brks("", $ratio1_2, 64, @ratio1_2);
    printf "Straight Horn2 Conical Length : %4.3f ms\n", 
           con_len_cvt($str_horn2_con_len);
    print_brks("", $str_horn2_con_len, 1023, @str_horn2_con_len);
    print "Ratio 2 to Conical     : $ratio2_to_con\n";
    print_brks("", $ratio2_to_con, 64, @ratio2_to_con);
  } else {
    print "\n"
  }
  print "Short Length Mode      : $pipe_short_len_mode\n";
  print_brks("Short Length Ratio     ", $pipe_short_len_ratio, 255, 
	     @short_length);
  print "High Frequency Abs Mode: $high_freq_abs_mode\n";
  print_breaks("Absorption             ", 
	       $pipe_absorb[2], 127, *format_lpf_cutoff_val, 
	       @pipe_absorb_brks);

  print "Damping Decay Min      : $damping_decay_min";
  print "  Damping Decay Max: $damping_decay_max\n";

  print_minmax_breaks("", $damping_decay_min, $damping_decay_max,
		      127, undef, @decay_minmax_brks);

  printf "1st Harmonic Damp - Above       : %s\n",
         note_offset_to_note($register_key_open); ;
  print_brks("Low/High Mode Balance           ", $high_low_mode, 127, 
	     @high_low_mode_brks);

  print $parm_separator;
}

sub print_elt_inst_driver {
  local($message, *address)=@_;
  debugaddr($address);
  my($driver_input_gain)  = get_signed_64_value(*address);
  my(@driver_input_gain)  = get_brkpts(*address, *get_signed_64_value, 8);
  my($driver_press_level) = get_unsigned_byte_range(*address, 127);
  my(@driver_press_level) = get_brkpts(*address, *get_signed_64_value, 5);
  my($driver_slit_sign)   = get_choice(*address, ("-", "+"));
  my($driver_add_force)   = get_signed_64_value(*address);
  my($driver_feedback)    = get_signed_64_value(*address, 127);
  $address++;
  my($driver_graham)      = get_signed_64_value(*address);
  my(@driver_graham)      = get_brkpts(*address, *get_signed_64_value, 5);
  my($driver_out_shutoff) = get_choice(*address, ("Shut off", "Through"));
  my($driver_out_level)   = get_signed_64_value(*address);
  my(@driver_out_level)   = get_brkpts(*address, *get_signed_64_value, 5);

  print("$message\n");
  print_breaks("Velocity/Pressure Input Gain    ", 
	       $driver_input_gain, 127, *format_input_gain,
	       @driver_input_gain);

  print_brks("Velocity/Pressure Level to Reed ", $driver_input_gain, 127, 
	     @driver_press_level);

  print  "Friction/Slit Sign: $driver_slit_sign\n";
  printf "Additional Friction Force/Reed Aperature       : %3d\n",
          $driver_add_force;
  print  "Friction Transition/Saturation Feedback Balance: ", 
         enhanced("%3d",  $driver_feedback, 63.5), "\n";

  print_breaks("Graham Function Argument", 
	       $driver_graham, 63, *format_graham_arg, 
	       @driver_graham);

  print "Driver Output Shut Off: $driver_out_shutoff\n";

  print_breaks("Driver Output Level", 
	       $driver_out_level, 55, *format_driver_out, 
	       @driver_out_level);

  print $parm_separator;
}

sub print_elt_inst_child_reed {
  local($message, *address)=@_;
  debugaddr($address);
  my($child_force)    = get_unsigned_byte_range(*address, 127);
  my($child_displace) = get_unsigned_byte_range(*address, 127);
  my($child_veloc)    = get_unsigned_byte_range(*address, 127);
  $child_res_ctrl     = get_choice(*address, "Absolute", "Relative");
  my($child_lag_time) = get_unsigned_byte_range(*address, 127);

  print("$message\n");

  print  "Friction/Force to Child Reed:   ", 
         enhanced("%3d",  $child_force, 127), "\n";

  print  "Reed Displacement Output Level: ", 
         enhanced("%3d",  $child_displace, 127), "\n";

  print  "Reed Velocity Output:           ",
         enhanced("%3d",  $child_veloc, 127), "\n";

  print  "Resonance Control:              $child_res_ctrl\n";

  print  "Resonance Lag Time:             ", 
          format_rez_lag($child_lag_time), "\n";

  print $parm_separator;
}

sub print_elt_inst_parent_reed {
  local($message, *address)=@_;
  debugaddr($address);
  my($parent_force)    = get_unsigned_byte_range(*address, 127);
  my($parent_output)   = get_unsigned_byte_range(*address, 127);
  my($parent_cutoff)   = get_unsigned_byte_range(*address, 127);
  my(@parent_cutoff)   = get_brkpts(*address, *get_signed_64_value, 5);
  my($parent_lip)      = get_signed_64_value(*address);
  my(@parent_lip)      = get_brkpts(*address, *get_signed_64_value, 5);

  print("$message\n");

  # If output level is 0, does any of this matter? 

  print  "Friction/Force to Parent Reed: ", 
         enhanced("%3d",  $parent_force, 127), "\n";

  print  "Output Level                 : ", 
         enhanced("%3d",  $parent_output, 127), "\n";

  print_brks("Lip Collision                ", 
	     $parent_lip, 127, @parent_lip);

  print_breaks("Cutoff                       ", 
	       $parent_cutoff, 127, *format_parent_cutoff, 
	       @parent_cutoff);

  print $parm_separator;
}

sub print_elt_inst_pressure_control {
  local($message, *address)=@_;
  debugaddr($address);

  my($thickness)       = get_unsigned_byte_range(*address, 16);
  my($resonance)       = get_unsigned_byte_range(*address, 16);

  print("$message\n");

  print  "Pressure to Reed Thickness: ", 
         enhanced("%3d",  $thickness, 16), "\n";

  print  "Pressure to Reed Resonance: ", 
         enhanced("%3d",  $resonance, 16), "\n";

  print $parm_separator;
}

sub print_elt_inst_emb_control {
  local($message, *address)=@_;
  debugaddr($address);
  my($init_aperat_min)   = get_unsigned_byte_range(*address, 127);
  my($init_aperat_max)   = get_unsigned_byte_range(*address, 127);
  my(@init_aperat_brks)  = get_brkpts(*address, *get_minmax_signed_64, 8);
  my($mouth_narrow_min)  = get_unsigned_byte_range(*address, 127);
  my($mouth_narrow_max)  = get_unsigned_byte_range(*address, 127);
  my(@mouth_narrow_brks) = get_brkpts(*address, *get_minmax_signed_64, 8);
  my($reed_flex_min)     = get_unsigned_byte_range(*address, 127);
  my($reed_flex_max)     = get_unsigned_byte_range(*address, 127);
  my(@reed_flex_brks)    = get_brkpts(*address, *get_minmax_unsigned_127, 8);
  my($extend_reed_cutoff)= get_on_off(*address);
  my($child_reed_res_min);
  my($child_reed_res_max);
  my(@reed_res_brks);
  if ($child_res_ctrl eq 'Absolute') {
    $child_reed_res_min= get_unsigned_255_value(*address);
    $child_reed_res_max= get_unsigned_255_value(*address);
    @reed_res_brks     = get_brkpts(*address, *get_minmax_short, 12);
  } elsif ($child_res_ctrl eq 'Relative') {
    $child_reed_res_min= get_signed_127_value(*address);
    $child_reed_res_max= get_signed_127_value(*address);
    @reed_res_brks     = get_brkpts(*address, *get_minmax_signed_127, 12);
  }
  my($child_reed_amt_min)= get_unsigned_byte_range(*address, 127);
  my($child_reed_amt_max)= get_unsigned_byte_range(*address, 127);
  my(@child_reed_brks)   = get_brkpts(*address, *get_minmax_signed_64, 8);

  print("$message\n");

  print "\nInitial Aperature Min: ",  
        enhanced("%3d", $init_aperat_min, 127), "\n";

  print "Initial Aperature Max: ", 
        enhanced("%3d", $init_aperat_max, 127), "\n";

  print_minmax_breaks("", $init_aperat_min, $init_aperat_max,
		      127, undef, @init_aperat_brks);

  print "\nMouth Narrowness Min: ", 
        enhanced("%3d", $mouth_narrow_min, 127), "\n";
  print "Mouth Narrowness Max: ", 
        enhanced("%3d", $mouth_narrow_max, 127), "\n";

  print_minmax_breaks("", $mouth_narrow_min, $mouth_narrow_max,
		      127, undef, @mouth_narrow_brks);

  print "\nReed Flexibility Min: ", 
        enhanced("%3d", $reed_flex_min, 127), "\n";
  print "Reed Flexibility Max: ", 
        enhanced("%3d", $reed_flex_max, 127), "\n";

  print_minmax_breaks("", $reed_flex_min, $reed_flex_max,
		      127, undef, @reed_flex_brks);

  print "\nExtend Child Reed Cutoff: $extend_reed_cutoff\n";

  print "\nChild Reed Resonance Freq. Min: ", 
         format_child_reed_res($child_reed_res_min), "\n";
  print "Child Reed Resonance Freq. Max: ", 
         format_child_reed_res($child_reed_res_max), "\n";

  print_minmax_breaks("", $child_reed_res_min, $child_reed_res_max,
		      $extend_reed_cutoff eq 'on' ? 1024 : 255, 
		      *format_child_reed_res, @reed_res_brks);

  print "\nChild Reed Resonance Amount Min: ", 
         format_rez($child_reed_amt_min), "\n";
  print "Child Reed Resonance Amount Max: ", 
         format_rez($child_reed_amt_max), "\n";

  print_minmax_breaks("", $child_reed_amt_min, $child_reed_amt_max,
		      127, *format_rez, @child_reed_brks);

  print $parm_separator;
}

sub print_minmax_breaks {
  local($message, $minlevel, $maxlevel, $max, *routine, @brks) = @_;
  my($pitch_str); 
  my($curr_pitch, $might_warn);

  return if !@brks;

  my($ref)=$brk[0];
  my($prev_pitch)  = $ref->[0];
  my($prev_offset_max) = $ref->[1];
  my($prev_offset_min) = $ref->[2];

  print "$message\n" if $message;

  # Check to see if the levels are all the same. 
  if ($terse) {
    my($ref)=$brk[0];
    $prev_offset_min   = $ref->[1];
    $prev_offset_max   = $ref->[2];
    foreach $brk (@brks) {
      goto not_flat if $prev_offset_min != $brk->[1] 
	            || $prev_offset_max != $brk->[2];
    }
    # Yep they are the same. I thought so. 
    # If the offset zero then this doesn't add/subtract to the 
    # overall level, so since we're tersed, don't add to the clutter.
    return if $prev_offset_min == 0 && $pref_offset_max == 0;
    my($effective_level_max) = $minlevel + $prev_offset_max;
    my($effective_level_min) = $maxlevel + $prev_offset_min;
    if ($minlevel + $prev_offset_min > $max) {
      warn_msg("min value + offset > max value $max. $max used.\n");
      $effective_level_min = $max;
    }
    if ($maxlevel + $prev_offset_max > $max) {
      warn_msg("min value + offset > max value $max. $max used.\n");
      $effective_level_min = $max;
    }
    printf "  all min brkpt values: %3d, effective level: %3d\n", 
      $level_min, $effective_level_min;
    printf "  all max brkpt values: %3d, effective level: %d\n", 
      $level_max, $effective_level_max;
    return;
  not_flat: 
    
  }
  
  $prev_pitch  = -9999; 
  $prev_offset = -9999;
  my($i) = 0; 
  foreach $brk (@brks) {
    $i++;
    $curr_pitch  = $brk->[0];
    $curr_offset_min = $brk->[1];
    $curr_offset_max = $brk->[2];
    $pitch_str = note_offset_to_note($curr_pitch);
    my($same) = $prev_offset_min == $curr_offset_min 
             && $prev_offset_max == $curr_offset_max && 
	        $prev_pitch == $curr_pitch;
    if (nontersed( ($i > $#brks && !$same)
		   || $prev_pitch != $curr_pitch)) {
      $might_warn = $prev_pitch > $curr_pitch;
      my($minsum) = $minlevel + $curr_offset_min;
      my($maxsum) = $maxlevel + $curr_offset_max;

      # If we've got a breakpoint that's has the same offset
      # value as the ones around it and we want a terse listing,
      # we don't have to print this one. The effect is the same
      # as whatever was around it.
      if (nontersed(($i > $#brks)
		    || (($curr_offset_min != $prev_offset_min 
		      || $curr_offset_max != $prev_offset_max
		      || $curr_offset != $brks[$i]->[1])))) {
	printf "   %2d: %-5s, ", $i, $pitch_str, $curr_offset_min;
	printf "(%3d)", $curr_offset_min if $debug & 2;
	if ($enhanced) {
	  my($val) = ($minsum > $max) ? $max : $minsum;
	  if (!defined(&routine)) {
	    printf " %3d", $val;
	  } else {
	    printf "  %s", routine($val);
	  }
	}
	printf " | ", $curr_offset_max;
	printf "(%3d)", $curr_offset_max if $debug & 2;
	if ($enhanced) {
	  my($val) = ($maxsum > $max) ? $max : $maxsum;
	  if (!defined(&routine)) {
	    printf " %3d", $val;
	  } else {
	    printf " %s", routine($val);
	  }
	}
	printf "\n";
      }
      if ($minsum > $max) {
	warn_msg("value + offset > max value $max. $max used.\n")
      }
      if ($maxsum > $max) {
	warn_msg("value + offset > max value $max. $max used.\n")
      }
      warn_msg("this breakpoint not larger than previous one.\n")
	if $might_warn;
    }
    $prev_pitch  = $curr_pitch;
    $prev_offset_min = $curr_offset_min;
    $prev_offset_max = $curr_offset_max;
  }
}

sub format_input_gain {
  my($val) = @_;
  return format_cvt($val, *input_gain_cvt, 64, undef, 3);
}

sub format_graham_arg {
  my($val) = @_;
  return format_cvt($val, *graham_arg_cvt, 64, undef, 3);
}

sub format_driver_out {
  my($val) = @_;
  return format_cvt($val, *driver_out_cvt, 64, undef, 3);
}

sub format_rez_lag {
  my($val) = @_;
  return "fastest" if $val == 0;
  return format_cvt($val, *rez_lag_time_cvt, 0, "sec", 3);
}

sub format_parent_cutoff {
  my($val) = @_;
  return format_cutoff($val, *parent_cutoff_cvt);
}

sub format_child_reed_res {
  my($val) = @_;
  if ($child_res_ctrl eq 'Relative') {
    if ($extend_reed_cutoff) { 
      # Need to add whether $extend_reed_cutoff is in effect or not.
      $rem = $val % 4;
      $val = $val / 4
    }
    return val_to_oct($val);
  } elsif ($child_res_ctrl eq 'Absolute') {
    # Fix up -- add table lookup.
    # Need to add whether $extend_reed_cutoff is in effect or not.
    return $val;
  } else {
    warn_msg("Invalid child_res_ctrl: $child_res_ctrl");
    return $val;
  }
}

sub con_len_cvt {
  my($val) = @_;
  return $val * 0.020854;
}

sub get_minmax_signed_64 {
  local(*where) = @_;
  my($min) = get_signed_64_value(*where);
  my($max) = get_signed_64_value(*where);
  return ($min, $max);
}

sub get_minmax_signed_127 {
  local(*where) = @_;
  my($min) = get_signed_127_value(*where);
  my($max) = get_signed_127_value(*where);
  return ($min, $max);
}

sub get_minmax_unsigned_127 {
  local(*where) = @_;
  my($min) = get_unsigned_byte_range(*where, 127);
  my($max) = get_unsigned_byte_range(*where, 127);
  return ($min, $max);
}

sub get_minmax_short {
  local(*where) = @_;
  my($min) = get_short_word(*where);
  my($max) = get_short_word(*where);
  return ($min, $max);
}

sub get_minmax_signed_127 {
  local(*where) = @_;
  my($min) = get_signed_127_value(*where);
  my($max) = get_signed_127_value(*where);
  return ($min, $max);
}

1;
