{ S Z . P A S
  ===========
  Synthe-Zap! timbre editor for Ad Lib* instrument files.

  Copyright (C) 1990 by J Meeks & Machina Sapiens.

  * Ad Lib is a trademark of Ad Lib, Inc.

  Thanks to Matthew Cooley for providing the format of the .INS file!

  Written using Turbo Pascal 5.5 by J Meeks, 03-03-90. }



program SZ;


{ The following directive ought to define Shareware to compile the fully
  functional, registered version of Synthe/Zap!.  To compile the demo version
  (without the ability to save instruments), instead define Demonstration. }

(*
{$Define Shareware}
*)
{$Define Demonstration}

{$I Switches.Inc}


uses
  CRT, Graph, Printer,
  JUtility, JString, JVideo, JGraph, JKeybd, JMouse, JPlaque, JButton, JKnob,
  JFieldGr, JFile, JAdLib;



const
  TestPitch:       array [1.. 2, 0.. 12] of PitchStr =
                    (('c', 'd', 'e', 'f', 'g', 'a', 'b', 'C', 'D', 'E', 'F',
                      'G', 'A'),
                     ('e``', 'f``', 'g``', 'a``', 'b``', 'c`', 'd`', 'e`',
                      'f`', 'g`', 'a`', 'b`', 'c'));


var
  StafX0,
  StafY0:          word;

  LastInstr:       Str8;

  LastLoadDef,
  InstrDef:        DrvDataDescr;

  FirstLoad:       boolean;

  CurrClef:        byte;
  UnderClef:       pointer;

  IntroPlq,
  MainPlq:         Plaque;

  CarrBx,
  ModuBx,
  NameBx,
  ModeBx,
  StafBx,
  VoicBx:          GrooveBox;

  CAttackRateKnb,
  CDecayRateKnb,
  CSustainLevKnb,
  CReleaseRateKnb,
  CFreqMultipKnb,
  COutputLevelKnb,
  CLevelScalKnb,
  MAttackRateKnb,
  MDecayRateKnb,
  MSustainLevKnb,
  MReleaseRateKnb,
  MFreqMultipKnb,
  MModFeedbackKnb,
  MOutputLevelKnb,
  MLevelScalKnb:   KnobLined;

  StopBtn:         OctagonButton;

  CSustainSndBtn,
  CEnvScalingBtn,
  CVibratoBtn,
  CTremoloBtn,
  MSustainSndBtn,
  MEnvScalingBtn,
  MVibratoBtn,
  MTremoloBtn:     OnOffButton;

  NameBtn,
  ModeBtn,
  VoicBtn,
  ClefBtn:         Button;

  NoteBtn:         array [0.. 12] of Button;

  CurrMode,
  CurrVoice:       integer;

  I:               byte;
  Key:             char;




procedure DrawNote (I, LocX, LocY: word; Color: ScrnColor);
var
  NoteChar:        char;
  SaveTextSet:     TextSettingsType;
  SaveColor:       ScrnColor;
begin
  GetTextSettings (SaveTextSet);
  SaveColor := GetColor;
  SetTextJustify (LeftText, TopText);
  SetTextStyle (DefaultFont, HorizDir, 1);
  SetTextStyle (TriplexFont, HorizDir, 4);
  SetColor (Color);
  if (I <= 5) then
    NoteChar := 'ã'
  else
    NoteChar := 'è';
  OutTextXY (LocX + 25 + 13 * I, LocY + 6 - 2 * I, NoteChar);
  SetTextStyle (SaveTextSet.Font, SaveTextSet.Direction, SaveTextSet.CharSize);
  SetTextJustify (SaveTextSet.Horiz, SaveTextSet.Vert);
  SetColor (SaveColor);
end;    { procedure DrawNote. }



procedure DrawClef (LocX, LocY: word; Color: ScrnColor);
var
  SaveTextSet:     TextSettingsType;
  SaveColor:       ScrnColor;
begin
  GetTextSettings (SaveTextSet);
  SaveColor := GetColor;
  SetTextJustify (LeftText, TopText);
  SetTextStyle (DefaultFont, HorizDir, 1);
  SetTextStyle (TriplexFont, HorizDir, 4);
  SetColor (Color);
  case CurrClef of
    Treble: OutTextXY (LocX + 5, LocY, 'ï');
    Bass:   OutTextXY (LocX + 5, LocY, 'î');
  end;
  SetTextStyle (SaveTextSet.Font, SaveTextSet.Direction, SaveTextSet.CharSize);
  SetTextJustify (SaveTextSet.Horiz, SaveTextSet.Vert);
  SetColor (SaveColor);
end;    { procedure DrawClef. }



{ Display staff with G clef and notes user can use to play simple tunes for
  testing the timbre being edited. }

procedure DrawStaff (LocX, LocY: word);
var
  SaveTextSet:     TextSettingsType;
  SaveColor:       ScrnColor;
begin
  GetTextSettings (SaveTextSet);
  SaveColor := GetColor;
  SetTextJustify (LeftText, TopText);
  SetTextStyle (DefaultFont, HorizDir, 1);
  SetTextStyle (TriplexFont, HorizDir, 4);
  { Staff & end bars. }
  SetColor (LightBlue);
  OutTextXY (LocX, LocY, 'ððððð');
  OutTextXY (LocX, LocY, 'ñ');
  OutTextXY (LocX + 200, LocY, 'ñ');
  { Ledger lines. }
  HorzLine (LocX + 22, LocX + 36, LocY + 35);
  HorzLine (LocX + 178, LocX + 192, LocY + 11);
  { Save area that will be covered by clef (save this one time, then restore
    every time a new clef needs to be drawn). }
  GetMem (UnderClef, ImageSize (LocX + 5, LocY + 9, LocX + 18, LocY + 36));
  GetImage (LocX + 5, LocY + 9, LocX + 19, LocY + 36, UnderClef^);
  { Now draw clef. }
  SetColor (Yellow);
  OutTextXY (LocX + 5, LocY, 'ï');
  ClefBtn.Create (LocX + 5, LocY + 9, 13, 27);
  { Notes and their associated (invisible) buttons. }
  for I := 0 to 12 do begin
    DrawNote (I, LocX, LocY, LightCyan);
    { Use 10 in the next-to-last (width) parameter of this Create for dead
      zones between note-buttons, 13 for buttons that run right up to one
      another. }
    NoteBtn [I].Create (LocX + 24 + 13 * I, LocY + 21 - 2 * I,
     10, 28);
  end;
  SetTextStyle (SaveTextSet.Font, SaveTextSet.Direction, SaveTextSet.CharSize);
  SetTextJustify (SaveTextSet.Horiz, SaveTextSet.Vert);
  SetColor (SaveColor);
end;    { procedure DrawStaff. }


procedure Setup;
const
  S1 =             5;
  S2 =             8;
  S3 =             0;
  S4 =             8;
begin
  EndSession := False;
  FirstLoad := True;
  LastInstr := 'PIANO1';
  FillChar (InstrDef, SizeOf (InstrDef), 0);
  FillChar (LastLoadDef, SizeOf (LastLoadDef), 0);
  { Set up graphics. }
  LoadBGIFont ('TripMusi.Chr');
  DummyInt := GraphSetup;
  { Set up mouse. }
  MouseBtn := InitMouse;
  SetMouseLimits (5, 1, 629, 334);
  SetMousePos (280, 200);
  MouseGraphCursor (MouseUpLftHandCurs);
  ClearTo (SolidFill, DarkGray);

  { Initialize main plaque. }
  MainPlq.Init  (10, 10, 620, 330, 16, Blue, LightBlue, Black, PlqRound);

  { Initialize groove boxes and attach items. }
  CarrBx.Init  (25,  85, 290, 246, LightBlue, Black, Blue, LightBlue,
   'Carrier',   TxHLeft);

  ModuBx.Init  (325, 85, 290, 246, LightBlue, Black, Blue, LightBlue,
   'Modulator', TxHLeft);

  NameBx.Init  (25,  20, 116, 22,  LightBlue, Black, Blue, LightBlue,
   'Name',      TxHLeft);
  NameBtn.AttachToBox (NameBx);

  ModeBx.Init  (25,  52, 102, 22,  LightBlue, Black, Blue, LightBlue,
   'Mode',      TxHLeft);
  ModeBtn.AttachToBox (ModeBx);

  VoicBx.Init  (142, 52, 78,  22,  LightBlue, Black, Blue, LightBlue,
   'Voice',     TxHLeft);
  VoicBtn.AttachToBox (VoicBx);

  StafBx.Init  (236, 20, 240, 54,  LightBlue, Black, Blue, LightBlue,
   'Test Play', TxHLeft);
  StafX0 := StafBx.X + 19;
  StafY0 := StafBx.Y + 5;

  { Initialize knobs & buttons. }
  StopBtn.Init          (585,     24,      24,   LightRed, Black, Red,
   White, Red,   LightRed);

  with CarrBx do begin
    CSustainSndBtn.Init (X + 26,  Y + 65,  0, 0, LightMagenta, Black, Magenta,
     White, Magenta, LightMagenta, Black, 'On', 'Off', TxHLeft, True);
    CEnvScalingBtn.Init (X + 26,  Y + 85,  0, 0, LightMagenta, Black, Magenta,
     White, Magenta, LightMagenta, Black, 'On', 'Off', TxHLeft, True);
    CVibratoBtn.Init    (X + 26,  Y + 200, 0, 0, LightMagenta, Black, Magenta,
     White, Magenta, LightMagenta, Black, 'On', 'Off', TxHLeft, False);
    CTremoloBtn.Init    (X + 26,  Y + 220, 0, 0, LightMagenta, Black, Magenta,
     White, Magenta, LightMagenta, Black, 'On', 'Off', TxHLeft, False);
    CAttackRateKnb.Init  (0.0,  15.0, 1,  -150, +150, 16, X + 45,  Y + 40,
     27, LightGreen, Black, Green, White, LightGreen, Green, White);
    CDecayRateKnb.Init   (0.0,  15.0, 1,  -150, +150, 16, X + 110, Y + 40,
     27, LightGreen, Black, Green, White, LightGreen, Green, White);
    CSustainLevKnb.Init  (0.0,  15.0, 1,  -150, +150, 16, X + 175, Y + 40,
     27, LightGreen, Black, Green, White, LightGreen, Green, White);
    CReleaseRateKnb.Init (0.0,  15.0, 1,  -150, +150, 16, X + 240, Y + 40,
     27, LightGreen, Black, Green, White, LightGreen, Green, White);
    CFreqMultipKnb.Init  (0.0,  15.0, 1,  -150, +150, 16, X + 75,  Y + 130,
     22, LightGreen, Black, Green, White, LightGreen, Green, White);
    COutputLevelKnb.Init (0.0,  63.0, 1,  -150, +150, 64, X + 75,  Y + 175,
     22, LightGreen, Black, Green, White, LightGreen, Green, White);
    CLevelScalKnb.Init   (0.0,  3.0,  1,  -150, +150, 4,  X + 175, Y + 175,
     22, LightGreen, Black, Green, White, LightGreen, Green, White);
  end;
  with ModuBx do begin
    MSustainSndBtn.Init (X + 26,  Y + 65,  0, 0, LightMagenta, Black, Magenta,
     White, Magenta, LightMagenta, Black, 'On', 'Off', TxHLeft, True);
    MEnvScalingBtn.Init (X + 26,  Y + 85,  0, 0, LightMagenta, Black, Magenta,
     White, Magenta, LightMagenta, Black, 'On', 'Off', TxHLeft, True);
    MVibratoBtn.Init    (X + 26,  Y + 200, 0, 0, LightMagenta, Black, Magenta,
     White, Magenta, LightMagenta, Black, 'On', 'Off', TxHLeft, False);
    MTremoloBtn.Init    (X + 26,  Y + 220, 0, 0, LightMagenta, Black, Magenta,
     White, Magenta, LightMagenta, Black, 'On', 'Off', TxHLeft, False);
    MAttackRateKnb.Init  (0.0,  15.0, 1,  -150, +150, 16, X + 45,  Y + 40,
     27, LightGreen, Black, Green, White, LightGreen, Green, White);
    MDecayRateKnb.Init   (0.0,  15.0, 1,  -150, +150, 16, X + 110, Y + 40,
     27, LightGreen, Black, Green, White, LightGreen, Green, White);
    MSustainLevKnb.Init  (0.0,  15.0, 1,  -150, +150, 16, X + 175, Y + 40,
     27, LightGreen, Black, Green, White, LightGreen, Green, White);
    MReleaseRateKnb.Init (0.0,  15.0, 1,  -150, +150, 16, X + 240, Y + 40,
     27, LightGreen, Black, Green, White, LightGreen, Green, White);
    MFreqMultipKnb.Init  (0.0,  15.0, 1,  -150, +150, 16, X + 75,  Y + 130,
     22, LightGreen, Black, Green, White, LightGreen, Green, White);
    MModFeedbackKnb.Init (0.0,  7.0,  1,  -150, +150, 8,  X + 175, Y + 130,
     22, LightGreen, Black, Green, White, LightGreen, Green, White);
    MOutputLevelKnb.Init (0.0,  63.0, 1,  -150, +150, 64, X + 75,  Y + 175,
     22, LightGreen, Black, Green, White, LightGreen, Green, White);
    MLevelScalKnb.Init   (0.0,  3.0,  1,  -150, +150, 4,  X + 175, Y + 175,
     22, LightGreen, Black, Green, White, LightGreen, Green, White);
  end;

  CurrClef := Treble;

  MainPlq.Refresh;

  NameBx.Draw;
  ModeBx.Draw;
  VoicBx.Draw;
  StafBx.Draw;
  CarrBx.Draw;
  ModuBx.Draw;
  StopBtn.Normal;
  DrawStaff (StafX0, StafY0);

  CAttackRateKnb.Draw;
  CAttackRateKnb.Labl  (TxHCenter, TxVTop, 0, S1, LightCyan, 'Attack');
  CDecayRateKnb.Draw;
  CDecayRateKnb.Labl   (TxHCenter, TxVTop, 0, S1, LightCyan, 'Decay');
  CSustainLevKnb.Draw;
  CSustainLevKnb.Labl  (TxHCenter, TxVTop, 0, S1, LightCyan, 'Sustain');
  CReleaseRateKnb.Draw;
  CReleaseRateKnb.Labl (TxHCenter, TxVTop, 0, S1, LightCyan, 'Release');
  CFreqMultipKnb.Draw;
  CFreqMultipKnb.Labl  (TxHCenter, TxVTop, 0, S4, LightCyan, 'Freq Mult');
  COutputLevelKnb.Draw;
  COutputLevelKnb.Labl (TxHCenter, TxVTop, 0, S4, LightCyan, 'Out Level');
  CLevelScalKnb.Draw;
  CLevelScalKnb.Labl   (TxHCenter, TxVTop, 0, S4, LightCyan, 'Lev Scale');

  CSustainSndBtn.Normal;
  CSustainSndBtn.Labl (TxHRight, TxVCenter, S2, S3, LightCyan,
   'Sustain Sound');
  CEnvScalingBtn.Normal;
  CEnvScalingBtn.Labl (TxHRight, TxVCenter, S2, S3, LightCyan,
   'Envelope Scaling');
  CVibratoBtn.Normal;
  CVibratoBtn.Labl    (TxHRight, TxVCenter, S2, S3, LightCyan,
   'Pitch Vibrato');
  CTremoloBtn.Normal;
  CTremoloBtn.Labl    (TxHRight, TxVCenter, S2, S3, LightCyan,
   'Amplitude Tremolo');

  MAttackRateKnb.Draw;
  MAttackRateKnb.Labl  (TxHCenter, TxVTop, 0, S1, LightCyan, 'Attack');
  MDecayRateKnb.Draw;
  MDecayRateKnb.Labl   (TxHCenter, TxVTop, 0, S1, LightCyan, 'Decay');
  MSustainLevKnb.Draw;
  MSustainLevKnb.Labl  (TxHCenter, TxVTop, 0, S1, LightCyan, 'Sustain');
  MReleaseRateKnb.Draw;
  MReleaseRateKnb.Labl (TxHCenter, TxVTop, 0, S1, LightCyan, 'Release');
  MFreqMultipKnb.Draw;
  MFreqMultipKnb.Labl  (TxHCenter, TxVTop, 0, S4, LightCyan, 'Freq Mult');
  MModFeedbackKnb.Draw;
  MModFeedbackKnb.Labl (TxHCenter, TxVTop, 0, S4, LightCyan, 'Mod Feedback');
  MOutputLevelKnb.Draw;
  MOutputLevelKnb.Labl (TxHCenter, TxVTop, 0, S4, LightCyan, 'Out Level');
  MLevelScalKnb.Draw;
  MLevelScalKnb.Labl   (TxHCenter, TxVTop, 0, S4, LightCyan, 'Lev Scale');

  MSustainSndBtn.Normal;
  MSustainSndBtn.Labl (TxHRight, TxVCenter, S2, S3, LightCyan,
   'Sustain Sound');
  MEnvScalingBtn.Normal;
  MEnvScalingBtn.Labl (TxHRight, TxVCenter, S2, S3, LightCyan,
   'Envelope Scaling');
  MVibratoBtn.Normal;
  MVibratoBtn.Labl    (TxHRight, TxVCenter, S2, S3, LightCyan,
   'Pitch Vibrato');
  MTremoloBtn.Normal;
  MTremoloBtn.Labl    (TxHRight, TxVCenter, S2, S3, LightCyan,
   'Amplitude Tremolo');

  CurrVoice := 0;
  ClrKeyBuffer;

{$IfDef Shareware}
  IntroPlq.Init (96, 98, 438, 203, 16, Brown, Yellow, Black, PlqRound);
{$Else}
  IntroPlq.Init (96, 93, 438, 233, 16, Brown, Yellow, Black, PlqRound);
{$EndIf}

  with IntroPlq do
    if (MemAvail > ImageSize (X, Y, X2, Y2)) then begin
      IntroPlq.Draw;
      OutTextXYThk (X + 16, Y + 6,  'Machina Sapiens presents', 3,
       2, White, Black, TriplexFont);
      OutTextXYThk (X + 16, Y + 26, 'Synthe­Zap!', 4,
       4, White, Black, TriplexFont);
      OutTextXYThk (X + 16, Y + 72,
       'Instrument Editor for Ad Lib* music synthesizer', 2, 1, White, Black,
       DefaultFont);
      LineThk (X + 16, Y + 89, X2 - 14, Y + 89, 2, Yellow, Black);
      Copyright    (X + 16, Y + 97,  '1990 by J Meeks & Machina Sapiens',
       2, White, Black);
      SetColor (Yellow);
      OutTextXY (X + 16, Y + 112,
       '* Ad Lib is a trademark of Ad Lib, Inc.');
      OutTextXY (X + 16, Y + 124,
       'Synthe/Zap! and Machina Sapiens are trademarks of');
      OutTextXY (X + 16, Y + 134,
       'Machina Sapiens & J Meeks.');
{$IfDef Shareware}
      OutTextXY (X + 16, Y + 150,
       'This is a registered shareware copy of Synthe/Zap!');
      OutTextXY (X + 16, Y + 160,
       'The author thanks you for supporting shareware and');
      OutTextXY (X + 16, Y + 170,
       'Machina Sapiens.  Please enjoy the program, and...');
      OutTextXYThk (X + 16, Y + 184, 'ROCK ''n'' ROLL!   J Meeks', 2, 1,
       White, Black, DefaultFont);
{$Else}
      OutTextXY (X + 16, Y + 150,
       'This is a demonstration copy of the shareware');
      OutTextXY (X + 16, Y + 160,
       'program Synthe/Zap!  To receive the full version');
      OutTextXY (X + 16, Y + 170,
       '(which can save instrument definitions), please');
      OutTextXY (X + 16, Y + 180,
       'send $22.50 in check or money order to:');
      OutTextXYThk (X + 38, Y + 194, 'J Meeks / Machina Sapiens',
       2, 1, White, Black, DefaultFont);
      OutTextXYThk (X + 38, Y + 205, '3808 Greenbay Rd #3',
       2, 1, White, Black, DefaultFont);
      OutTextXYThk (X + 38, Y + 216, 'Richmond, VA  23234',
       2, 1, White, Black, DefaultFont);
      SetTextStyle (TriplexFont, HorizDir, 4);
      OutTextXY (X + 270, Y + 191, 'ððð');
      OutTextXY (X + 270, Y + 191, 'ñ');
      OutTextXY (X + 299, Y + 191, 'ô');
      OutTextXY (X + 381, Y + 191, 'õ');
      SetTextStyle (DefaultFont, HorizDir, 1);
      OutTextXYThk (X + 277, Y + 191, 'î', 2, 4, White, Black, TriplexFont);
      OutTextXYThk (X + 314, Y + 207, 'T', 2, 1, White, Black, DefaultFont);
      OutTextXYThk (X + 322, Y + 209, 'H', 2, 1, White, Black, DefaultFont);
      OutTextXYThk (X + 332, Y + 211, 'A', 2, 1, White, Black, DefaultFont);
      OutTextXYThk (X + 342, Y + 213, 'N', 2, 1, White, Black, DefaultFont);
      OutTextXYThk (X + 352, Y + 209, 'K', 2, 1, White, Black, DefaultFont);
      OutTextXYThk (X + 362, Y + 207, 'S', 2, 1, White, Black, DefaultFont);
      OutTextXYThk (X + 370, Y + 213, '!', 2, 1, White, Black, DefaultFont);
{$EndIf}
      repeat
        ReadMouse;
      until (KeyPressed or MsPressed);
      IntroPlq.Remove;
      ClrKeyBuffer;
    end;
end;    { procedure Setup. }


function CompTimbre (First, Second: DrvDataDescr): boolean;
var
  CompOK:          boolean;

procedure CompLn (LeftNum, RightNum: integer);
begin
  if (LeftNum <> RightNum) then
    CompOK := False;
end;

begin
  CompOK := True;
  CompLn (First.Carrier.AttackRate,       Second.Carrier.AttackRate);
  CompLn (First.Carrier.DecayRate,        Second.Carrier.DecayRate);
  CompLn (First.Carrier.SustainLevel,     Second.Carrier.SustainLevel);
  CompLn (First.Carrier.ReleaseRate,      Second.Carrier.ReleaseRate);
  CompLn (First.Carrier.FreqMultiplier,   Second.Carrier.FreqMultiplier);
  CompLn (First.Carrier.OutputLevel,      Second.Carrier.OutputLevel);
  CompLn (First.Carrier.LevelScaling,     Second.Carrier.LevelScaling);
  CompLn (First.Carrier.SustainSound,     Second.Carrier.SustainSound);
  CompLn (First.Carrier.EnvScaling,       Second.Carrier.EnvScaling);
  CompLn (First.Carrier.PitchVibrato,     Second.Carrier.PitchVibrato);
  CompLn (First.Carrier.AmpVibrato,       Second.Carrier.AmpVibrato);
  CompLn (First.Carrier.SynthTech,        Second.Carrier.SynthTech);
  CompLn (First.Modulator.AttackRate,     Second.Modulator.AttackRate);
  CompLn (First.Modulator.DecayRate,      Second.Modulator.DecayRate);
  CompLn (First.Modulator.SustainLevel,   Second.Modulator.SustainLevel);
  CompLn (First.Modulator.ReleaseRate,    Second.Modulator.ReleaseRate);
  CompLn (First.Modulator.FreqMultiplier, Second.Modulator.FreqMultiplier);
  CompLn (First.Modulator.OutputLevel,    Second.Modulator.OutputLevel);
  CompLn (First.Modulator.LevelScaling,   Second.Modulator.LevelScaling);
  CompLn (First.Modulator.SustainSound,   Second.Modulator.SustainSound);
  CompLn (First.Modulator.EnvScaling,     Second.Modulator.EnvScaling);
  CompLn (First.Modulator.PitchVibrato,   Second.Modulator.PitchVibrato);
  CompLn (First.Modulator.AmpVibrato,     Second.Modulator.AmpVibrato);
  CompLn (First.Modulator.SynthTech,      Second.Modulator.SynthTech);
  CompTimbre := CompOK;
end;    { procedure CompTimbre. }




{ Adjust all controls' settings and display images after loading a new
  instrument. }

procedure TimbreToPanel;
begin
  with InstrDef do begin
    ModeBx.Wipe;
    if (CurrMode = MdPercussive) then
      OutText ('Percussive')
    else
      OutText ('Melodic');

    VoicBx.Wipe;
    if (CurrMode = MdPercussive) then
      OutText (PercVoiceStr [CurrVoice])
    else
      OutText (MeloVoiceStr [CurrVoice]);

    with Carrier do begin
      { Adjust all knobs' and buttons' settings. }
      CAttackRateKnb.SetTo  (15 - AttackRate);
      CDecayRateKnb.SetTo   (15 - DecayRate);
      CSustainLevKnb.SetTo  (15 - SustainLevel);
      CReleaseRateKnb.SetTo (15 - ReleaseRate);
      CFreqMultipKnb.SetTo  (FreqMultiplier);
      COutputLevelKnb.SetTo (63 - OutputLevel);
      CLevelScalKnb.SetTo   (LevelScaling);
      CSustainSndBtn.SetTo  (SustainSound <> 0);
      CEnvScalingBtn.SetTo  (EnvScaling <> 0);
      CVibratoBtn.SetTo     (PitchVibrato <> 0);
      CTremoloBtn.SetTo     (AmpVibrato <> 0);
    end;

    with Modulator do begin
      MAttackRateKnb.SetTo  (15 - AttackRate);
      MDecayRateKnb.SetTo   (15 - DecayRate);
      MSustainLevKnb.SetTo  (15 - SustainLevel);
      MReleaseRateKnb.SetTo (15 - ReleaseRate);
      MFreqMultipKnb.SetTo  (FreqMultiplier);
      MModFeedbackKnb.SetTo (ModFeedback);
      MOutputLevelKnb.SetTo (63 - OutputLevel);
      MLevelScalKnb.SetTo   (LevelScaling);
      MSustainSndBtn.SetTo  (SustainSound <> 0);
      MEnvScalingBtn.SetTo  (EnvScaling <> 0);
      MVibratoBtn.SetTo     (PitchVibrato <> 0);
      MTremoloBtn.SetTo     (AmpVibrato <> 0);
    end;
  end;

  { Now redraw all controls. }
  CAttackRateKnb.Draw;
  CDecayRateKnb.Draw;
  CSustainLevKnb.Draw;
  CReleaseRateKnb.Draw;
  CFreqMultipKnb.Draw;
  COutputLevelKnb.Draw;
  CLevelScalKnb.Draw;

  CSustainSndBtn.Normal;
  CEnvScalingBtn.Normal;
  CVibratoBtn.Normal;
  CTremoloBtn.Normal;

  MAttackRateKnb.Draw;
  MDecayRateKnb.Draw;
  MSustainLevKnb.Draw;
  MReleaseRateKnb.Draw;
  MFreqMultipKnb.Draw;
  MModFeedbackKnb.Draw;
  MOutputLevelKnb.Draw;
  MLevelScalKnb.Draw;

  MSustainSndBtn.Normal;
  MEnvScalingBtn.Normal;
  MVibratoBtn.Normal;
  MTremoloBtn.Normal;
  LastLoadDef := InstrDef;
end;    { procedure TimbreToPanel. }


{ Transfer knob and button settings to timbre descriptor. }

procedure PanelToTimbre (var Timb: DrvDataDescr);
begin
  with Timb.Carrier do begin
    AttackRate :=     Trunc (15 - CAttackRateKnb.Value);
    DecayRate :=      Trunc (15 - CDecayRateKnb.Value);
    SustainLevel :=   Trunc (15 - CSustainLevKnb.Value);
    ReleaseRate :=    Trunc (15 - CReleaseRateKnb.Value);
    FreqMultiplier := Trunc (CFreqMultipKnb.Value);
    OutputLevel :=    Trunc (63 - COutputLevelKnb.Value);
    LevelScaling :=   Trunc (CLevelScalKnb.Value);
    SustainSound :=   Ord (CSustainSndBtn.State);
    EnvScaling :=     Ord (CEnvScalingBtn.State);
    PitchVibrato :=   Ord (CVibratoBtn.State);
    AmpVibrato :=     Ord (CTremoloBtn.State);
  end;
  with Timb.Modulator do begin
    AttackRate :=     Trunc (15 - MAttackRateKnb.Value);
    DecayRate :=      Trunc (15 - MDecayRateKnb.Value);
    SustainLevel :=   Trunc (15 - MSustainLevKnb.Value);
    ReleaseRate :=    Trunc (15 - MReleaseRateKnb.Value);
    FreqMultiplier := Trunc (MFreqMultipKnb.Value);
    ModFeedback :=    Trunc (MModFeedbackKnb.Value);
    OutputLevel :=    Trunc (63 - MOutputLevelKnb.Value);
    LevelScaling :=   Trunc (MLevelScalKnb.Value);
    SustainSound :=   Ord (MSustainSndBtn.State);
    EnvScaling :=     Ord (MEnvScalingBtn.State);
    PitchVibrato :=   Ord (MVibratoBtn.State);
    AmpVibrato  :=    Ord (MTremoloBtn.State);
  end;
end;    { procedure PanelToTimbre. }


procedure SaveInstrument (OverLoadPlq: boolean);
var
  EntryPlq:        Plaque;
  InstrName:       Str8;
  YesOrNo:         Str1;
  OKToSave:        boolean;
begin
  OKToSave := False;
  HideMouse;
{$IfDef Shareware}
  PanelToTimbre (InstrDef);
  if (OverLoadPlq) then
    EntryPlq.Init (43, 87, 480, 60, 10, Red, LightRed, Black, PlqRound)
  else
    EntryPlq.Init (33, 81, 480, 60, 10, Red, LightRed, Black, PlqRound);
  InstrName := LastInstr;
  with EntryPlq do begin
    EntryPlq.Draw;
    SetColor (White);
    OutTextXY (X + 20, Y + 10,
     'Please enter the filename to save the instrument under:');
    EditFieldGr (InstrName, 8, X + 30, Y + 25, White, Black, UpCaseSet,
     [CR, Esc], TermBy);
    if (TermBy = Esc) then begin
      OutTextXY (X + 20, Y + 42, 'Aborting operation -- file not saved!');
      Delay (1500);
      EntryPlq.Remove;
    end else begin
      if (Exists (InstrName + '.Ins')) then begin
        OutTextXY (X + 20, Y + 42,
         'That file already exists -- replace it (Y or N)?');
        YesOrNo := 'N';
        EditFieldGr (YesOrNo, 1, X + 425, Y + 43, White, Black, ['Y', 'N'],
         ['Y', 'N', CR], TermBy);
        OKToSave := (TermBy = 'Y');
        SetFillStyle (SolidFill, EntryPlq.FaceColor);
        Bar (X + 20, Y + 40, X2 - 10, Y + 54);
      end else
        OKToSave := True;
      if (OKToSave) then begin
        SaveInsFile (InstrDef, CurrMode, CurrVoice, InstrName + '.Ins');
        EntryPlq.Remove;
        NameBx.Wipe;
        OutText (InstrName + '.INS');
        LastInstr := InstrName;
      end else begin
        OutTextXY (X + 20, Y + 42, 'Aborting operation -- file not saved!');
        Delay (1500);
        EntryPlq.Remove;
      end;
    end;
  end;
{$Else}
  if (OverLoadPlq) then
    EntryPlq.Init (43, 87, 515, 110, 10, Red, LightRed, Black, PlqRound)
  else
    EntryPlq.Init (33, 81, 515, 110, 10, Red, LightRed, Black, PlqRound);
  with EntryPlq do begin
    EntryPlq.Draw;
    SetColor (Yellow);
    OutTextXY (X + 20, Y + 10,
     'This is the demonstration version of Synthe/Zap!  The only');
    OutTextXY (X + 20, Y + 20,
     'difference between the demo and the fully functional version');
    OutTextXY (X + 20, Y + 30,
     'is that this demo will not save your instruments.');
    OutTextXY (X + 20, Y + 45,
     'If you would like to have the fully functional shareware');
    OutTextXY (X + 20, Y + 55,
     'version, please send a check or money order for $22.50 to:');
    OutTextXYThk (X + 38, Y + 70, 'J Meeks / Machina Sapiens',
     2, 1, White, Black, DefaultFont);
    OutTextXYThk (X + 38, Y + 81, '3808 Greenbay Rd #3',
     2, 1, White, Black, DefaultFont);
    OutTextXYThk (X + 38, Y + 92, 'Richmond, VA  23234',
     2, 1, White, Black, DefaultFont);
    repeat until (KeyPressed or MsPressed);
    EntryPlq.Remove;
  end;
{$EndIf}
  ClrKeyBuffer;
  ShowMouse;
end;    { procedure SaveInstrument. }


procedure LoadInstrument;
var
  EntryPlq:        Plaque;
  InstrName:       Str8;
  YesOrNo:         Str1;
  Aborted,
  InputOK:         boolean;
begin
  HideMouse;
  EntryPlq.Init (33, 81, 480, 60, 10, Red, LightRed, Black, PlqRound);
  InputOK := False;
  Aborted := False;
  InstrName := LastInstr;

  EntryPlq.Draw;
  with EntryPlq do begin
    PanelToTimbre (InstrDef);
    if ((not CompTimbre (InstrDef, LastLoadDef)) and (not FirstLoad)) then
     begin
      { Current instrument may need to be saved first! }
      SetColor (White);
      OutTextXY (X + 20, Y + 10, 'You have not yet saved the instrument that you''ve');
      OutTextXY (X + 20, Y + 20, 'been working on.');
      OutTextXY (X + 20, Y + 34, 'Do you want to save it now (Y or N)?');
      YesOrNo := 'Y';
      EditFieldGr (YesOrNo, 1, X + 325, Y + 34, White, Black, ['Y', 'N'],
       ['Y', 'N', CR], TermBy);
      if (TermBy <> 'N') then
        SaveInstrument (True);
      EntryPlq.Refresh;
    end;

    repeat
      SetColor (White);
      OutTextXY (X + 20, Y + 10,
       'Please enter the filename of the instrument to load:');
      EditFieldGr (InstrName, 8, X + 30, Y + 25, White, Black, UpCaseSet,
       [CR, Esc], TermBy);

      if (TermBy = Esc) then begin
        OutTextXY (X + 20, Y + 42, 'Aborting operation -- no file loaded!');
        Delay (1500);
        InputOK := True;
        Aborted := True;
      end else
        if (Exists (InstrName + '.Ins')) then begin
          ReadInsFile (InstrDef, CurrMode, CurrVoice, InstrName + '.Ins');
          NameBx.Wipe;
          OutText (InstrName + '.INS');
          LastInstr := InstrName;
          InputOK := True;
        end else begin
          OutTextXY (X + 20, Y + 42,
           'File not found!  Please try another name.');
          Delay (1500);
          EntryPlq.Refresh;
          InputOK := False;
        end;
    until (InputOK or Aborted);
  end;
  EntryPlq.Remove;
  FirstLoad := False;
  ClrKeyBuffer;
  TimbreToPanel;
  LastLoadDef := InstrDef;
  ShowMouse;
end;    { procedure LoadInstrument. }


procedure PlayTestNote (NoteVal: integer);
begin
  HideMouse;
  DrawNote (I, StafX0, StafY0, Yellow);
  ShowMouse;
  PanelToTimbre (InstrDef);
  SetTimbreImm (1, @InstrDef);
  PlayNoteImm (1, NoteVal);
  Delay (250);
  ClrKeyBuffer;
  StopNoteImm (1);
  HideMouse;
  DrawNote (I, StafX0, StafY0, LightCyan);
  ShowMouse;
end;    { procedure PlayTestNote. }


function ClefNum: byte;
begin
  case CurrClef of
    Treble:  ClefNum := 1;
    Bass:    ClefNum := 2;
  end;
end;    { function ClefNum. }


procedure TestNote (I: integer);
begin
  HideMouse;
  DrawNote (I, StafX0, StafY0, Yellow);
  ShowMouse;
  PanelToTimbre (InstrDef);
  SetTimbreImm (1, @InstrDef);
  PlayNoteImm (1, InterpretNote (TestPitch [ClefNum, I]));
  repeat until not (NoteBtn [I].Clicked (MsLeftBtn));
  StopNoteImm (1);
  HideMouse;
  DrawNote (I, StafX0, StafY0, LightCyan);
  ShowMouse;
end;    { procedure TestNote. }



procedure TestChord (UseMouse: boolean);
begin
  HideMouse;
  DrawClef (StafX0, StafY0, LightCyan);
  ShowMouse;
  PanelToTimbre (InstrDef);
  SetTimbreImm (1, @InstrDef);
  SetTimbreImm (2, @InstrDef);
  SetTimbreImm (3, @InstrDef);
  PlayNoteImm (1, InterpretNote (TestPitch [ClefNum, 1]));
  PlayNoteImm (2, InterpretNote (TestPitch [ClefNum, 3]));
  PlayNoteImm (3, InterpretNote (TestPitch [ClefNum, 5]));
  if (UseMouse) then
    repeat
      ReadMouse;
    until (MsNeither)
  else
    Delay (1000);
  StopNoteImm (1);
  StopNoteImm (2);
  StopNoteImm (3);
  HideMouse;
  DrawClef (StafX0, StafY0, Yellow);
  ShowMouse;
end;    { procedure TestChord. }



procedure ConfirmQuit;
var
  ConfirmPlq:      Plaque;
  YesOrNo:         Str1;
begin
  HideMouse;
  ConfirmPlq.Init (370, 81, 235, 45, 10, Red, LightRed, Black, PlqRound);
  with ConfirmPlq do begin
    ConfirmPlq.Draw;
    SetColor (White);
    OutTextXY (X + 20, Y + 10, 'Are you certain you want ');
    OutTextXY (X + 20, Y + 22, 'to quit (Y or N)?');
    YesOrNo := 'N';
    EditFieldGr (YesOrNo, 1, X + 170, Y + 26, White, Black, ['Y', 'N'],
     ['Y', 'N', CR], TermBy);
    EndSession := (TermBy = 'Y');
    ConfirmPlq.Remove;
  end;
  if (not EndSession) then ShowMouse;
end;    { procedure ConfirmQuit. }


procedure Cleanup;
begin
  ClrKeyBuffer;
  StopBtn.Highlight;
  TestChord (False);
  StopBtn.Normal;
  Delay (350);
  CloseGraph;
end;    { procedure Cleanup. }



begin    { program SZ. }
  Setup;
  LoadInstrument;

  ShowMouse;
  repeat
    Key := NullChar;
    if (KeyPressed) then begin
      Key := ScanKey;
      ClrKeyBuffer;
    end;

    CAttackRateKnb.Adjust;
    CDecayRateKnb.Adjust;
    CSustainLevKnb.Adjust;
    CReleaseRateKnb.Adjust;
    CFreqMultipKnb.Adjust;
    COutputLevelKnb.Adjust;
    CLevelScalKnb.Adjust;
    MAttackRateKnb.Adjust;
    MDecayRateKnb.Adjust;
    MSustainLevKnb.Adjust;
    MReleaseRateKnb.Adjust;
    MFreqMultipKnb.Adjust;
    MModFeedbackKnb.Adjust;
    MOutputLevelKnb.Adjust;
    MLevelScalKnb.Adjust;

    CSustainSndBtn.Adjust (MsLeftBtn);
    CEnvScalingBtn.Adjust (MsLeftBtn);
    CVibratoBtn.Adjust (MsLeftBtn);
    CTremoloBtn.Adjust (MsLeftBtn);
    MSustainSndBtn.Adjust (MsLeftBtn);
    MEnvScalingBtn.Adjust (MsLeftBtn);
    MVibratoBtn.Adjust (MsLeftBtn);
    MTremoloBtn.Adjust (MsLeftBtn);

    if ((NameBtn.Clicked (MsLeftBtn)) or (Key = F3Scan)) then
      LoadInstrument;

    if ((NameBtn.Clicked (MsRightBtn)) or (Key = F2Scan)) then
      SaveInstrument (False);

    if (ModeBtn.Clicked (MsLeftBtn)) then
      { Do nothing for the time being. }  ;

    if ((ClefBtn.Clicked (MsLeftBtn)) or (Key = F4Scan)) then begin
      { Switch to the next clef. }
      case CurrClef of
        Treble: CurrClef := Bass;
        Bass:   CurrClef := Treble;
      end;
      HideMouse;
      PutImage (StafX0 + 5, StafY0 + 9, UnderClef^, NormalPut);
      TestChord (True);
      DrawClef (StafX0, StafY0, Yellow);
      ShowMouse;
    end;

    if ((UpCase (Key) >= 'A') and (UpCase (Key) <= 'G')) then
      PlayTestNote (InterpretNote (Key));
    for I := 0 to 12 do
      if (NoteBtn [I].Clicked (MsLeftBtn)) then
        TestNote (I);

    if ((StopBtn.Clicked (MsRightBtn)) or (Key = Esc)) then
      ConfirmQuit;

  until (EndSession);

  Cleanup;
end.    { program SZ. }



{ End file SZ.Pas. }
