TRAMUGENCLASS.PAS

{


**************************************************************
                   TRamugenClass unit
**************************************************************

Contains the following classes:

TRamugenPreset : Preset containing hard limits and probabilities which
                 control how random values are selected;

TRamugenMood and TRamugenMindset : Two very similar objects which just
                 select presets and moods, respectively; they're used for
                 the default A.I. and are present in the TCustRamugen class
                 because they're useful features to have in any similar project.

TCustRamugen : The bare bones of TRamugen, ie. without the engine.
               This is the class you should inherit from if you're making
               object-oriented music applications with the TRamugen component
               and wish to subclass in order to extend functionality.

TRamugen : The actual component which is registered in Delphi. If you're not
           using Ramugen as a Delphi component but still wish to use it, just
           add it to your interface 'uses' clause and call TRamugen.Create(Self)
           to instantiate it. It's a singleton class and lives as long as your
           application so there's no unhandled clean-up to do.

This unit uses objects in TRamugenSmallClasses and TMinMax. The dialog-box provided
with the TWeightingSystem object for its 'InputWeightings' method is in unit WSDialog.

TRamugenSmallClasses includes TMidiArray, TGlobalProbability and TWeightingSystem.

If you do write your own music apps with the components, please drop me a line
because I'd most definitely be interested in learning about your approach to music-making.

mailto : gregskius@_NOSPAMTHANKYOU_tesco.net


**************************************************************
           Copyright 2007 OPEN SOURCE Greg Fox
     http://homepages.tesco.net/gregskius/ramugen.html
**************************************************************



}

unit TRamugenClass;

interface
uses Dialogs, WinTypes, Classes, SysUtils, Controls, StdCtrls, ExtCtrls,
     WSDialog, TMinMaxClass, TSmallRamugenClasses;

type

TRamugenPreset = class(TComponent)
           protected
            FName : string[32];
            FNumNotes,
             FDuration,
             FAmplitude,
             FArticulation,
             FAggregateDelay : TSimpleMinMax; //see separate unit TMinMax
            FTransposeFactor,
             FPitch,
             FMoveInterval,
             FChordIntervals : TExtendedMinMax;
            FMoveHand,
             FChangeLandmark,
             FActivateLandmark,
             FBackTrack,
             FRest,
             FChordLocal : TGlobalProbability; //from TSmallRamugenClasses
            FFilled : Boolean;
            procedure SetPName (T: string);
            function GetPName : String;
           public
            procedure Mu;
            procedure Assign(aPreset : TPersistent); override;
            procedure AssignTo(aPreset : TPersistent); override;
            constructor Create(AOwner : TComponent); override;
            destructor Destroy; override;
           published
            property PresetName : string read GetPName write SetPName;
            property IsFilled : Boolean read FFilled write FFilled;
            property Duration : TSimpleMinMax read FDuration write FDuration;
            property Amplitude : TSimpleMinMax read FAmplitude write FAmplitude;
            property Articulation : TSimpleMinMax read FArticulation write FArticulation;
            property AggregateDelay : TSimpleMinMax read FAggregateDelay write FAggregateDelay;
            property NumNotes : TSimpleMinMax read FNumNotes write FNumNotes;
            property TransposeFactor : TExtendedMinMax read FTransposeFactor write FTransposeFactor;
            property Pitch : TExtendedMinMax read FPitch write FPitch;
            property MoveInterval : TExtendedMinMax read FMoveInterval write FMoveInterval;
            property ChordIntervals : TExtendedMinMax read FChordIntervals write FChordIntervals;
            property MoveHand : TGlobalProbability read FMoveHand write FMoveHand;
            property ChangeLandmark : TGlobalProbability read FChangeLandmark write FChangeLandmark;
            property ActivateLandmark : TGlobalProbability read FActivateLandmark write FActivateLandmark;
            property Backtrack : TGlobalProbability read FBacktrack write FBacktrack;
            property Rest : TGlobalProbability read FRest write FRest;
            property ChordLocal : TGlobalProbability read FChordLocal write FChordLocal;
           end;


TRamugenMood = class(TWeightingSystem)
               protected
                FFilled : Boolean;  //not worth superclassing for one property
               public
                PresetChange : TGlobalProbability;
                procedure Assign(X : TPersistent); override;
                procedure AssignTo(X : TPersistent); override;
                constructor Create(AOwner : TComponent); override;
                destructor Destroy; override;
               published
                property IsFilled : Boolean read FFilled write FFilled;
               end;

TRamugenMindset = class(TWeightingSystem)
               protected
                FFilled : Boolean;
               public
                MoodChange : TGlobalProbability;
                procedure Assign(X : TPersistent); override;
                procedure AssignTo(X : TPersistent); override;
                constructor Create(AOwner : TComponent); override;
                destructor Destroy; override;
               published
                property IsFilled : Boolean read FFilled write FFilled;
               end;

//Main Custom Ramugen Class

   TDevolvee = (dvPresets, dvMoods, dvMindsets);
   TDevolvees = set of TDevolvee;

   TCrescendo = (crescendo, diminuendo);
   TAccelerando = (accelerando, rallentando);

TCustRamugen = class(TComponent)
           private
           protected
            FMIDIDevice : Integer;
            FMIDIVoice : Integer;
            FMIDIChannel : Integer;
            MidiHandle : Integer;
            FMIDIArray : TMidiArray;
            FStoreToArray : Boolean;
            FSkipEngine : Boolean;
            FNote,
             FNote2,
             FNote3,
             FNote4,
             FNote5,
             FNote6,
             FOldNote,
             FOldNote2,
             FOldNote3,
             FOldNote4,
             FOldNote5,
             FOldNote6,
             FDuration,
             FAmplitude,
             FKey,
             FLandmark : Integer;

            APreset : TRamugenPreset;
             AName : string;
             ADuration,
              AAmplitude,
              AArticulation,
              AAggregateDelay,
              ANumNotes : TSimpleMinMax;
             ATransposeFactor,
              APitch,
              AMoveInterval,
              AChordIntervals : TExtendedMinMax;
             AMoveHand,
              AChangeLandmark,
              AActivateLandmark,
              ABackTrack,
              ARest,
              AChordLocal : TGlobalProbability;
            AMood : TRamugenMood;
            AMindset : TRamugenMindset;
            APresetChange,
             AMoodChange,
             AMindsetChange : TGlobalprobability;
            FActivePreset,
             FActiveMood,
             FActiveMindset : Integer;
            FDevolvees : TDevolvees;
             Presets : Array [0..127] of TRamugenPreset;
             Moods : Array [0..127] of TRamugenMood;
             Mindsets : Array [0..127] of TRamugenMindset;
            FCrescendo : TCrescendo;
            FAccelerando : TAccelerando;
          //events
            FOnMemoryPhraseStart : TNotifyEvent;
            FOnMemoryPhraseEnd : TNotifyEvent;
            FOnPlayNoteEnd : TNotifyEvent;
            FOnNoteStart : TNoteStartEvent;
            procedure SetDefaults; virtual;
            function SetNoteSub(T : Integer) : Integer; //does the donkeywork for the next six
            procedure SetNote(T : Integer);
             procedure SetNote2(T : Integer);
             procedure SetNote3(T : Integer);
             procedure SetNote4(T : Integer);
             procedure SetNote5(T : Integer);
             procedure SetNote6(T : Integer);
            procedure SetAmplitude(T : Integer);
            procedure SetDuration(T : Integer);
            procedure SetMIDIDevice(T : Integer); virtual;
            procedure SetMIDIVoice(T : Integer);
            procedure SetMIDIChannel(T : Integer);
            procedure SetActivePreset(T : Integer);
            procedure SetPresetName(T : String);
            procedure SetActiveMood(T : Integer);
            procedure SetActiveMindset(T : Integer);
            procedure SetPFilled (T : Boolean);
            procedure SetMFilled (T : Boolean);
            procedure SetSFilled (T : Boolean);
            procedure SetMemoryPhraseStartEvent (X : TNotifyEvent); //event setters
            procedure SetMemoryPhraseEndEvent (X : TNotifyEvent);
            procedure SetNoteStart (X : TNoteStartEvent);
            procedure SetPlayNoteEnd(X : TNotifyEvent);
            procedure SetPreset(P : Integer; Value : TRamugenPreset);
        {
            The following block defines the recommended interface for
            specific engine implementations for Ramugen type objects.
            BrainTimerDefault is the suggested default event-handler
            for a timer to control mindset/mood/preset-selection.

            MIDITimerDefault is the suggested default event-handler
            for a timer to control the main note-playing engine.

            I recommend that you break this main engine down into sub-methods
            using the "Template Method" Pattern. The recommended steps are provided.

            The published object TRamugen (a TCustRamugen descendant) implements
            this interface.
     }
            procedure BrainTimerDefault(Sender: TObject); virtual; abstract;//mood-changer
            procedure MIDITimerDefault(Sender: TObject); virtual; abstract; //main engine
            //main engine is a template method containing the following sub-methods:
              procedure EngineInitialise(Sender: TObject); virtual; abstract;
              procedure EngineStopNote(Sender: TObject); virtual; abstract;
              procedure EngineCheckPlaying(Sender: TObject); virtual; abstract;
              procedure EngineLandMarkCheck(Sender: TObject); virtual; abstract;
              procedure EnginePitch(Sender: TObject); virtual; abstract;
              procedure EngineLandMark2(Sender: TObject); virtual; abstract;
              procedure EngineKey(Sender: TObject); virtual; abstract;
              procedure EngineAmplitude(Sender: TObject); virtual; abstract;
              procedure EngineDuration(Sender: TObject); virtual; abstract;
              procedure EngineRest(Sender: TObject); virtual; abstract;
              procedure EngineStartNote(Sender: TObject); virtual; abstract;
              procedure EngineFinalise(Sender: TObject); virtual; abstract;
              procedure Start; virtual; abstract; //basic start and stop functionality
              procedure Stop; virtual; abstract;


            //some Getters
            function GetPFilled : Boolean;
            function GetMFilled : Boolean;
            function GetSFilled : Boolean;
            function GetPresetCount : Integer;
            function GetMoodCount : Integer;
            function GetMindsetCount : Integer;
            function GetPreset(P : Integer) : TRamugenPreset;

           public
            procedure SaveToFile(FileName : string = 'prompt'); virtual; abstract;
            procedure SaveToStream(SourceStream : TStream); virtual; abstract;
            procedure LoadFromFile(FileName : string = 'prompt'); virtual; abstract;
            procedure LoadFromStream(SourceStream : TStream); virtual; abstract;
            procedure SaveMemoryToFile(FileName : string = 'prompt'); virtual;
            procedure LoadMemoryFromFile(FileName : string = 'prompt'; Append : boolean = false); virtual;
            procedure PresetClone(PresetNumber : Integer);
             //MIDI-handling methods : see TMIDIArray (these are just an interface to hide the worker object from end-users)
            procedure StartNote(pitch, amplitude : Integer); overload; virtual;
            procedure StartNote(notename : string; octave : Integer = 5; dynamicmarking : string = 'mf'); overload; virtual;
            procedure StopNote(pitch : Integer); overload; virtual;
            procedure StopNote(notename : string; octave : Integer = 5); overload; virtual;
            procedure PlayNote(pitch : Integer; duration : Integer=700; amplitude : Integer=100); overload; virtual;
            procedure PlayNote(notename : string; octave : Integer = 5; duration : Integer = 700; dynamicmarking : string = 'mf'); overload; virtual;
            procedure PlayArray(pitches : array of integer; duration : Integer = 700; amplitude : Integer = 100; TranspositionFactor : Integer = 0; SpeedPercent : Integer = 100); overload; virtual;
            procedure PlayArray(pitches : array of integer; durations : array of integer; amplitude : Integer = 100; TranspositionFactor : Integer = 0; SpeedPercent : Integer = 100); overload; virtual;
            procedure PlayArray(pitches : array of integer; durations : array of integer; amplitudes : array of integer; TranspositionFactor : Integer = 0; SpeedPercent : Integer = 100; AmplitudeLowerLimit : Integer = 40; AmplitudeHigherLimit : Integer = 127); overload; virtual;
            procedure PlayPhrase(transpositionfactor : integer = 0; speedpercent : integer = 100); virtual; //play using data already loaded
            procedure ClearMemory; virtual;
            procedure AddNote(pitch : integer; duration : integer = 700; amplitude : integer = 100); virtual;
            procedure SetMoodWeightings; virtual;
            procedure SetMindsetWeightings; virtual;
            procedure Assign(X : TPersistent); override;
            procedure AssignTo(X : TPersistent); override;
            constructor Create(AOwner : TComponent); override;
            destructor Destroy; override;
            //run-time properties
            property CurrentPitch : Integer read FNote write SetNote;
             property CurrentPitch2 : Integer read FNote2 write SetNote2;
             property CurrentPitch3 : Integer read FNote3 write SetNote3;
             property CurrentPitch4 : Integer read FNote4 write SetNote4;
             property CurrentPitch5 : Integer read FNote5 write SetNote5;
             property CurrentPitch6 : Integer read FNote6 write SetNote6;
            property LastPitch : Integer read FOldNote write FOldNote;
             property LastPitch2 : Integer read FOldNote2 write FOldNote2;
             property LastPitch3 : Integer read FOldNote3 write FOldNote3;
             property LastPitch4 : Integer read FOldNote4 write FOldNote4;
             property LastPitch5 : Integer read FOldNote5 write FOldNote5;
             property LastPitch6 : Integer read FOldNote6 write FOldNote6;
            property CurrentAmplitude : Integer read FAmplitude write SetAmplitude;
            property CurrentDuration : Integer read FDuration write SetDuration;
            property CurrentDynamicDirection : TCrescendo read FCrescendo write FCrescendo;
            property CurrentSpeedDirection : TAccelerando read FAccelerando write FAccelerando;
            property PresetCount : Integer read GetPresetCount;
            property MoodCount : Integer read GetMoodCount;
            property MindsetCount : Integer read GetMindsetCount;
            property DataPresets[P : Integer]: TRamugenPreset read GetPreset write SetPreset;
            property PresetName : string read AName write SetPresetName;
            property ActivePresetFilled : Boolean read GetPFilled write SetPFilled;
            property ActiveMoodFilled : Boolean read GetMFilled write SetMFilled;
            property ActiveMindsetFilled : Boolean read GetSFilled write SetSFilled;


           published
            //events
            property OnNoteStart : TNoteStartEvent read FOnNoteStart write SetNoteStart;
            property OnNoteStop : TNotifyEvent read FOnPlayNoteEnd write SetPlayNoteEnd;
            property OnMemoryPhraseStop : TNotifyEvent read FOnMemoryPhraseEnd write SetMemoryPhraseEndEvent;
            property OnMemoryPhraseStart : TNotifyEvent read FOnMemoryPhraseStart write SetMemoryPhraseStartEvent;

            property Devolvees : TDevolvees read FDevolvees write FDevolvees;

            //the following inset are an interface to the initial preset object
             property Memory : TMidiArray read FMidiArray write FMidiArray;
             property Pitch : TExtendedMinMax read APitch write APitch;
             property TransposeFactor : TExtendedMinMax read ATransposeFactor write ATransposeFactor;
             property MoveInterval : TExtendedMinMax read AMoveInterval write AMoveInterval;
             property ChordIntervals : TExtendedMinMax read AChordIntervals write AChordIntervals;
             property Duration : TSimpleMinMax read ADuration write ADuration;
             property AggregateDelay : TSimpleMinMax read AAggregateDelay write AAggregateDelay;
             property Articulation : TSimpleMinMax read AArticulation write AArticulation;
             property Amplitude : TSimpleMinMax read AAmplitude write AAmplitude;
             property NumNotes : TSimpleMinMax read ANumNotes write ANumNotes;
             property MoveHand : TGlobalProbability read AMoveHand write AMoveHand;
             property ActivateLandmark : TGlobalProbability read AActivateLandmark write AActivateLandmark;
             property ChangeLandmark : TGlobalProbability read AChangeLandmark write AChangeLandmark;
             property BackTrack : TGlobalProbability read ABackTrack write ABackTrack;
             property Rest : TGlobalProbability read ARest write ARest;
             property ChordLocal : TGlobalProbability read AChordLocal write AChordLocal;
            property ActivePreset : Integer read FActivePreset write SetActivePreset;
            property ActiveMood : Integer read FActiveMood write SetActiveMood;
            property ActiveMindset : Integer read FActiveMindset write SetActiveMindset;
            property PresetChange : TGlobalProbability read APresetChange write APresetChange;
            property MoodChange : TGlobalProbability read AMoodChange write AMoodChange;
            property MindsetChange : TGlobalProbability read AMindsetChange write AMindsetChange;
            property MIDIDevice : Integer read FMIDIDevice write SetMIDIDevice;
            property MIDIVoice : Integer read FMIDIVoice write SetMIDIVoice;
            property MIDIChannel : Integer read FMIDIChannel write SetMIDIChannel;
            property RecordOnly : Boolean read FSkipEngine write FSkipEngine;
            property StorePlayback : Boolean read FStoreToArray write FStoreToArray;

           end;

TRamugen = class(TCustRamugen)
           protected
            FPlaying : Boolean;
            MIDITimer,
            BrainTimer : TTimer;
            FMIDIOnTimer,
            FBrainOnTimer : TNotifyEvent;
            FOnStart : TNotifyEvent;
            FOnStop : TNotifyEvent;

            //placeholders allowing over-riding of main engine sub-methods
            FEngineInitialise,
            FEngineStopNote,
            FEngineCheckPlaying,
            FEngineLandMarkCheck,
            FEnginePitch,
            FEngineLandMark2,
            FEngineKey,
            FEngineAmplitude,
            FEngineDuration,
            FEngineRest,
            FEngineStartNote,
            FEngineFinalise : TNotifyEvent;

            //setters
            procedure SetMainEngine (X : TNotifyEvent);
            procedure SetBrainEngine (X : TNotifyEvent);

            procedure SetPlaying (T : Boolean); virtual;
            procedure SetStartPlaying(X : TNotifyEvent);
            procedure SetStopPlaying(X : TNotifyEvent);

            procedure SetBrainInterval(T: Integer);

            procedure MIDITimerDefault(Sender: TObject); override;  //main engine
            procedure BrainTimerDefault(Sender: TObject); override; //mood-changer


            //main engine is a template method containing the following sub-methods:

            procedure EngineStopNote(Sender: TObject); override;
            procedure EngineCheckPlaying(Sender: TObject); override;
            procedure EngineLandMarkCheck(Sender: TObject); override;
            procedure EnginePitch(Sender: TObject); override;
            procedure EngineLandMark2(Sender: TObject); override;
            procedure EngineKey(Sender: TObject); override;
            procedure EngineAmplitude(Sender: TObject); override;
            procedure EngineDuration(Sender: TObject); override;
            procedure EngineStartNote(Sender: TObject); override;

            function GetBrainInterval : Integer;
           public

            procedure SaveToStream(SourceStream : TStream); override;
            procedure SaveToFile(FileName : string = 'prompt'); override;
            procedure LoadFromFile(FileName : string = 'prompt'); override;
            procedure LoadFromStream(SourceStream : TStream); override;
            procedure Mu;
            procedure Start; override;
            procedure Stop; override;
            procedure Assign(X : TPersistent); override;
            procedure AssignTo(X : TPersistent); override;
            constructor Create(AOwner : TComponent); override;
            destructor Destroy; override;
            //run-time properties
            property Playing : Boolean read FPlaying write SetPlaying;
           published
            //events
            property OnEngineStart : TNotifyEvent read FOnStart write SetStartPlaying;
            property OnEngineStop : TNotifyEvent read FOnStop write SetStopPlaying;
               //wholesale override of Ramugen engine (ie. exposes the two Timers basically!)
            property TEO_01_PlayNote : TNotifyEvent read FMIDIOnTimer write SetMainEngine; //nb. TEO rather than MEO so that the OnEvents are at the top in the object inspector!
            property TEO_02_Moods : TNotifyEvent read FBrainOnTimer write SetBrainEngine;
               //partial override of engine - keep some bits, change others (Template Method)
            property PEO_01_Initialise : TNotifyEvent read FEngineInitialise write FEngineInitialise;
            property PEO_02_StopNotes : TNotifyEvent read FEngineStopNote write FEngineStopNote;
            property PEO_03_CheckPlaying : TNotifyEvent read FEngineCheckPlaying write FEngineCheckPlaying;
            property PEO_04_UpdateLandMark : TNotifyEvent read FEngineLandMarkCheck write FEngineLandMarkCheck;
            property PEO_05_PITCH : TNotifyEvent read FEnginePitch write FEnginePitch;
            property PEO_06_Decide_LandMark : TNotifyEvent read FEngineLandMark2 write FEngineLandMark2;
            property PEO_07_KEY : TNotifyEvent read FEngineKey write FEngineKey;
            property PEO_08_AMPLITUDE : TNotifyEvent read FEngineAmplitude write FEngineAmplitude;
            property PEO_09_DURATION : TNotifyEvent read FEngineDuration write FEngineDuration;
            property PEO_10_Decide_REST : TNotifyEvent read FEngineRest write FEngineRest;
            property PEO_11_StartNotes : TNotifyEvent read FEngineStartNote write FEngineStartNote;
            property PEO_12_Finalise : TNotifyEvent read FEngineFinalise write FEngineFinalise;

            //design-time properties
            property BrainInterval : Integer read GetBrainInterval write SetBrainInterval;
          end;

Procedure Register;

implementation

uses MMSystem;



// Constructor & Destructor Pairs

constructor TRamugenMindset.Create;
begin
inherited;
 MoodChange := TGlobalProbability.Create;
end;

destructor TRamugenMindset.Destroy;
begin
 FreeAndNil(MoodChange);
inherited;
end;

constructor TRamugenMood.Create;
begin
inherited;
 PresetChange := TGlobalProbability.Create;
end;

destructor TRamugenMood.Destroy;
begin
 FreeAndNil(PresetChange);
inherited;
end;


constructor TCustRamugen.Create(AOwner: TComponent);
var t: Integer;
begin
 inherited Create(AOwner);
 randomize;
 for t := 0 to 127 do Presets[t] := TRamugenPreset.Create(nil);
 for t := 0 to 127 do Moods[t] := TRamugenMood.Create(nil);
 for t := 0 to 127 do Mindsets[t] := TRamugenMindset.Create(nil);
 FMIDIArray := TMidiArray.Create(nil);
 FSkipEngine := false;
 ActivePreset := 0; //nb. preset-adapter pointers are set by the setter
 ActiveMood := 0;
 ActiveMindset := 0;
 SetDefaults; //Default property settings
end;

destructor TCustRamugen.Destroy;
var t: Integer;
begin
 MidiOutClose(midihandle);
  for t := 0 to 127 do FreeAndNil(Presets[t]);
  for t := 0 to 127 do FreeAndNil(Moods[t]);
  for t := 0 to 127 do FreeAndNil(Mindsets[t]);
 FreeAndNil(FMIDIArray);
inherited;
end;

constructor TRamugen.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
 MidiDevice := 0;
 MIDITimer := TTimer.Create(nil);
  MIDITimer.Enabled := false;
  MIDITimer.OnTimer := MIDITimerDefault;
 BrainTimer := TTimer.Create(nil);
  BrainTimer.Enabled := false;
  BrainTimer.OnTimer := BrainTimerDefault;
  FEngineStopNote := EngineStopNote; //nb. initialise, rest and finalise remain abstract but can be user-assigned
  FEngineCheckPlaying := EngineCheckPlaying;
  FEngineLandMarkCheck := EngineLandMarkCheck;
  FEnginePitch := EnginePitch;
  FEngineLandMark2 := EngineLandMark2;
  FEngineKey := EngineKey;
  FEngineAmplitude := EngineAmplitude;
  FEngineDuration := EngineDuration;
  FEngineStartNote := EngineStartNote;
end;

destructor TRamugen.Destroy;
begin
 FreeAndNil(MIDITimer);
 FreeAndNil(BrainTimer);
inherited;
end;

constructor TRamugenPreset.Create(AOwner : TComponent);
begin
 inherited Create(AOwner);
 FPitch := TExtendedMinMax.Create;
 FDuration := TSimpleMinMax.Create;
  FDuration.HardMax := 30000;
 FAmplitude := TSimpleMinMax.Create;
 FArticulation := TSimpleMinMax.Create;
 FAggregateDelay := TSimpleMinMax.Create;
 FTransposeFactor := TExtendedMinMax.Create;
  FTransposeFactor.HardMax := 30;
  FTransposeFactor.Weightings.Initialize (0);
 FMoveHand := TGlobalProbability.Create;
 FChangeLandmark := TGlobalProbability.Create;
 FActivateLandmark := TGlobalProbability.Create;
 FBackTrack := TGlobalProbability.Create;
 FRest := TGlobalProbability.Create;
 FChordLocal := TGlobalProbability.Create;
 FNumNotes := TSimpleMinMax.Create;
 FMoveInterval := TExtendedMinMax.Create;
  FMoveInterval.HardMax := 24;
  FMoveInterval.Weightings.Initialize (0);
 FChordIntervals := TExtendedMinMax.Create;
  FChordIntervals.HardMax := 40;
  FChordIntervals.Weightings.Initialize (0);
  FChordIntervals.HardMax := 12;
  FChordIntervals.Weightings.Initialize (1);
end;

destructor TRamugenPreset.Destroy;
begin
 FreeAndNil(FPitch);
 FreeAndNil(FDuration);
 FreeAndNil(FAmplitude);
 FreeAndNil(FArticulation);
 FreeAndNil(FAggregateDelay);
 FreeAndNil(FTransposeFactor);
 FreeAndNil(FMoveHand);
 FreeAndNil(FChangeLandmark);
 FreeAndNil(FActivateLandmark);
 FreeAndNil(FBackTrack);
 FreeAndNil(FRest);
 FreeAndNil(FChordLocal);
 FreeAndNil(FNumNotes);
 FreeAndNil(FMoveInterval);
 FreeAndNil(FChordIntervals);
inherited;
end;


//Save and Load methods

procedure TCustRamugen.SaveMemoryToFile(FileName : string = 'prompt'); //delegate
begin
 Memory.SaveToFile(FileName);
end;

procedure TCustRamugen.LoadMemoryFromFile(FileName : string = 'prompt'; Append : Boolean = false);
begin
 Memory.LoadFromFile(FileName,Append);
end;



procedure TRamugen.SaveToFile(FileName : string = 'prompt');
var
    str1 : TFileStream;
    savedialog1 : TSaveDialog;
    t : Integer;
begin
 if FileExists(FileName) then
   if MessageDlg('Do you want to over-write this file?',mtConfirmation,[MbYes, MbNo],0) = mrNo
   then FileName := 'prompt';
 if (comparestr(FileName, 'prompt') = 0) then
 begin
   savedialog1 := TSaveDialog.Create(nil);
   try
     savedialog1.Filter:='Ramugen Settings|*.rmg';
     savedialog1.DefaultExt := 'rmg';
     savedialog1.Options := [ofOverwritePrompt];
     if savedialog1.execute then FileName := savedialog1.filename;
   finally
     savedialog1.free;
   end;
 end;

 str1 := TFileStream.Create(FileName, fmOpenWrite or fmCreate);
 try
   str1.Seek(0,soFromBeginning);
   for t := 0 to 127 do
   begin
     str1.WriteComponent(Presets[t]);
     str1.WriteComponent(Moods[t]);
     str1.WriteComponent(Mindsets[t]);
   end;
 finally
   str1.free;
 end;
end;

procedure TRamugen.LoadFromFile(FileName : string = 'prompt');
var
   str1 : TFileStream;
   opendialog1 : TOpenDialog;
   t : Integer;
begin
  if CompareStr(Filename, 'prompt') = 0 then
    begin
      opendialog1 := TOpenDialog.Create(nil);
      try
        opendialog1.Options := [ofFileMustExist];
        opendialog1.filter:='Ramugen Settings File|*.rmg';
        if opendialog1.execute then Filename := opendialog1.filename;
      finally
        opendialog1.Free;
      end;
    end;
  if (fileexists(filename)) then
      begin
        str1 := TFileStream.Create(FileName, fmOpenRead);
        try
          str1.Seek(0,soFromBeginning);
          for t := 0 to 127 do
          begin
            Presets[t] := str1.readcomponent(Presets[t]) as TRamugenPreset;
            Moods[t] := str1.readcomponent(Moods[t]) as TRamugenMood;
            Mindsets[t] := str1.readcomponent(Mindsets[t]) as TRamugenMindset;
          end;
        finally
          str1.free;
        end;
      end;
end;


procedure TRamugen.LoadFromStream(SourceStream : TStream);
var t : Integer;
begin
  SourceStream.Seek(0,soFromBeginning);
          for t := 0 to 127 do
          begin
            Presets[t] := SourceStream.readcomponent(Presets[t]) as TRamugenPreset;
            Moods[t] := SourceStream.readcomponent(Moods[t]) as TRamugenMood;
            Mindsets[t] := SourceStream.readcomponent(Mindsets[t]) as TRamugenMindset;
          end;
end;


procedure TRamugen.SaveToStream(SourceStream : TStream);
var t : Integer;
begin
   sourcestream.Seek(0,soFromBeginning);
   for t := 0 to 127 do
   begin
     sourcestream.WriteComponent(Presets[t]);
     sourcestream.WriteComponent(Moods[t]);
     sourcestream.WriteComponent(Mindsets[t]);
   end;
end;



procedure TCustRamugen.PresetClone(PresetNumber : Integer);
begin
  If PresetNumber > (PresetCount - 1) then exit;
  APreset.Assign(Presets[PresetNumber]);
end;



// Mu methods

procedure TRamugen.Mu;
var x : Integer;
begin
 for x := 0 to 127 do begin
  ActivePreset := x;
  APreset.Backtrack.Probability := random(1);
  APreset.Rest.Probability := random(1);
  APreset.Mu;
                      end;
 for x := 0 to 127 do begin
  ActiveMood := x;
  AMood.Mu;
                      end;
 for x := 0 to 127 do begin
  ActiveMindset := x;
  AMindset.Mu;
                      end;
end;


procedure TRamugenPreset.Mu;
begin
 Pitch.HardMin := 26; Pitch.HardMax := 104;
 Pitch.SetRange(Pitch.Choose, Pitch.Choose);
 Pitch.Mu;
 Duration.HardMin := 50; Duration.HardMax := 4500;
 Duration.Min := 50; Duration.Max := 4500;
 Duration.SetRange(Duration.Choose, Duration.Choose);
 Amplitude.HardMin := 45; Amplitude.HardMax := 127;
 Amplitude.SetRange(Amplitude.Choose, Amplitude.Choose);
 NumNotes.SetRange(1,3);
end;


//Assign methods

procedure TRamugenPreset.Assign(aPreset : TPersistent);
begin
  if aPreset is TRamugenPreset then begin
                                     PresetName := TRamugenPreset(aPreset).PresetName;
                                     ChordLocal.Assign(TRamugenPreset(aPreset).ChordLocal);
                                     Rest.Assign(TRamugenPreset(aPreset).Rest);
                                     BackTrack.Assign(TRamugenPreset(aPreset).BackTrack);
                                     ActivateLandmark.Assign(TRamugenPreset(aPreset).ActivateLandmark);
                                     ChangeLandmark.Assign(TRamugenPreset(aPreset).ChangeLandmark);
                                     MoveHand.Assign(TRamugenPreset(aPreset).MoveHand);
                                     ChordIntervals.Assign(TRamugenPreset(aPreset).ChordIntervals);
                                     MoveInterval.Assign(TRamugenPreset(aPreset).MoveInterval);
                                     Pitch.Assign(TRamugenPreset(aPreset).Pitch);
                                     TransposeFactor.Assign(TRamugenPreset(aPreset).TransposeFactor);
                                     NumNotes.Assign(TRamugenPreset(aPreset).NumNotes);
                                     AggregateDelay.Assign(TRamugenPreset(aPreset).AggregateDelay);
                                     Articulation.Assign(TRamugenPreset(aPreset).Articulation);
                                     Amplitude.Assign(TRamugenPreset(aPreset).Amplitude);
                                     Duration.Assign(TRamugenPreset(aPreset).Duration);
                                     IsFilled := true;
                                    end
                               else
                                    inherited Assign(aPreset);
end;

procedure TRamugenPreset.AssignTo(aPreset : TPersistent);
begin
  if aPreset is TRamugenPreset then begin
                                     TRamugenPreset(aPreset).PresetName := PresetName;
                                     ChordLocal.AssignTo(TRamugenPreset(aPreset).ChordLocal);
                                     Rest.AssignTo(TRamugenPreset(aPreset).Rest);
                                     BackTrack.AssignTo(TRamugenPreset(aPreset).BackTrack);
                                     ActivateLandmark.AssignTo(TRamugenPreset(aPreset).ActivateLandmark);
                                     ChangeLandmark.AssignTo(TRamugenPreset(aPreset).ChangeLandmark);
                                     MoveHand.AssignTo(TRamugenPreset(aPreset).MoveHand);
                                     ChordIntervals.AssignTo(TRamugenPreset(aPreset).ChordIntervals);
                                     MoveInterval.AssignTo(TRamugenPreset(aPreset).MoveInterval);
                                     Pitch.AssignTo(TRamugenPreset(aPreset).Pitch);
                                     TransposeFactor.AssignTo(TRamugenPreset(aPreset).TransposeFactor);
                                     NumNotes.AssignTo(TRamugenPreset(aPreset).NumNotes);
                                     AggregateDelay.AssignTo(TRamugenPreset(aPreset).AggregateDelay);
                                     Articulation.AssignTo(TRamugenPreset(aPreset).Articulation);
                                     Amplitude.AssignTo(TRamugenPreset(aPreset).Amplitude);
                                     Duration.AssignTo(TRamugenPreset(aPreset).Duration);
                                     TRamugenPreset(aPreset).IsFilled := true;
                                    end
                               else
                                    inherited AssignTo(aPreset);
end;

procedure TRamugen.Assign(X: TPersistent);
begin
  inherited Assign(X); //TCustRamugen.Assign
  if X is TRamugen then
    begin
      Playing := TRamugen(X).Playing;
      MIDITimer.Interval := TRamugen(X).MIDITimer.Interval;
      BrainTimer.Interval := TRamugen(X).BrainTimer.Interval;
    end;
end;

procedure TRamugen.AssignTo(X: TPersistent);
begin
  inherited AssignTo(X); //TCustRamugen.AssignTo
  if X is TRamugen then
    begin
      TRamugen(X).Playing := Playing;
      TRamugen(X).MIDITimer.Interval := MIDITimer.Interval;
      TRamugen(X).BrainTimer.Interval := BrainTimer.Interval;
    end;
end;


procedure TCustRamugen.Assign(X: TPersistent);
var t : Integer;
begin
 if X is TCustRamugen then
     begin
       FLandmark := TCustRamugen(X).FLandmark;
       FMIDIArray.Assign(TCustRamugen(X).FMIDIArray);
       MIDIDevice := TCustRamugen(X).MIDIDevice;
       MIDIVoice := TCustRamugen(X).MIDIVoice;
       MIDIChannel := TCustRamugen(X).MIDIChannel;
       FOnNoteStart := TCustRamugen(X).FOnNoteStart;
       FOnPlayNoteEnd := TCustRamugen(X).FOnPlayNoteEnd;
       FOnMemoryPhraseStart := TCustRamugen(X).FOnMemoryPhraseStart;
       FOnMemoryPhraseEnd := TCustRamugen(X).FOnMemoryPhraseEnd;
       FCrescendo := TCustRamugen(X).FCrescendo;
       FAccelerando := TCustRamugen(X).FAccelerando;
       for t := 0 to 127 do
         begin
           Presets[t].Assign(TCustRamugen(X).Presets[t]);
           Moods[t].Assign(TCustRamugen(X).Moods[t]);
           Mindsets[t].Assign(TCustRamugen(X).Mindsets[t]);
         end;
       FDevolvees := TCustRamugen(X).FDevolvees;
       ActiveMindset := TCustRamugen(X).ActiveMindset;
       ActiveMood := TCustRamugen(X).ActiveMood;
       ActivePreset := TCustRamugen(X).ActivePreset;  //no need to do the A-type pointers
     end              else
   inherited Assign(X);
end;

procedure TCustRamugen.AssignTo(X: TPersistent);
var t : Integer;
begin
 if X is TCustRamugen then
     begin
       TCustRamugen(X).FLandmark := FLandmark;
       FMIDIArray.AssignTo(TCustRamugen(X).FMIDIArray);
       TCustRamugen(X).MIDIDevice := MIDIDevice;
       TCustRamugen(X).MIDIVoice := MIDIVoice;
       TCustRamugen(X).MIDIChannel := MIDIChannel;
       TCustRamugen(X).FCrescendo := FCrescendo;
       TCustRamugen(X).FAccelerando := FAccelerando;
       for t := 0 to 127 do
         begin
           Presets[t].AssignTo(TCustRamugen(X).Presets[t]);
           Moods[t].AssignTo(TCustRamugen(X).Moods[t]);
           Mindsets[t].AssignTo(TCustRamugen(X).Mindsets[t]);
         end;
       TCustRamugen(X).FDevolvees := FDevolvees;
       TCustRamugen(X).ActiveMindset := ActiveMindset;
       TCustRamugen(X).ActiveMood := ActiveMood;
       TCustRamugen(X).ActivePreset := ActivePreset;  //no need to do the A-type pointers
     end              else
   inherited Assign(X);
end;

procedure TRamugenMood.Assign(X: TPersistent);
begin
 if X is TRamugenMood then
   begin
     FFilled := TRamugenMood(X).FFilled;
     PresetChange.Assign(TRamugenMood(X).PresetChange);
   end else inherited Assign(X);
end;

procedure TRamugenMood.AssignTo(X: TPersistent);
begin
 if X is TRamugenMood then
   begin
     TRamugenMood(X).FFilled := FFilled;
     PresetChange.AssignTo(TRamugenMood(X).PresetChange);
   end else inherited AssignTo(X);
end;


procedure TRamugenMindset.Assign(X: TPersistent);
begin
 if X is TRamugenMindset then
   begin
     FFilled := TRamugenMindset(X).FFilled;
     MoodChange.Assign(TRamugenMindset(X).MoodChange);
   end else inherited Assign(X);
end;

procedure TRamugenMindset.AssignTo(X: TPersistent);
begin
 if X is TRamugenMindset then
   begin
     TRamugenMindset(X).FFilled := FFilled;
     MoodChange.AssignTo(TRamugenMindset(X).MoodChange);
   end else inherited AssignTo(X);
end;

// Delegation Setters

procedure TCustRamugen.SetNoteStart(X : TNoteStartEvent);
begin
 FOnNoteStart := X;
 Memory.OnNoteStart := X;  //automatically mirror the event in the worker object
end;

procedure TCustRamugen.SetPlayNoteEnd(X : TNotifyEvent);
begin
 FOnPlayNoteEnd := X;
 Memory.OnNoteStop := X;
end;

procedure TCustRamugen.SetMemoryPhraseStartEvent (X : TNotifyEvent);
begin
  FOnMemoryPhraseStart := X;
  Memory.OnPhraseStart := X;
end;

procedure TCustRamugen.SetMemoryPhraseEndEvent (X : TNotifyEvent);
begin
  FOnMemoryPhraseEnd := X;
  Memory.OnPhraseStop := X;
end;

procedure TRamugen.SetMainEngine(X : TNotifyEvent);
begin
  FMIDIOnTimer := X; //just stores it for the object inspector
  MIDITimer.OnTimer := X; //delegate to hidden worker object thus hiding its default handler
end;

procedure TRamugen.SetBrainEngine(X : TNotifyEvent);
begin
  FBrainOnTimer := X;
  BrainTimer.OnTimer := X;
end;

procedure TCustRamugen.SetActivePreset(T : Integer);
begin
 if T < 0 then T := 0;
 if T > 127 then T := 127;
 FActivePreset := T;
 APreset := Presets[T];
   //the following lines are an adaptor so that the Active Preset's
   //properties are editable directly via T{Cust}Ramugen
  AName := APreset.PresetName;
  APitch := APreset.Pitch;
  ADuration := APreset.Duration;
  AAmplitude := APreset.Amplitude;
  AArticulation := APreset.Articulation;
  AAggregateDelay := APreset.AggregateDelay;
  ANumNotes := APreset.NumNotes;
  ATransposeFactor := APreset.TransposeFactor;
  AMoveInterval := APreset.MoveInterval;
  AChordIntervals := APreset.ChordIntervals;
  AMoveHand := APreset.MoveHand;
  AChangeLandmark := APreset.ChangeLandmark;
  AActivateLandmark := APreset.ActivateLandmark;
  ABackTrack := APreset.BackTrack;
  ARest := APreset.Rest;
  AChordLocal := APreset.ChordLocal;
end;

procedure TCustRamugen.SetActiveMood(T : Integer);
begin
 if T < 0 then T := 0;
 if T > 127 then T := 127;
 FActiveMood := T;
 AMood := Moods[T];
  APresetChange := AMood.PresetChange;
end;

procedure TCustRamugen.SetActiveMindset(T : Integer);
begin
 if T < 0 then T := 0;
 if T > 127 then T := 127;
 FActiveMindset := T;
 AMindset := Mindsets[T];
  AMoodChange := AMindset.MoodChange;
end;

procedure TCustRamugen.SetPFilled(T : Boolean);
begin
 APreset.FFilled := T;
end;

procedure TCustRamugen.SetMFilled(T : Boolean);
begin
 AMood.FFilled := T;
end;

procedure TCustRamugen.SetSFilled(T : Boolean);
begin
 AMindset.FFilled := T;
end;

procedure TCustRamugen.SetPresetName(T : String);
begin
 APreset.FName := T;
end;


procedure TRamugen.SetBrainInterval(T: Integer);
begin
 BrainTimer.Interval := T;
end;


function TRamugen.GetBrainInterval : Integer;
begin
 Result := BrainTimer.Interval;
end;

//Property Setters

    //TCustRamugen direct data access array property setters
procedure TCustRamugen.SetPreset(P : Integer; Value : TRamugenPreset);
begin
 Presets[P] := Value;
end;


procedure TRamugen.SetStartPlaying(X : TNotifyEvent);
begin
 FOnStart := X;
end;

procedure TRamugen.SetStopPlaying(X : TNotifyEvent);
begin
 FOnStop := X;
end;

procedure TRamugenPreset.SetPName(T : String);
begin
 FName := T;
end;

function TCustRamugen.SetNoteSub(T : Integer) : Integer;
begin
 if T < APreset.Pitch.Min then T := APreset.Pitch.Min;
 if T > APreset.Pitch.Max then T := APreset.Pitch.Max;
 Result := T;
end;

procedure TCustRamugen.SetNote(T : Integer);
begin
 FNote := SetNoteSub(T);
end;

procedure TCustRamugen.SetNote2(T : Integer);
begin
 FNote2 := SetNoteSub(T);
end;

procedure TCustRamugen.SetNote3(T : Integer);
begin
 FNote3 := SetNoteSub(T);
end;

procedure TCustRamugen.SetNote4(T : Integer);
begin
 FNote4 := SetNoteSub(T);
end;

procedure TCustRamugen.SetNote5(T : Integer);
begin
 FNote5 := SetNoteSub(T);
end;

procedure TCustRamugen.SetNote6(T : Integer);
begin
 FNote6 := SetNoteSub(T);
end;



procedure TCustRamugen.SetAmplitude(T : Integer);
begin
 if T < APreset.Amplitude.Min then T := APreset.Amplitude.Min;
 if T > APreset.Amplitude.Max then T := APreset.Amplitude.Max;
 FAmplitude := T;
end;

procedure TCustRamugen.SetDuration(T : Integer);
begin
 if T < APreset.Duration.Min then T := APreset.Duration.Min;
 if T > APreset.Duration.Max then T := APreset.Duration.Max;
 FDuration := T;
end;

procedure TCustRamugen.SetMIDIDevice(T : Integer);
begin
 if T < -1 then T := -1;
 if T > 8 then T := -1;  //Ideally, this should be calculated; 8 is correct on MY computer
 FMIDIDevice := T;
 MidiOutClose(midihandle);
 MidiOutOpen(addr(MidiHandle), MidiDevice, 0, 0, 0);
 FMIDIArray.Handle := MidiHandle;
end;

procedure TCustRamugen.SetMIDIVoice(T : Integer);
begin
 if T < 0 then T := 0;
 if T > 127 then T := 127;
 FMIDIVoice := T;
 MidiOutShortMsg(MidiHandle, $C0 + FMIDIChannel + FMIDIVoice shl 8 + 0 shl 16);
end;

procedure TCustRamugen.SetMIDIChannel(T : Integer);
begin
 if T < 0 then T := 0;
 if T > 127 then T := 127;
 FMIDIChannel := T;
 FMIDIArray.Channel := FMIDIChannel;
end;

procedure TRamugen.SetPlaying(T : Boolean);
begin
 FPlaying := T;
 if FPlaying then begin
                         MIDITimer.Enabled := true;
                         if devolvees <> [] then BrainTimer.Enabled := true;
                  end
             else begin
                         MIDITimer.Enabled := true;
                         BrainTimer.Enabled := false;
                         Memory.Halt;
                  end;
       end;

procedure TCustRamugen.SetMoodWeightings;
begin
 AMood.Title := 'Mood #' + inttostr(ActiveMood);
 AMood.Description :=
 'Assign a value to the various stored presets so that, when this mood prevails, they determine the likelihood of each preset being used at any given time.';
 AMood.InputWeightings(0, PresetCount -1);
end;

procedure TCustRamugen.SetMindsetWeightings;
begin
 AMindset.Title := 'Mindset #' + inttostr(ActiveMindset);
 AMindset.Description :=
 'Assign a value to the various stored presets so that, when this mindset prevails, they determine the likelihood of each mood being used at any given time.';
 AMindset.InputWeightings(0, MoodCount -1);
end;


//user interface ease-of-use Setters (methods make more sense here)

procedure TRamugen.Start;
begin
 playing := true;
 if Assigned(FOnStart) then FOnStart(Self); //optional custom events
end;

procedure TRamugen.Stop;
begin
 playing := false;
 if Assigned(FOnStop) then FOnStop(Self);
end;



// Getters

function TCustRamugen.GetPreset(P: Integer) : TRamugenPreset;
begin
 Result := Presets[P];
end;

function TCustRamugen.GetPresetCount : Integer;
var t: Integer;
begin
 Result := 0;
 for t := 0 to 127 do begin
     if Presets[t].FFilled then inc(Result) else break;
                      end;
end;

function TCustRamugen.GetMoodCount : Integer;
var t: Integer;
begin
 Result := 0;
 for t := 0 to 127 do begin
     if Moods[t].FFilled then
              begin inc(Result);
              end
                         else break;
                      end;
end;

function TCustRamugen.GetMindsetCount : Integer;
var t: Integer;
begin
 Result := 0;
 for t := 0 to 127 do begin
     if Mindsets[t].FFilled then inc(Result) else break;
                      end;
end;


function TCustRamugen.GetPFilled : Boolean;
begin
 Result := APreset.FFilled;
end;

function TCustRamugen.GetMFilled : Boolean;
begin
 Result := AMood.FFilled;
end;

function TCustRamugen.GetSFilled : Boolean;
begin
 Result := AMindset.FFilled;
end;


function TRamugenPreset.GetPName : String;
begin
 Result := FName;
end;

//Misc. internal

procedure TCustRamugen.SetDefaults;
var t : integer;
begin
  for t := 127 downto 0 do
  begin
    ActivePreset := t;
    Duration.SetRange(100,1000);
    Duration.UnitSize := 100;
    AggregateDelay.Max := 0;
    Amplitude.SetRange(80,110);
    Pitch.SetRange(40,80);
    Pitch.Title := 'Pitch';
    Pitch.Change.Probability := 1;
    Articulation.HardMax := 30000;
    Articulation.SetRange(5000,5000);
    NumNotes.HardMax := 6;
    NumNotes.HardMin := 1;
    NumNotes.SetRange(1,1);
    PresetName := 'Untitled';
    TransposeFactor.Max := 0;
    TransposeFactor.Title := 'Transposition Factor';
    TransposeFactor.Description := 'These values are used to simulate "key changes".';
    MoveInterval.Title := 'Hand Emulation';
  end;
end;


// Low-level MIDI encapsulation for end-users - functionality provided by TMidiArray worker object

procedure TCustRamugen.StartNote(pitch, amplitude : Integer);
begin
  FMIDIArray.StartNote(pitch, amplitude);
end;


procedure TCustRamugen.StartNote(NoteName : string; Octave : Integer = 5; dynamicmarking : string = 'mf');
begin
 FMIDIArray.StartNote(NoteName, Octave, DynamicMarking);
end;


procedure TCustRamugen.StopNote(pitch : Integer);
begin
 FMIDIArray.StopNote(Pitch);
end;

procedure TCustRamugen.StopNote(NoteName : string; Octave : Integer = 5);
begin
  FMIDIArray.StopNote(NoteName, Octave);
end;


procedure TCustRamugen.PlayNote(Pitch: Integer; Duration: Integer=700; Amplitude: Integer=100);
begin
  FMIDIArray.PlayNote(Pitch, Duration, Amplitude);
  if FStoreToArray then FMIDIArray.AddNote(pitch,duration,amplitude);
end;

procedure TCustRamugen.PlayNote(NoteName : string; Octave : Integer = 5; duration : Integer = 700; dynamicmarking : string = 'mf');
var n, v : Integer;
begin
 n := NoteNametoNoteValue(NoteName, Octave);
 v := 80; //'mf'
 if dynamicmarking = 'pppp' then v := 25;
 if dynamicmarking = 'ppp' then v := 37;
 if dynamicmarking = 'pp' then v := 49;
 if dynamicmarking = 'p' then v := 60;
 if dynamicmarking = 'mp' then v := 70;
 if dynamicmarking = 'f' then v := 90;
 if dynamicmarking = 'ff' then v := 103;
 if dynamicmarking = 'fff' then v := 115;
 if dynamicmarking = 'ffff' then v := 127;
 if n = 255 then begin v := 0; n := 1; end;
 if FStoreToArray then FMIDIArray.AddNote(n, Duration, v);
 FMIDIArray.PlayNote(n, Duration, v);
end;


procedure TCustRamugen.ClearMemory;
begin
 FMIDIArray.Clear;
end;

procedure TCustRamugen.AddNote(pitch : integer; duration : integer = 700; amplitude : integer = 100);
begin
 FMIDIArray.AddNote(pitch,duration,amplitude);
end;

procedure TCustRamugen.PlayPhrase (TranspositionFactor : Integer = 0; SpeedPercent : Integer = 100);
begin
 FMIDIArray.PlayPhrase(TranspositionFactor,SpeedPercent);
end;

procedure TCustRamugen.PlayArray(Pitches : array of Integer; Duration : Integer = 700; Amplitude : Integer = 100; TranspositionFactor : Integer = 0; SpeedPercent : Integer = 100);
begin
 FMIDIArray.Assign(Pitches, Duration, Amplitude);
 PlayPhrase(TranspositionFactor, SpeedPercent);
end;

procedure TCustRamugen.PlayArray(Pitches : array of Integer; Durations : array of Integer; Amplitude : Integer = 100; TranspositionFactor : Integer = 0; SpeedPercent : Integer = 100);
begin
 FMIDIArray.Assign(Pitches,Durations);
 PlayPhrase(TranspositionFactor, SpeedPercent);
end;

procedure TCustRamugen.PlayArray(Pitches : array of Integer; Durations : array of Integer; Amplitudes : array of integer; TranspositionFactor : Integer = 0; SpeedPercent : Integer = 100; AmplitudeLowerLimit : Integer = 40; AmplitudeHigherLimit : Integer = 127);
begin
 FMIDIArray.Assign(Pitches, Durations, Amplitudes);
 PlayPhrase(TranspositionFactor, SpeedPercent);
end;


//Default event-handler for picking presets, etc.
//This can be over-ridden by assigning the TEO_02_Brain event

procedure TRamugen.BrainTimerDefault(Sender : TObject);
begin
  BrainTimer.Enabled := false;
  if Devolvees = [] then exit;
  if dvPresets in Devolvees then begin  //pick a preset
          if PresetChange.IsRequired then
             ActivePreset := AMood.Choose(0,PresetCount - 1);
                                 end;
  if dvMoods in Devolvees then begin    //pick a mood
          if MoodChange.IsRequired then
             ActiveMood := AMindset.Choose(0,MoodCount - 1);
                               end;
{  if dvMindsets in Devolvees then begin //pick a mindset
          if MindsetChange.IsRequired then

 //need a mechanism for picking mindsets!!! another TWeightingSystem
//             ActiveMindset := trunc(random(MindsetCount) - 1);
                                  end;
 }
  BrainTimer.Interval := 1000 + trunc(random(5000));
  BrainTimer.Enabled := true;
end;




{****************************************************************

                       RAMUGEN      ENGINE

 ****************************************************************

    This is the default engine for the TRamugen class.
    It's modelled on Ramugen V.1, which was the non-OOP version
    of Ramugen. If you don't want to use this type of engine,
    use TCustRamugen instead of TRamugen for your application.

    It's also possible to over-ride the TRamugen component's engine
    partially or completely.
    To over-ride completely, just assign your own object method to
    TEO_01_PlayNote event. ("Total Engine Override")
    To over-ride only parts of the engine, use the various PEO events.
    ("Partial Engine Override")

    The implementation of the Ramugen Engine begins with a "Template method"
    which delegates the action to the various engine events.
}


procedure TRamugen.MIDITimerDefault(Sender : TObject); //"Template method" pattern
begin
  if Assigned(FEngineInitialise) then FEngineInitialise(Self);
  if Assigned(FEngineStopNote) then FEngineStopNote(Self);
  if Assigned(FEngineCheckPlaying) then FEngineCheckPlaying(Self);
  if Assigned(FEngineLandMarkCheck) then FEngineLandMarkCheck(Self);
  if Assigned(FEnginePitch) then FEnginePitch(Self);
  if Assigned(FEngineLandMark2) then FEngineLandMark2(Self);
  if Assigned(FEngineKey) then FEngineKey(Self);
  if Assigned(FEngineAmplitude) then FEngineAmplitude(Self);
  if Assigned(FEngineDuration) then FEngineDuration(Self);
  if Assigned(FEngineRest) then FEngineRest(Self);
  if Assigned(FEngineStartNote) then FEngineStartNote(Self);
  if Assigned(FEngineFinalise) then FEngineFinalise(Self);
end;

//main engine sub-methods

procedure TRamugen.EngineStopNote(Sender: TObject);
begin
  StopNote(FNote);
  StopNote(FNote2);
  StopNote(FNote3);
  StopNote(FNote4);
  StopNote(FNote5);
  StopNote(FNote6);
end;

procedure TRamugen.EngineCheckPlaying(Sender: TObject);
begin
  MIDITimer.Enabled := false;
  if not playing then exit;
end;

procedure TRamugen.EngineLandMarkCheck(Sender: TObject);
begin
  if ChangeLandmark.IsRequired then FLandMark := FNote;
end;

procedure TRamugen.EnginePitch(Sender: TObject);
var zA : Integer;
begin
 if not(BackTrack.IsRequired) then begin
      LastPitch := FNote; //faster with direct access
      LastPitch2 := FNote2;
      LastPitch3 := FNote3;
      LastPitch4 := FNote4;
      LastPitch5 := FNote5;
      LastPitch6 := FNote6;
        //pick a new pitch
      if Pitch.Change.IsRequired then //global jump
         CurrentPitch := pitch.choose
                                 else
         begin //Move (local)
           if MoveInterval.Weighted then
             begin  //Move by interval
               if MoveInterval.MovingUp then
                 CurrentPitch := CurrentPitch + MoveInterval.Choose
                                        else
                 CurrentPitch := CurrentPitch - MoveInterval.Choose;
             end                    else
             begin //Move by local jump
               CurrentPitch := pitch.choose(CurrentPitch - MoveInterval.Max, CurrentPitch + MoveInterval.Max);
             end;
         end;
              //Chord - up to 5 extra notes on top of the main note

     if chordlocal.IsRequired then
       begin  //If chord is made up of intervals, do this:
         FNote2 := FNote + ChordIntervals.Choose;
         while FNote2 > Pitch.Max do
           FNote2 := FNote2 - 12;
         FNote3 := FNote2 + ChordIntervals.Choose;
         while FNote3 > Pitch.Max do
           FNote3 := FNote3 - 12;
         FNote4 := Fnote3 + ChordIntervals.Choose;
         while FNote4 > Pitch.Max do
           FNote4 := FNote4 - 12;
         FNote5 := FNote4 + ChordIntervals.Choose;
         while FNote5 > Pitch.Max do
           FNote5 := FNote5 - 12;
         FNote6 := FNote5 + ChordIntervals.Choose;
         while FNote6 > Pitch.Max do
           FNote6 := FNote6 - 12;
         while FNote2 > Pitch.Min do
           FNote2 := FNote2 + 12;
         while FNote3 > Pitch.Min do
           FNote3 := FNote3 + 12;
         while FNote4 > Pitch.Min do
           FNote4 := FNote4 + 12;
         while FNote5 > Pitch.Min do
           FNote5 := FNote5 + 12;
         while FNote6 > Pitch.Min do
           FNote6 := FNote6 + 12;
       end else //If chord is made up of pitches rather than intervals do this:
       begin
         CurrentPitch2 := pitch.choose;
         CurrentPitch3 := pitch.choose;
         CurrentPitch4 := pitch.choose;
         CurrentPitch5 := pitch.choose;
         CurrentPitch6 := pitch.choose;
       end;
                                   end
                              else
       begin   //swap with last note(s)
         zA := FNote;
         FNote := LastPitch;
         LastPitch := zA;
         zA := FNote2;
         CurrentPitch2 := LastPitch2;  //use the CurrentPitch property to avoid having
                                       //to range-check the values
         LastPitch2 := zA;
         zA := FNote3;
         CurrentPitch3 := LastPitch3;
         LastPitch3 := zA;
         zA := FNote4;
         CurrentPitch4 := LastPitch4;
         LastPitch4 := zA;
         zA := FNote5;
         CurrentPitch5 := LastPitch5;
         LastPitch5 := zA;
         zA := FNote6;
         CurrentPitch6 := LastPitch6;
         LastPitch6 := zA;
      end;
end; //pitch engine

procedure TRamugen.EngineLandMark2(Sender: TObject);
begin
  if ActivateLandmark.IsRequired
    then
      CurrentPitch := FLandMark;
end;

procedure TRamugen.EngineKey(Sender: TObject);
begin
   if TransposeFactor.Change.IsRequired
      then FKey := TransposeFactor.Choose;
   FNote := FNote + FKey;   if FNote > Pitch.Max then FNote := FNote - 12;
   FNote2 := FNote2 + FKey; if FNote2 > Pitch.Max then FNote2 := FNote2 - 12;
   FNote3 := FNote3 + FKey; if FNote3 > Pitch.Max then FNote3 := FNote3 - 12;
   FNote4 := FNote4 + FKey; if FNote4 > Pitch.Max then FNote4 := FNote4 - 12;
   FNote5 := FNote5 + FKey; if FNote5 > Pitch.Max then FNote5 := FNote5 - 12;
   FNote6 := FNote6 + FKey; if FNote6 > Pitch.Max then FNote6 := FNote6 - 12;
end;

procedure TRamugen.EngineAmplitude(Sender: TObject);
begin
 if Amplitude.Change.IsRequired then
                 CurrentAmplitude := amplitude.choose
                                else
                 begin  //Cresc./Dim.
                   if Amplitude.MovingUp
                     then CurrentAmplitude := CurrentAmplitude + Amplitude.UnitSize
                     else CurrentAmplitude := CurrentAmplitude - Amplitude.UnitSize
                 end;
end;

procedure TRamugen.EngineDuration(Sender: TObject);
begin
 if Duration.Change.IsRequired then
                 CurrentDuration := duration.choose
                               else begin
                     if Duration.MovingUp then
                 CurrentDuration := CurrentDuration + Duration.UnitSize
                                                else
                 CurrentDuration := CurrentDuration - Duration.UnitSize
                                     end;
end;

procedure TRamugen.EngineStartNote(Sender: TObject);
var zA, t : Integer;
begin
 zA := CurrentAmplitude; if Rest.IsRequired then zA := 0;
 StartNote(FNote, zA);
 t := NumNotes.Choose;
 if t > 1 then StartNote(FNote2, zA);
  if t > 2 then StartNote(FNote3, zA);
  if t > 3 then StartNote(FNote4, zA);
  if t > 4 then StartNote(FNote5, zA);
  if t > 5 then StartNote(FNote6, zA);
 if FStoreToArray then AddNote(Fnote, CurrentDuration,zA);
 MIDITimer.Interval := CurrentDuration;
 if FSkipEngine then MIDITimer.Interval := 10;
 if playing then MIDITimer.Enabled := true else EngineStopNote(nil);
end;






//Register Components for the VCL

procedure Register;
begin
 RegisterComponents('Ramugen' , [TRamugen, TRamugenPreset, TMidiArray]);
end;


end.