/*AlgoRhythms.c*/ /*Thomas E. Janzen 4 September 1989; 2-11-90 */ /*18 September 1989 26 nov 89 11 December 1989 16 December 1989*/ /*Music played with this program Copyright 1990 Thomas E. Janzen */ /*This program Copyright 1990 Thomas E. Janzen All Rights Reserved */ /*This program is for a Commodore (TM) Amiga (TM) 500 playing MIDI */ /*Lattice C 5.05*/ /*out the serial port to a single MIDI channel with MAXVOICE voices */ /*The music is randomized by changes slowly by sinusoidal functions */ /* ** FACILITY: ** ** AlgoRhythms music improviser on Commodore (TM) Amiga (TM) ** compiled with Lattice (TM) C 5.05 ** ** ABSTRACT: ** ** Algorhythms improvises music over the MIDI serial port. ** See the manual. ** ** AUTHOR: Thomas E. Janzen ** ** CREATION DATE: 26-MAR-1990 ** ** MODIFICATION HISTORY: ** DATE NAME DESCRIPTION **-- */ #define TWOPI (2 * PI) #define DURMAX 2.0 #define PROJECT (0) #define FORM (1) #define SCALE (2) #define CHANNEL (3) #include "limits.h" #include "math.h" #include "stdlib.h" #include "stdio.h" #include "string.h" #include "intuition/intuition.h" #include "exec/memory.h" #include "exec/interrupts.h" #include "exec/devices.h" #include "devices/serial.h" #include #include #include #include #include #include "Workbench/startup.h" #define STRINGSIZE 80 #include "musictimer.h" #include "window.h" extern struct Window *w; #include "musicserial.h" #include "Gadgets.h" struct Parameter Randomize_Parameter(void); #include "files.h" #include "DrawForm.h" extern struct GfxBase *GfxBase; extern struct IntuitionBase *IntuitionBase; extern struct DOSBase *DOSBase; extern struct MathBase *MathBase; void MakeEvent(struct NoteEvent *, struct Character *, const double CurTime); /* Calculates the next note to send out */ struct Parameter { double CenterCycle; double CenterPhase; double SpreadCycle; double SpreadPhase; }; int fubar=FALSE; static struct Parameter Pitch_Form = {180.0,-PID2,200.0,-PID2}; static struct Parameter Thickness_Form = {0.0, 0.0,190.0,-PID2}; static struct Parameter Dynamics_Form = {170.0,-PID2,165 ,-PID2}; static struct Parameter Duration_Form = {200.0, PID2,180.0, PID2}; struct Character { double PitchSpread; /*range of pitches*/ double PitchCenter; /* ~ median pitch*/ double DynamicSpread; /*range of dynamic levels*/ double DynamicCenter; /* ~ median of dynamic levels*/ double DurSpread; /*range of durations */ double DurCenter; /* ~ median of durations */ double Thickness; /*number of voices playing at once*/ }; struct Character *ranges( struct Parameter *Pitch, struct Parameter *Dyn, struct Parameter *Dur, struct Parameter *Thickness, const double phase); int EventPointer = 0; /*Pitch Range*/ int range=10; int halfrange=5; #include "scales.h" #include "menus.h" int ParseMessage(int class, int code, double *StrTime, struct Parameter *PitchForm, struct Parameter *RhythmForm, struct Parameter *DynamicsForm, struct Parameter *TextureForm, double *Duration,int *tempo, struct NoteEvent *Events); int scale[120] = {48,50,53,55,58,60,62,65,67,70,72,74,77}; /* holds the scale of notes allowed */ static int numvoices=MAXVOICE; static struct timeval timval; int playing = FALSE; /* playing music */ int started= FALSE; int done=FALSE; int quit=FALSE; int DelayTicks = 2; double MaxNoteLen = 2.0; double MinNoteLen = 0.0; double NoteLenDif = 2.0; void main(void) { register int tick=0; int code, class; /* mouse codes esp. for menus */ int Action=0; register double CurTime; /*the current time in seconds*/ double PieceDuration=600.0; /*piece length in seconds*/ int tempo; static struct NoteEvent Events[MAXVOICE]; register int temptv_secs; register int temptv_micro; static struct Character Now; int i; static double StartTime=0.0;/*time that the program starts to execute*/ register double phase; MakeWindow(); Open_MIDI_Port(); if(fubar == TRUE) goto cleanup2; Init_Menu(); /*menu*/ tempo=50/DelayTicks; range=InstallScale(11,scale); halfrange=range/2; for (i=0;i Events[EventPointer].Duration) { MakeEvent(&Events[EventPointer], &Now, CurTime); } } DrawTime(CurTime,PieceDuration); if (Action=CheckMenu(&class, &code)) { ParseMessage(class,code,&StartTime, &Pitch_Form,&Duration_Form,&Dynamics_Form, &Thickness_Form,&PieceDuration,&tempo, Events); } if (DelayTicks) { for(tick=0;tick PieceDuration)) { StopAllNotes(Events); started=FALSE; } } Wait(1 << w->UserPort->mp_SigBit); if (Action=CheckMenu(&class, &code)) { ParseMessage(class,code,&StartTime, &Pitch_Form,&Duration_Form,&Dynamics_Form, &Thickness_Form,&PieceDuration,&tempo,Events); } continue; } cleanup1: CloseMenu(); StopAllNotes(Events); Delay(10); RemoveTimer(); cleanup2: /* exit here if the serial port wouldn't open*/ ShutWindow(); StopMIDI(); exit(0); } /* end main*/ struct Character *ranges( struct Parameter *Pitch, struct Parameter *Dyn, struct Parameter *Dur, struct Parameter *Thickness, const double phase) { static struct Character NextNow; register double realhalfrange; realhalfrange=(double)halfrange; NextNow.PitchSpread=(sin((phase/Pitch->SpreadCycle)+ Pitch->SpreadPhase)+1.0)* realhalfrange; /*Pitch range is a sin function of time*/ NextNow.PitchCenter=(sin((phase/(Pitch->CenterCycle))+ (Pitch->CenterPhase))+1.0)* realhalfrange; /*Median Pitch is a sin funct of time*/ if((NextNow.PitchCenter - (NextNow.PitchSpread/2)) < 0) { NextNow.PitchCenter = 1.0 + ((NextNow.PitchCenter+ (NextNow.PitchSpread/2))/2); } else { if((NextNow.PitchCenter+(NextNow.PitchSpread/2)) > (double)range) { NextNow.PitchCenter = realhalfrange - 1.0 + ((NextNow.PitchCenter - (NextNow.PitchSpread/2.0)) /2.0); } } NextNow.DynamicSpread=(sin((phase/(Dyn->SpreadCycle))+ Dyn->SpreadPhase)+1.01)*63; /*Range of dynamics is a sin function of time*/ NextNow.DynamicCenter=((sin((phase/(Dyn->CenterCycle))+ Dyn->CenterPhase)+1.01)* 50) + 25; /*Median dynamic is a sin function of time*/ NextNow.DurSpread=(sin((phase/(Dur->SpreadCycle))+ Dur->SpreadPhase)+1.01)*NoteLenDif; /* Range of durations is a sin function of time*/ NextNow.DurCenter=((sin((phase/(Dur->CenterCycle))+ Dur->CenterPhase)+1.01)*NoteLenDif)+MinNoteLen; /*Median duration is a sin function of time*/ NextNow.Thickness=((sin((phase/(Thickness->SpreadCycle))+ Thickness->SpreadPhase)+1.01)*(double)numvoices/2.0)-1.0; /*the number of voices playing is a sin function of time*/ return &NextNow; } struct Parameter Randomize_Parameter(void) { struct Parameter Temp; register double reallong_max; reallong_max=(double)LONG_MAX; Temp.CenterCycle=((double)rand()/reallong_max)*(120)+90; /*1.5 to 3.5 minutes */ Temp.CenterPhase=((double)rand()/reallong_max)*TWOPI; Temp.SpreadCycle=((double)rand()/reallong_max)*(120)+90; /*1.5 to 3.5 minutes */ Temp.SpreadPhase=((double)rand()/reallong_max)*TWOPI; return Temp; } void MakeEvent(struct NoteEvent *NewEvent, struct Character *Style, const double CurTime) { int LowNote; int HighNote; int OKRandNote; int walk; register double reallong_max; register double reallong_maxdiv2; double NewDynamic; /*a temporary holder of a new dynamic value*/ double NewDuration; /*a temporary holder of a new duration value*/ int NewPitchIndex; OKRandNote=TRUE; reallong_max=(double)LONG_MAX; reallong_maxdiv2=reallong_max/2.0; PlayNoteOff(NewEvent); /*Turn off the previous note*/ /*for this voice*/ if (EventPointer<=(int)(Style->Thickness)) { NewDynamic= ((((double)rand()/reallong_maxdiv2))-1.0) *(Style->DynamicSpread)+(Style->DynamicCenter); /*get random dynamic*/ if (NewDynamic > 127){ /*compress dynamic to be valid*/ NewDynamic=127; } if (NewDynamic < 0 ) { /*compress dynamic to be valid*/ NewDynamic=0; } NewEvent->Dynamic=(int)NewDynamic;/* make dynamic a byte*/ NewDuration=((((double)rand()/reallong_maxdiv2))-1.0) * (Style->DurSpread)+(Style->DurCenter) + ((Style->DurSpread)/8); /*Get random duration*/ if (NewDuration<0.0) { /*force duration positive*/ NewDuration = 0; } NewEvent->Duration=NewDuration; /*Put duration in */ /* durations list */ NewEvent->StartTime=CurTime; /*Make attacktime for this note */ /* equal to current time */ /*PITCH*/ if(NewEvent->Walking) { HighNote= ((NewEvent->CurPitch) >= NewEvent->HighPitch); LowNote= ((NewEvent->CurPitch) <= NewEvent->LowPitch); walk = (rand() % 3)-1; if(LowNote) { walk=1; } if(HighNote) { walk=-1; } NewPitchIndex=(NewEvent->Pitch)+walk; } else { NewPitchIndex= ((((double)rand()/reallong_maxdiv2))-1.0) * (Style->PitchSpread)+(Style->PitchCenter); HighNote= ((scale[NewPitchIndex]) >= NewEvent->HighPitch); LowNote= ((scale[NewPitchIndex]) <= NewEvent->LowPitch); OKRandNote=(!(HighNote) && (!(LowNote))); } if (NewPitchIndex >= range) { NewPitchIndex=range-1; /*fold under if too hi*/ } if (NewPitchIndex < 0) { /*fold up if too low */ NewPitchIndex = 0; } NewEvent->Pitch=(int)NewPitchIndex; /*pitch in note list*/ NewEvent->CurPitch=scale[(int)NewPitchIndex]; /*Play the note*/ if (playing && OKRandNote)PlayNoteOn(NewEvent); } } int ParseMessage(int class, int code, double *StrTime, struct Parameter *PitchForm, struct Parameter *RhythmForm, struct Parameter *DynamicsForm, struct Parameter *TextureForm, double *Duration,int *tempo, struct NoteEvent Events[MAXVOICE] ) { int i; int tempint; int Status; /* file status */ extern int midi_addr; int item=0; int subitem=0; static double StopTime, ContTime; static char AnswerBuf[96]; static char FileString[64]={0,0,0,0,0,0,0,0,0}; char *AnsBuf; static char FileNameString[] = "File Name"; static char DurationString[]="Duration in Seconds"; static char PaceString[]="Rhythmic Pace"; static char DynamicString[]="Dynamics"; static char TextureString[]="Texture"; static char PitchString[]="Pitch"; static char DynamicMean[]="Dynamic Mean"; static char DynamicSpread[]="Dynamic Spread"; static char TextureSpread[]="Texture Spread"; static char PitchMean[]="Pitch Mean"; static char PitchSpread[]="Pitch Spread"; static char RhythmMean[]="Rhythm Mean"; static char RhythmSpread[]="Rhythm Spread"; static char VoiceString[]="Number of Voices"; static char BlankString[4]=" "; static char SpreadPeriodString[]="Spread Period"; static char MeanPeriodString[]="Mean Period"; static char PulseString[]="Pulses per Second"; static char TransposeString[]="Tranpose"; static char LoadFileString[]="Load File"; static char SaveFileString[]="Save File"; static char NoteLenString[]="Note Length"; static char MinimumString[]="Minimum"; static char MaximumString[]="Maximum"; static char About1String[24]= "Welcome to AlgoRhythms"; static struct IntuiText About1Txt= {2,1,JAM1,5,4,NULL,About1String,NULL}; static char About2String[32]= "Copyright 1990 Thomas E. Janzen"; static struct IntuiText About2Txt= {2,1,JAM1,5,15,NULL,About2String,&About1Txt}; static char ThanksString[]="Thanks"; struct IntuiText ThanksTxt={2,1,JAM1,5,4,NULL,ThanksString, NULL}; int Response; switch(class) { case NEWSIZE: DrawForm(*Duration,PitchForm,RhythmForm, DynamicsForm,TextureForm); break; case MENUPICK: if (code != MENUNULL) { item=ITEMNUM(code); subitem=SUBNUM(code); switch(MENUNUM(code)) { case PROJECT: if (item != NOITEM) switch(item) { case 0: /*quit*/ quit=TRUE; break; case 1: /*About*/ Response=AutoRequest(w,&About2Txt,&ThanksTxt,&ThanksTxt, 0L,0L,300L,60L); break; case 2: /*save*/ sprintf(AnswerBuf,"%s",FileString); Status=GetStringInput(AnswerBuf,SaveFileString, FileNameString); if (Status == 1) break; Status=Save_File(AnswerBuf,Duration,&range,scale, &numvoices,tempo,PitchForm,TextureForm,DynamicsForm, RhythmForm,Events,MinNoteLen,MaxNoteLen); if (Status == 0) { __builtin_strcpy(FileString,AnswerBuf); } else { DisplayBeep(NULL); } break; case 3: /*load*/ sprintf(AnswerBuf,"%s",FileString); Status=GetStringInput(AnswerBuf,LoadFileString, FileNameString); if (Status == 1) break; Status=Read_File(AnswerBuf,Duration, &range,scale,&numvoices,tempo,PitchForm, TextureForm,DynamicsForm,RhythmForm,Events, &MinNoteLen,&MaxNoteLen); if (Status == 0) { if (*tempo == 0) { DelayTicks=0; } else { DelayTicks=50/(*tempo); } halfrange=range/2; __builtin_strcpy(FileString,AnswerBuf); DrawForm(*Duration,PitchForm,RhythmForm, DynamicsForm,TextureForm); } else { DisplayBeep(NULL); } break; case 4: /* Continue */ started=TRUE; GetSysTime(&timval); ContTime=(double)timval.tv_secs+ (((double)timval.tv_micro)*0.000001); *StrTime=*StrTime+(ContTime-StopTime); break; case 5: /*stop*/ started=FALSE; for (i=0;iStartTime=0.0; (&Events[i])->Duration=0.0; } StopAllNotes(Events); GetSysTime(&timval); StopTime=(double)timval.tv_secs+ (((double)timval.tv_micro)*0.000001); break; case 6: /*Start to Play*/ started=TRUE; done=FALSE; GetSysTime(&timval); *StrTime=(double)timval.tv_secs+ (((double)timval.tv_micro)*0.000001); for (i=0;iStartTime=0.0; (&Events[i])->Duration=0.0; } SendFunction(STARTFUNCT); break; } /*switch itemnum*/ break; case FORM: if (item != NOITEM) switch(item) { case 0: /*note length*/ if (subitem != NOSUB) switch(subitem) { case 0: /*minimum*/ sprintf(AnswerBuf,"%5.2f",MinNoteLen); AnsBuf=GetGadgetInput(AnswerBuf, NoteLenString,MinimumString); MinNoteLen=atof(AnsBuf); NoteLenDif=MaxNoteLen-MinNoteLen; break; case 1: /*maximum*/ sprintf(AnswerBuf,"%5.2f",MaxNoteLen); AnsBuf=GetGadgetInput(AnswerBuf, NoteLenString,MaximumString); MaxNoteLen=atof(AnsBuf); NoteLenDif=MaxNoteLen-MinNoteLen; break; } break; case 1: /*Texture*/ if (subitem != NOSUB) switch(subitem) { case 0: /* Randomize */ *TextureForm=Randomize_Parameter(); break; case 1: /*Spread Period*/ sprintf(AnswerBuf,"%2.0f",(TextureForm->SpreadCycle)); AnsBuf=GetGadgetInput(AnswerBuf, TextureString,SpreadPeriodString); TextureForm->SpreadCycle=atof(AnsBuf); break; case 2: /*Spread Phase*/ TextureForm->SpreadPhase= GetPhaseInput(TextureForm->SpreadPhase,TextureSpread); break; } break; case 2: /*Dynamic*/ if (subitem != NOSUB) switch(subitem) { case 0: /* Randomize */ *DynamicsForm=Randomize_Parameter(); break; case 1: /* Spread Phase*/ DynamicsForm->SpreadPhase= GetPhaseInput(DynamicsForm->SpreadPhase,DynamicSpread); break; case 2: /* Spread Period*/ sprintf(AnswerBuf,"%2.0f",DynamicsForm->SpreadCycle); AnsBuf=GetGadgetInput(AnswerBuf, DynamicString,SpreadPeriodString); DynamicsForm->SpreadCycle=atof(AnsBuf); break; case 3: /* mean Phase*/ DynamicsForm->CenterPhase= GetPhaseInput(DynamicsForm->CenterPhase,DynamicMean); break; case 4: /* Mean Period*/ sprintf(AnswerBuf,"%2.0f",DynamicsForm->CenterCycle); AnsBuf=GetGadgetInput(AnswerBuf, DynamicString,MeanPeriodString); DynamicsForm->CenterCycle=atof(AnsBuf); break; } break; case 3: /*Rhythm*/ if (subitem != NOSUB) switch(subitem) { case 0: /* Randomize */ *RhythmForm=Randomize_Parameter(); break; case 1: /* Spread Phase*/ RhythmForm->SpreadPhase= GetPhaseInput(RhythmForm->SpreadPhase,RhythmSpread); break; case 2: /* Spread Period*/ sprintf(AnswerBuf,"%2.0f",RhythmForm->SpreadCycle); AnsBuf=GetGadgetInput(AnswerBuf, PaceString,SpreadPeriodString); RhythmForm->SpreadCycle=atof(AnsBuf); break; case 3: /* mean Phase*/ RhythmForm->CenterPhase= GetPhaseInput(RhythmForm->CenterPhase,RhythmMean); break; case 4: /* Mean Period*/ sprintf(AnswerBuf,"%2.0f",RhythmForm->CenterCycle); AnsBuf=GetGadgetInput(AnswerBuf, PaceString,MeanPeriodString); RhythmForm->CenterCycle=atof(AnsBuf); break; } break; case 4: /*Pitch*/ if (subitem != NOSUB) switch(subitem) { case 0: /* Randomize */ *PitchForm=Randomize_Parameter(); break; case 1: /* Spread Phase*/ PitchForm->SpreadPhase= GetPhaseInput(PitchForm->SpreadPhase, PitchSpread); break; case 2: /* Spread Period*/ sprintf(AnswerBuf,"%2.0f",PitchForm->SpreadCycle); AnsBuf=GetGadgetInput(AnswerBuf, PitchString,SpreadPeriodString); PitchForm->SpreadCycle=atof(AnsBuf); break; case 3: /* mean Phase*/ PitchForm->CenterPhase= GetPhaseInput(PitchForm->CenterPhase,PitchMean); break; case 4: /* Mean Period*/ sprintf(AnswerBuf,"%2.0f",PitchForm->CenterCycle); AnsBuf=GetGadgetInput(AnswerBuf, PitchString,MeanPeriodString); PitchForm->CenterCycle=atof(AnsBuf); break; } break; case 5: /*Duration*/ sprintf(AnswerBuf,"%2.0f",*Duration); AnsBuf=GetGadgetInput(AnswerBuf, BlankString,DurationString); *Duration=atof(AnsBuf); break; case 6: /*pulse*/ sprintf(AnswerBuf,"%d",*tempo); AnsBuf=GetGadgetInput(AnswerBuf, BlankString,PulseString); *tempo=abs(atoi(AnsBuf)); if (*tempo == 0) { DelayTicks=0; } else { DelayTicks=50/(*tempo); } break; case 7: /*ReDraw*/ DrawForm(*Duration,PitchForm,RhythmForm, DynamicsForm,TextureForm); break; case 8: /*numvoices*/ sprintf(AnswerBuf,"%d",numvoices); AnsBuf=GetGadgetInput(AnswerBuf, BlankString,VoiceString); tempint=atoi(AnsBuf); if((tempint <= MAXVOICE)&&(tempint > 0)) { numvoices=tempint; } else { DisplayBeep(NULL); } break; } /*switch itemnum*/ break; case SCALE: if (item != NOITEM) switch(item) { case 0: /*transpose*/ sprintf(AnswerBuf,"0"); AnsBuf=GetGadgetInput(AnswerBuf, BlankString,TransposeString); TransposeScale(atoi(AnsBuf),scale,range); break; default: range=InstallScale(12-item,scale); halfrange=range/2; } /*switch itemnum*/ break; case CHANNEL: GetChannelStuff(&Events[item],item); break; } /*switch menunum*/ } /*while menunull*/ break; case CLOSEWINDOW: quit=TRUE; break; } /*switch class*/ return (0); } /*end function*/