mirror of
https://github.com/ssb22/clara-empricost.git
synced 2023-06-10 09:55:28 +00:00
251 lines
11 KiB
C++
251 lines
11 KiB
C++
/*
|
|
Clara Empricost - Condition List Action Rejection
|
|
Algorithm for the Evaluation of Music at Pseudo-Random
|
|
Idea Construction Stage
|
|
|
|
(C) 1996-2001 Silas S. Brown http://ssb22.user.srcf.net
|
|
This code is no longer being maintained by the author.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
#ifndef CHORD_H
|
|
#define CHORD_H
|
|
|
|
#define incThird 2
|
|
#define incFifth 4
|
|
#define SOP_OCTAVE 4
|
|
#define ALT_OCTAVE 3
|
|
#define TEN_OCTAVE 3
|
|
#define BAS_OCTAVE 2
|
|
|
|
#include "melody.h"
|
|
#include "note.h"
|
|
#include "interval.h"
|
|
#include <stdlib.h>
|
|
#include "part.h"
|
|
#include "mwr.h"
|
|
|
|
enum Inversion {IROOT,IFIRST,ISECOND,IUNDEFINED};
|
|
enum Figure {I=0,II,III,IV,V,VI,VII,FUNDEFINED};
|
|
|
|
// 1/3/5 in each part, *2 8ves, *7 figures
|
|
#define possOctaves 2
|
|
#define possChordDegrees 3
|
|
#define possFigs 7
|
|
#define BITS_PER_BYTE 8
|
|
#define FULLBYTE 255
|
|
//#define numPossibilities possOctaves*possChordDegrees*possOctaves*possChordDegrees*possOctaves*possChordDegrees*possOctaves*possChordDegrees*possFigs
|
|
#define numPossibilities 9072
|
|
#define packSize 81
|
|
// packSize: Or 16, etc. (goes into numPossibilities but NOT multiple of possFigs)
|
|
|
|
enum NoteOfChord { N_ONE1,N_THREE1,N_FIVE1,N_ONE2,N_THREE2,N_FIVE2,N_UNDEFINED };
|
|
|
|
// Choose at random occasionally, more likely in sop, on weak beats
|
|
// Ornament theOrnPart if defined
|
|
enum OrnamentType {
|
|
ORN_NONE, // No ornament
|
|
ORN_DAGATA, // Split a quaver into two semiquavers then have a quaver
|
|
ORN_TADAGA, // Quaver then the two semis
|
|
ORN_SKIP, // Make passing note from quaver to semiquaver, dotting the previous quaver (including "Scotch snap" for accented passing notes)
|
|
// ORN_RPT, // Repeat the note after a harmony note eg. L8cL16ec
|
|
ORN_TRILL, // Trill (NOT the passing note!): ACCEPTABLE EVEN IF NO PASS NOTE
|
|
MAX_ORNAMENT_TYPE // Value for number of possibilities for OrnamentType
|
|
};
|
|
|
|
enum SuspendedStatusType { NO_SUSPENSIONS,SOP_SUSPENDED,ALT_SUSPENDED,TEN_SUSPENDED,BAS_SUSPENDED,SA_SUSPENDED,AT_SUSPENDED,TB_SUSPENDED};
|
|
|
|
class ChordPoss {
|
|
public:
|
|
static NoteOfChord getSop(int possibility);
|
|
static NoteOfChord getAlt(int possibility);
|
|
static NoteOfChord getTen(int possibility);
|
|
static NoteOfChord getBas(int possibility);
|
|
static Figure getFigure(int possibility);
|
|
};
|
|
#define undefinedPoss (-1) /* Used when loading chords from a file */
|
|
class Chord {
|
|
public:
|
|
Chord(const Note &s,const Note &a,const Note &t,const Note &b,Melody* whichMelody,Chord* lastChord=NULL,NumBeats length=1,int possNo=undefinedPoss,int origPos=undefinedPoss/*,SuspendedStatusType suspendedStatus=NO_SUSPENSIONS*/,Key*k=NULL);
|
|
// constructor generates possibilities within SATB (undefinedNote is poss)
|
|
~Chord();
|
|
Chord* setNextChord(Chord* chord);
|
|
Inversion getInversion() const;
|
|
Figure getFigure() const { return(theFigure); }
|
|
Figure getFigureIn(KeyRef key) const;
|
|
void youAreNoGood();
|
|
Note getPart(Part part) const;
|
|
Note getSoprano() const;
|
|
Note getAlto() const;
|
|
Note getTenor() const;
|
|
Note getBass() const;
|
|
Note getPassNote(Part part) const;
|
|
Note getPartOrPassNote(Part part) const;
|
|
Note getPartOrPass(int pass,Part part) const { if (pass) return(getPassNote(part)); return(getPart(part)); }; // For MWR
|
|
Strength getBeatStrength() const;
|
|
Chord* getLastChord() const;
|
|
Chord* getNextChord() const;
|
|
Interval getIntBetween(Part p1,Part p2) const;
|
|
Interval getSAInt() const;
|
|
Interval getSTInt() const;
|
|
Interval getSBInt() const;
|
|
Interval getATInt() const;
|
|
Interval getABInt() const;
|
|
Interval getTBInt() const;
|
|
int getNumThirds(NoteOfChord n1=N_THREE1,NoteOfChord n2=N_THREE2) const;
|
|
Boolean isMajorTriad() const;
|
|
Boolean isDiminishedTriad() const;
|
|
NoteOfChord getNoteOfChord(Part part) const;
|
|
Boolean isLastOfCadence() const;
|
|
Boolean isFinalChord() const;
|
|
Boolean hasPassingNotes() const { return((havePassingNotes[0]!=P_UNDEFINED || havePassingNotes[1]!=P_UNDEFINED)?TRUE:FALSE); }
|
|
Boolean nextNeedsToAccent(Part part=P_UNDEFINED) const;
|
|
void saveTo(FILE* file) const;
|
|
void printReportTo(FILE* file) const;
|
|
int outputToMwr(Mwr &mwr,int extnOfLast,int &dot,int &d2,int &loop,int needToEndPart=0,const Key* transposeTo=NULL,Boolean addChromaticisms=FALSE,
|
|
Boolean doRepeatEnd=FALSE) const; // Returns 1 if next chord needs to be extention of the two
|
|
Key getKey() const { return(theKey); }
|
|
Key* getKeyPtr() /*const*/ { return(&theKey); } // Not const because this version of the compiler complains!
|
|
Boolean isInKey(KeyRef key) const { return((getSoprano().isInKey(key)==TRUE && getAlto().isInKey(key)==TRUE && getTenor().isInKey(key)==TRUE && getBass().isInKey(key)==TRUE)?TRUE:FALSE); }
|
|
int sopIsSuspended() const { return(theSuspendedStatus==SOP_SUSPENDED || theSuspendedStatus==SA_SUSPENDED); }
|
|
int altIsSuspended() const { return(theSuspendedStatus==ALT_SUSPENDED || theSuspendedStatus==SA_SUSPENDED || theSuspendedStatus==AT_SUSPENDED); }
|
|
int tenIsSuspended() const { return(theSuspendedStatus==TEN_SUSPENDED || theSuspendedStatus==TB_SUSPENDED || theSuspendedStatus==AT_SUSPENDED); }
|
|
int basIsSuspended() const { return(theSuspendedStatus==BAS_SUSPENDED || theSuspendedStatus==TB_SUSPENDED); }
|
|
Boolean isPartSuspended(Part part) const;
|
|
Boolean isUpbeat() const;
|
|
protected:
|
|
/* Chords select a possibility at random, and did add 1 to that
|
|
possibility until they found one which is acceptable. However,
|
|
if there is a large gap in the acceptable possibilities, then this
|
|
increases the probability of choosing the possibility after this
|
|
gap, and this bias is unwanted. Hence the indirection of the
|
|
selection via a "PackOfCards". However, it would be unweildy in both
|
|
processing time and storage space to allocate a separate PackOfCards
|
|
to each Chord. Allocating one to each Melody is feasible because the
|
|
more the better due to possible gaps in the remapped version, and Melodies
|
|
are treated as self-contained "tasks" in parallel processing, by the TSR
|
|
version, and when the program is interrupted part-way through.
|
|
*/
|
|
PackOfCards* getPack() const { return(theMelody->getPack()); }
|
|
void display() const;
|
|
void erase() const;
|
|
void calculateFigure();
|
|
Figure theFigure;
|
|
Boolean hasNoteInCommonWith(Chord* chord) const {
|
|
return((getSoprano().isEqualTo(chord->getSoprano())==TRUE || getAlto().isEqualTo(chord->getAlto())==TRUE || getTenor().isEqualTo(chord->getTenor())==TRUE || getBass().isEqualTo(chord->getBass())==TRUE)?TRUE:FALSE);
|
|
}
|
|
Boolean isFalseRelation() const;
|
|
Boolean modulationIsFineV(KeyRef newKey) /*const*/; // Call after 1st chord of cadence formed
|
|
Boolean modulationIsFineI() const; // Call after 2nd chord of cadence formed
|
|
Boolean theCadenceStatus;
|
|
void changeChromaticAccidental(char &accidental,Part whichPart) const;
|
|
void doChromaticUnessentials(char &accidental,const Note ¬e,const Note &before,const Note &after) const;
|
|
void chooseAPossibility(int start);
|
|
int findSomePossibility();
|
|
int theChosenPossibility,origVal,theUnmappedPossibility;
|
|
Melody* theMelody;
|
|
const NumBeats theLength;
|
|
Chord* theLastChord;
|
|
Chord* theNextChord;
|
|
const Note theSoprano,theAlto,theTenor,theBass;
|
|
Note realNote(NoteOfChord note,Octave octave,Figure figure,int addOne=0) const;
|
|
int isPossible();
|
|
Strength theBeatStrength;
|
|
int twoPartCheck(Part upper,Part lower) const;
|
|
int checkMelody(Part part) const;
|
|
int invalidCadence() const;
|
|
int isCadence() const;
|
|
Boolean theFinalChordStat;
|
|
Boolean isNextChordFinal() const { return(theNextChord?(theNextChord->isFinalChord()):(theMelody->isNextChordFinal())); }
|
|
Boolean canSuspendPart(Part part) const; // Next and last chords must be present
|
|
Part havePassingNotes[2];
|
|
Note* thePassingNotes[2];
|
|
void delPassNotes();
|
|
void selectPassNotes(); // Only called when next chord is defined
|
|
void selectSuspensions(); // Ditto
|
|
Part getPassPart(int index,int combination);
|
|
char getPassType(int index,int combination);
|
|
int initPassNotes(int combination);
|
|
int checkRange(const Note ¬e,Part part) const;
|
|
// int tuneIsFine() const;
|
|
SuspendedStatusType theSuspendedStatus; // Applicable to percussion of suspension
|
|
Boolean canAccentPassNotes() const;
|
|
Boolean accentPassNotes; OrnamentType theOrnament; Part theOrnPart;
|
|
Key theKey,oldKey;
|
|
char possibilitiesEvaluated;
|
|
};
|
|
|
|
Chord* loadChord(FILE* file,Melody* whichMelody,Chord* lastChord=NULL);
|
|
|
|
inline Figure ChordPoss::getFigure(int possibility) {
|
|
return((Figure)(possibility%possFigs));
|
|
}
|
|
|
|
inline Chord* Chord::setNextChord(Chord* chord) {
|
|
theNextChord=chord;
|
|
return(this);
|
|
}
|
|
|
|
inline Figure Chord::getFigureIn(KeyRef key) const {
|
|
if (key.isEqualTo(oldKey)==TRUE) return(ChordPoss::getFigure(theChosenPossibility));
|
|
Note x((DegreeOfScale)ChordPoss::getFigure(theChosenPossibility),BAS_OCTAVE,oldKey);
|
|
x.adjustToKey(key);
|
|
return((Figure)(x.getDegreeOfScale()));
|
|
}
|
|
|
|
inline void Chord::youAreNoGood() {
|
|
erase();
|
|
// Using the fact that whenever the program CAN modulate, it WILL,
|
|
// can change any EXISTING modulations to go to the destination key
|
|
// if this is the problem
|
|
if (theMelody->evalLastChordYet() && theKey.isEqualTo(oldKey)==FALSE && theKey.isEqualTo(theMelody->getDestinationKey())==FALSE && modulationIsFineV(theMelody->getDestinationKey())==TRUE)
|
|
{ theKey=theMelody->getDestinationKey(); calculateFigure(); display(); }
|
|
// Otherwise, it could be a modulation causing the problem, so don't modulate
|
|
else if (theKey.isEqualTo(oldKey)==FALSE) { theKey=oldKey; calculateFigure(); display(); }
|
|
// Failing all that, it's "back to the drawing board" ....
|
|
else { if (theLastChord) oldKey=theLastChord->getKey(); chooseAPossibility((theUnmappedPossibility+1)%numPossibilities); } // chooseAPossibility also assigns theKey
|
|
}
|
|
|
|
inline Strength Chord::getBeatStrength() const {
|
|
return(theBeatStrength);
|
|
}
|
|
|
|
inline Chord* Chord::getLastChord() const {
|
|
return(theLastChord);
|
|
}
|
|
|
|
inline Chord* Chord::getNextChord() const {
|
|
return(theNextChord);
|
|
}
|
|
|
|
inline Boolean Chord::isLastOfCadence() const {
|
|
return(theCadenceStatus);
|
|
}
|
|
|
|
inline Boolean Chord::isFinalChord() const {
|
|
return(theFinalChordStat);
|
|
}
|
|
|
|
inline Part Chord::getPassPart(int index,int combination) {
|
|
return((Part)((combination/(index?1:4))%4));
|
|
}
|
|
|
|
inline char Chord::getPassType(int index,int combination) {
|
|
return((char)((combination/(index?16:32))%2));
|
|
}
|
|
|
|
inline Boolean Chord::isUpbeat() const {
|
|
return(((theNextChord?theNextChord->getBeatStrength():theMelody->strengthOfNextBeat())>getBeatStrength())?TRUE:FALSE);
|
|
}
|
|
#endif
|