mirror of
https://github.com/ssb22/clara-empricost.git
synced 2023-06-10 09:55:28 +00:00
291 lines
12 KiB
C++
291 lines
12 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.
|
|
*/
|
|
#include "mappings.h"
|
|
#include "melody.h"
|
|
#include "chord.h"
|
|
#include "rng.h"
|
|
#include <stddef.h> // For NULL
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include "protect.h"
|
|
|
|
#ifndef TSR_VERSION
|
|
extern Boolean newMelodiesIn68;
|
|
#endif
|
|
int Melody::nextMelodyNumber=0;
|
|
|
|
int useYourTimeUsefully();
|
|
char* getFilename(int number,Boolean complete);
|
|
char* getFilename2(const char* fname);
|
|
|
|
void Melody::deleteAllFiles() {
|
|
for (int lp=0; lp<nextMelodyNumber; lp++)
|
|
deleteFile(getFilename(lp,TRUE));
|
|
}
|
|
|
|
PackOfCards* Melody::thePack=NULL;
|
|
|
|
#ifndef TSR_VERSION
|
|
Melody::Melody(NumBeats beatsPerBar,NumBeats anacrusis,KeyRef key,int numChords,int cadences,int imperfectEnd,int properStart,ModulateStatus canMod,Boolean canHavePassing,const Key* finishKey,Boolean willAddChr)
|
|
: theKey(key), theFinishKey(finishKey?(*finishKey):key), theAddChrStatus(willAddChr),
|
|
theAnacrusis(anacrusis), theCanModulate(canMod), canHaveUnessentials(canHavePassing),
|
|
doNeedEndFinal(imperfectEnd?FALSE:TRUE), properStartStatus(properStart?TRUE:FALSE),
|
|
theBeatsPerBar(beatsPerBar), theNumberOfChords(numChords), theNumberOfCadences(cadences),
|
|
in68(newMelodiesIn68), theNumber(nextMelodyNumber++) {
|
|
if (!thePack) thePack=new PackOfCards(packSize); else thePack->reShuffle();
|
|
theFirstChord=theLastChord=NULL; isComplete=FALSE; chordLp=1;
|
|
if (filenameMatching(getFilename(theNumber,TRUE)))
|
|
cout << "Task " << theNumber << " was already completed" << endl;
|
|
else if (!filenameMatching(getFilename(theNumber,isComplete))) {
|
|
if (filenameMatching(getFilename2(getFilename(theNumber,isComplete)))) {
|
|
cout << "Task " << theNumber << " had been incorrectly aborted; re-initialising from last recorded position" << endl;
|
|
renameFile(getFilename2(getFilename(theNumber,isComplete)),getFilename(theNumber,isComplete));
|
|
} else {
|
|
cout << "Initialising task " << theNumber << endl;
|
|
saveYourself();
|
|
}
|
|
} else cout << "Task " << theNumber << " was interrupted, can be resumed" << endl;
|
|
scrambleRNG();
|
|
}
|
|
#endif
|
|
|
|
void Melody::saveYourself() const {
|
|
char* c=getFilename2(getFilename(theNumber,isComplete));
|
|
// Start with file of different name in case anyone tries to open it while we are saving
|
|
// - NetWare lets you get away with murder
|
|
FILE* file=fopen(c,"wb");
|
|
makeAuthentic(file);
|
|
saveTo(file); fclose(file);
|
|
renameFile(c,getFilename(theNumber,isComplete));
|
|
}
|
|
|
|
#ifndef SETUP_ONLY_VERSION
|
|
Melody::Melody(FILE* file)
|
|
// Lots of initialisers so that the compiler doesn't complain
|
|
// (Note that we can't load from file here because the order of evaluation
|
|
// of the initialisers is liable to vary)
|
|
: canHaveUnessentials(TRUE), doNeedEndFinal(TRUE), theBeatsPerBar((NumBeats)0),
|
|
theAnacrusis((NumBeats)0), properStartStatus(FALSE), theCanModulate(MS_FALSE),
|
|
theAddChrStatus(FALSE), theNumberOfChords(0), theNumberOfCadences(0),
|
|
in68(FALSE), theNumber(0)
|
|
{
|
|
isComplete=FALSE; theFirstChord=theLastChord=NULL; chordLp=1; generate(file);
|
|
}
|
|
#endif
|
|
|
|
void Melody::saveTo(FILE* file) const {
|
|
thePack->saveTo(file);
|
|
saveRNG(file);
|
|
fwrite(&canHaveUnessentials,sizeof(canHaveUnessentials),1,file);
|
|
fwrite(&doNeedEndFinal,sizeof(doNeedEndFinal),1,file);
|
|
fwrite(&theBeatsPerBar,sizeof(theBeatsPerBar),1,file);
|
|
fwrite(&theAnacrusis,sizeof(theAnacrusis),1,file);
|
|
theKey.saveTo(file); theFinishKey.saveTo(file);
|
|
fwrite(&properStartStatus,sizeof(properStartStatus),1,file);
|
|
fwrite(&theCanModulate,sizeof(theCanModulate),1,file);
|
|
fwrite(&theAddChrStatus,sizeof(theAddChrStatus),1,file);
|
|
fwrite(&theNumberOfChords,sizeof(theNumberOfChords),1,file);
|
|
fwrite(&theNumberOfCadences,sizeof(theNumberOfCadences),1,file);
|
|
fwrite(&theNumber,sizeof(theNumber),1,file);
|
|
fwrite(&in68,sizeof(in68),1,file);
|
|
fwrite(&theEvalLastChordStatus,sizeof(theEvalLastChordStatus),1,file);
|
|
#ifndef SETUP_ONLY_VERSION
|
|
Chord* x=theFirstChord;
|
|
while (x) {
|
|
x->saveTo(file);
|
|
x=x->getNextChord();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef SETUP_ONLY_VERSION
|
|
void Melody::generate(FILE* loadFrom) {
|
|
if (loadFrom) {
|
|
if (!thePack) thePack=new PackOfCards(loadFrom); else thePack->reLoad(loadFrom);
|
|
loadRNG(loadFrom);
|
|
fread((void*)&canHaveUnessentials,sizeof(canHaveUnessentials),1,loadFrom);
|
|
fread((void*)&doNeedEndFinal,sizeof(doNeedEndFinal),1,loadFrom);
|
|
fread((void*)&theBeatsPerBar,sizeof(theBeatsPerBar),1,loadFrom);
|
|
fread((void*)&theAnacrusis,sizeof(theAnacrusis),1,loadFrom);
|
|
Key k1(loadFrom); Key k2(loadFrom); theKey=k1; theFinishKey=k2;
|
|
fread((void*)&properStartStatus,sizeof(properStartStatus),1,loadFrom);
|
|
fread((void*)&theCanModulate,sizeof(theCanModulate),1,loadFrom);
|
|
fread((void*)&theAddChrStatus,sizeof(theAddChrStatus),1,loadFrom);
|
|
fread((void*)&theNumberOfChords,sizeof(theNumberOfChords),1,loadFrom);
|
|
fread((void*)&theNumberOfCadences,sizeof(theNumberOfCadences),1,loadFrom);
|
|
fread((void*)&theNumber,sizeof(theNumber),1,loadFrom);
|
|
fread((void*)&in68,sizeof(in68),1,loadFrom);
|
|
fread((void*)&theEvalLastChordStatus,sizeof(theEvalLastChordStatus),1,loadFrom);
|
|
#ifndef TSR_VERSION
|
|
theKey.printReportTo(stdout); printf("->"); theFinishKey.printReportTo(stdout);
|
|
cout <<
|
|
" canMod=" << (int)theCanModulate <<
|
|
" bpb=" << (int)theBeatsPerBar <<
|
|
" anac=" << (int)theAnacrusis <<
|
|
" in68=" << (int)in68 <<
|
|
" chords=" << (int)theNumberOfChords <<
|
|
" cadences=" << (int)theNumberOfCadences <<
|
|
" endFinal=" << (int)doNeedEndFinal <<
|
|
endl;
|
|
#endif
|
|
} else {
|
|
if (chordLp>theNumberOfChords) return;
|
|
// thePack will always be initialised
|
|
}
|
|
if (chordLp==1) {
|
|
if (theAnacrusis) theBeatsLeft=theBeatsPerBar-theAnacrusis; else theBeatsLeft=theBeatsPerBar;
|
|
theFirstChord=theLastChord=NULL;
|
|
theCadenceStatus=theFinalStatus=theEvalLastChordStatus=FALSE;
|
|
} else if (!loadFrom)
|
|
// In this case, we are generating after a load, and don't forget
|
|
// that, just before the save, theLastChord was about to be told
|
|
theLastChord->youAreNoGood();
|
|
for (; chordLp<=theNumberOfChords; chordLp++) {
|
|
if (chordLp==theNumberOfChords) nextChordIsFinalCadence();
|
|
// Otherwise make on 3rd beat if 3, last but 1 if >3, 1st if 2
|
|
// Bar number = (chordLp-theAnacrusis)/theBeatsPerBar+1
|
|
// Total bars = (numChords-theAnacrusis)/theBeatsPerBar+1
|
|
// Bar numbers for cadences: !(barNumber%(totalBars/cadences))
|
|
// Beat of bar = chordLp-theAnacrusis
|
|
// For chordLp read chordLp-1
|
|
// So altogether we have:-
|
|
else if (chordLp>theAnacrusis &&
|
|
(!(((chordLp-1-theAnacrusis)/theBeatsPerBar+1)%(((theNumberOfChords-theAnacrusis)/theBeatsPerBar+1)/theNumberOfCadences)) && ((chordLp-theAnacrusis-1)%theBeatsPerBar)+1==((theBeatsPerBar==3)?3:theBeatsPerBar-1)))
|
|
nextChordIsCadence();
|
|
Chord* temp=loadChord(loadFrom,this,theLastChord);
|
|
if (temp) theLastChord=temp;
|
|
else {
|
|
if (loadFrom) return;
|
|
if (theLastChord) theLastChord=new Chord(undefinedNote,undefinedNote,undefinedNote,undefinedNote,this,theLastChord,(NumBeats)1,undefinedPoss,undefinedPoss,theLastChord->getKeyPtr());
|
|
else theLastChord=new Chord(undefinedNote,undefinedNote,undefinedNote,undefinedNote,this,theLastChord);
|
|
}
|
|
}
|
|
#ifndef TSR_VERSION
|
|
thePedalNote=NULL;
|
|
if (canModulate()==FALSE) {
|
|
Figure f1=theLastChord->getFigure(),f2=theFirstChord->getFigure();
|
|
if (f1==V && (f2==I || f2==III || f2==V))
|
|
thePedalNote=new Note(DOMINANT,PEDAL_OCTAVE,theKey);
|
|
else if (f1==I && (f2==IV || f2==VI || f2==I))
|
|
thePedalNote=new Note(TONIC,PEDAL_OCTAVE,theKey);
|
|
}
|
|
#endif
|
|
isComplete=TRUE;
|
|
}
|
|
#endif
|
|
|
|
Melody::~Melody() {
|
|
#ifndef SETUP_ONLY_VERSION
|
|
if (theFirstChord) delete theFirstChord
|
|
#endif
|
|
;
|
|
}
|
|
|
|
#ifndef SETUP_ONLY_VERSION
|
|
void Melody::addLength(NumBeats noteLen) {
|
|
theCadenceStatus=FALSE;
|
|
theBeatsLeft-=noteLen;
|
|
if (theBeatsLeft<=0) theBeatsLeft+=theBeatsPerBar;
|
|
}
|
|
|
|
Strength Melody::strengthOfNextBeat() const {
|
|
if (theBeatsLeft==theBeatsPerBar) return(S_1ST_BEAT);
|
|
else if (theBeatsLeft*2==theBeatsPerBar || theBeatsLeft*3==theBeatsPerBar) return(S_3RD_BEAT);
|
|
else if (theBeatsLeft*4==theBeatsPerBar) return(S_4TH_BEAT);
|
|
else if (theBeatsLeft*4/3==theBeatsPerBar || theBeatsLeft*3/2==theBeatsPerBar) return(S_2ND_BEAT);
|
|
else return(S_NOT_BEAT);
|
|
}
|
|
|
|
#ifndef TSR_VERSION
|
|
void Melody::outputToMwr(Mwr &mwr,int needToInitPart,int needToEndPart,const Key* transposeTo,Boolean addChromaticisms,NumBeats changeAnacrusis,Boolean mute) {
|
|
while (isComplete==FALSE) {
|
|
FILE* file;
|
|
while ((file=fopen(getFilename(theNumber,TRUE),"rb"))==NULL || !authentic(file)) {
|
|
if (file) { fclose(file); deleteFile(getFilename(theNumber,TRUE)); }
|
|
if (!useYourTimeUsefully()) {
|
|
cout << "No tasks available to this workstation, waiting for " << theNumber << endl;
|
|
#ifndef TSR_VERSION
|
|
sleep(1);
|
|
if (aKeyIsPressed()) { // Exit without saving anything
|
|
#ifdef RUNNING_ON_DOS
|
|
if (getch()=='Q') { // Stop the people pressing Escape!
|
|
#endif
|
|
exit(0);
|
|
#ifdef RUNNING_ON_DOS
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
generate(file); fclose(file);
|
|
}
|
|
if (needToInitPart) mwr.initPart(theBeatsPerBar);
|
|
mwr.setIn68(in68);
|
|
Key key=(transposeTo?(*transposeTo):theKey);
|
|
mwr.setBeatsLeft((theAnacrusis?theAnacrusis:theBeatsPerBar)*2);
|
|
if (needToInitPart || (key.isEqualTo(mwr.getKey())==FALSE && mwr.fullBarAhead())) mwr.setKey(key);
|
|
int dot=0,d2=0; // d2 is part of 6/8 fix
|
|
if (theAnacrusis && needToInitPart) {
|
|
NumBeats anacrusis=theBeatsPerBar-theAnacrusis;
|
|
while (anacrusis>=2) { mwr.setLen(2,d2); mwr.output("r",d2); anacrusis-=2; }
|
|
if (anacrusis) { mwr.setLen(4,d2); mwr.output("r",d2); }
|
|
}
|
|
|
|
// Don't change theAnacrusis until here, because it is for leaving an anacrusis at the end of the piece
|
|
NumBeats oldAnacrusis=theAnacrusis;
|
|
// "Liberal" interpretation of const object, because will be changed back before the function is done
|
|
if (changeAnacrusis) *((NumBeats*)(&theAnacrusis))=changeAnacrusis;
|
|
|
|
Chord* chord=theFirstChord;
|
|
int lastRetVal=0,loop=1,pedalBeatsLeft=0,firstTime=1; // firstTime is for setting beatsLeft to 0
|
|
while (chord) {
|
|
if (mwr.getPart()==P_PEDAL || mute==TRUE) {
|
|
if (pedalBeatsLeft) pedalBeatsLeft--;
|
|
else {
|
|
char pedalNote[3]="r"; if (thePedalNote && mute==FALSE) {
|
|
mwr.setOctave(/*thePedalNote->getMwrOctave()*/PEDAL_OCTAVE+OCT_TRANS_FOR_MWR); // NOT note.getMwrOctave
|
|
Note note=*thePedalNote; // So any transposition is not kept
|
|
if (transposeTo) note.transposeToKey(*transposeTo);
|
|
note.initMwr(pedalNote);
|
|
}
|
|
int opbl=pedalBeatsLeft=mwr.getBeatsLeft()/2;
|
|
if (!needToInitPart && oldAnacrusis && firstTime) {
|
|
opbl=0; // Because previous Melody would have filled the bar
|
|
// (Leave pedalBeatsLeft as it is to count down)
|
|
mwr.setBeatsLeft(theBeatsPerBar*2); // To counteract setBeatsLeft above
|
|
// (ie get ready for next bar)
|
|
}
|
|
firstTime=0;
|
|
while (opbl>=4) { mwr.setLen(1,d2); mwr.output(pedalNote,d2); opbl-=4; mwr.subtractBeats(8/mwr.getLength(),NULL,(!(chord->getNextChord()) && needToEndPart==2)?TRUE:FALSE); }
|
|
while (opbl>=2) { mwr.setLen(2,d2); mwr.output(pedalNote,d2); opbl-=2; mwr.subtractBeats(8/mwr.getLength(),NULL,(!(chord->getNextChord()) && needToEndPart==2)?TRUE:FALSE); }
|
|
if (opbl) { mwr.setLen(4,d2); mwr.output(pedalNote,d2); mwr.subtractBeats(8/mwr.getLength(),NULL,(!(chord->getNextChord()) && needToEndPart==2)?TRUE:FALSE); }
|
|
pedalBeatsLeft--;
|
|
}
|
|
} else lastRetVal=chord->outputToMwr(mwr,lastRetVal,dot,d2,loop,needToEndPart,transposeTo,addChromaticisms,(needToEndPart==2)?TRUE:FALSE);
|
|
chord=chord->getNextChord();
|
|
}
|
|
if (needToEndPart) mwr.printDoubleBar();
|
|
*((NumBeats*)(&theAnacrusis))=oldAnacrusis;
|
|
}
|
|
#endif
|
|
#endif
|