This is ChaosPerf.m in view mode; [Download] [Up]
/* Generated by Interface Builder */ #import "ChaosPerf.h" #import "ChaosPlot.h" #import "ScorePlot.h" #import "StanController.h" #import "HenonController.h" #import "LorenzController.h" #import "Formulas.h" #import <appkit/appkit.h> #import <musickit/musickit.h> #import <musickit/synthpatches/synthpatches.h> #import <math.h> // Sound modes #define DSP 0 #define MIDI 1 #define MUTE 2 // Run modes #define STOPPED 0 #define RUNNING 1 // Tuning modes #define TWELVE 0 #define INFINITE 1 // MK_delta_time_offset #define OFFSET_TIME 0.25 // Chaotic systems #define HENON 0 #define LORENZ 1 #define STANDARD 2 @implementation ChaosPerf - soundSwitch:sender { [Conductor lockPerformance]; [self pause]; switch (soundMode = [sender selectedTag]) { case DSP: nextNote = 1; break; case MIDI: nextNote = SOUNDSPLIT+1; break; case MUTE: break; } [cController setSoundMode:soundMode]; [myConductor emptyQueue]; if (runMode == RUNNING) [self resume]; [Conductor unlockPerformance]; return self; } - switchTuning:sender { int i; tuningMode = [sender selectedTag]; if (tuningMode == TWELVE) { for (i=5; i<=8; i++) { [aNoteUpdate[i] setPar:MK_pitchBend toInt:(unsigned)0x2000]; [aNoteSender[i] sendNote:aNoteUpdate[i]]; } } return self; } - startStop:sender { if (cController == nil) { NXRunAlertPanel("Not so fast!", "You must select a system first", "OK", 0, 0); return self; } [Conductor lockPerformance]; switch (runMode) { case STOPPED: runMode = RUNNING; [sender setTitle:"Stop"]; [self resume]; break; case RUNNING: runMode = STOPPED; [sender setTitle:"Start"]; [self pause]; break; } [Conductor unlockPerformance]; return self; } - clearPlot:sender { [cPlot display]; [cScorePlot display]; return self; } - windowWillClose:sender { plotting = 1; scoring = 0; return self; } // -perform gets notes to play and positions to plot by calling the // iterateFreq:Amp:Dur:Next:X:Y: method of the current system controller. // this method is required to return x and y plotting positions in the range // 0 to 1. - perform { float freq, amp, dur, next; float xpos, ypos; int ifreq /*, bndAmt */ ; struct ScoreArgs sc_args; if (![cController iterateFreq:&freq Amp:& Dur:&dur Next:&next X:&xpos Y:&ypos]) { [self pause]; runMode = STOPPED; [startButton setTitle:"Start"]; return nil; } // if ((freq > 20.0) && (freq < 8000.0) && (amp > 0.0)) { switch (soundMode) { case DSP: amp = MKdB(amp); if (++nextNote > NUMNOTES-SOUNDSPLIT) nextNote = 1; if (tuningMode != INFINITE) { ifreq = MKFreqToKeyNum((double)freq, NULL, 0.0); freq = MKKeyNumToFreq(ifreq); } [aNoteDur[nextNote] setDur:dur]; [aNoteDur[nextNote] setPar:MK_freq toDouble:(double)freq]; [aNoteDur[nextNote] setPar:MK_amp toDouble:(double)amp]; [aNoteSender[nextNote] sendNote:aNoteDur[nextNote]]; break; case MIDI: // MIDI output cycles between 4 channels to allow for separate pitchBend messages if (++nextNote > NUMNOTES) nextNote -= SOUNDSPLIT; ifreq = (int)freq; if (tuningMode == INFINITE) { [aNoteUpdate[nextNote] setPar:MK_pitchBend toInt:(unsigned)((freq - (float)ifreq) * (float)0x2000) + 0x2000]; [aNoteSender[nextNote] sendNote:aNoteUpdate[nextNote]]; } [aNoteDur[nextNote] setDur:dur]; [aNoteDur[nextNote] setPar:MK_keyNum toInt:ifreq]; [aNoteDur[nextNote] setPar:MK_velocity toInt:(int)amp]; [aNoteSender[nextNote] sendNote:aNoteDur[nextNote]]; break; case MUTE: break; } // } // DSP synthesis runs OFFSET_TIME behind realtime. Therefore plotting and scoring // messages during DSP are delayed by the same amount to synchronize sound with image. // Conductor class supplies a method for delayed massage passing. if (plotting) { if (soundMode != DSP) [cPlot plotPoint:xpos :ypos]; else { [cPlotStorage storeX:&xpos Y:&ypos]; [Conductor lockPerformance]; [myConductor sel:@selector(plotDelayedPoint) to:cPlot withDelay:OFFSET_TIME argCount:0]; [Conductor unlockPerformance]; } } if (scoring) { sc_args.start = [Conductor time]; sc_args.dur = dur; if (soundMode != DSP) { if (tuningMode != INFINITE) freq = (float)ifreq; sc_args.key = freq; sc_args.vel = amp; [cScorePlot drawNoteKey:&sc_args]; } else { // MKFreqToKeyNum produces mystifying results for bndAmt // sc_args.key = (float)MKFreqToKeyNum((double)freq, &bndAmt, 1.0); // if (tuningMode == INFINITE) sc_args.key += ((float)bndAmt/(float)0x3fff) - 0.5; if (tuningMode != INFINITE) sc_args.key = (float)ifreq; // use A440 (= 69) as reference else sc_args.key = ((log10(freq/440.)/log10(2.0)) * 12.0) + 69.0; sc_args.vel = (float)MKAmpAttenuationToMidi((double)amp); [cScoreStorage addElement:&sc_args]; [Conductor lockPerformance]; [myConductor sel:@selector(drawDelayedNoteKey) to:cScorePlot withDelay:OFFSET_TIME argCount:0]; [Conductor unlockPerformance]; } } if (next <= 0.0) next = 0.001; nextPerform = next; return self; } - showFormula:sender { [formulas setUp:sender]; return self; } - showScore:sender { [cScorePlot setUp]; scoring = 1; plotting = 0; return self; } - setSystem:sender { if (runMode == RUNNING) { runMode = STOPPED; [startButton setTitle:"Start"]; [self pause]; } [myConductor emptyQueue]; [cPlot display]; if (cController) [cController closeUp]; switch ([sender selectedTag]) { case HENON: cController = [[HenonController alloc] init]; [cPlot labelPlot:"Henon Map" Titlefont:[Font newFont:"Helvetica-Oblique" size:18] Xmax:"1.5" Xlabel:"X" Xmin:"-1.5" Ymax:"0.5" Ylabel:"Y" Ymin:"-0.5" axisFont:[Font newFont:"Helvetica" size:18]]; break; case LORENZ: cController = [[LorenzController alloc] initWithPlot:cPlot withConductor:myConductor]; // [cPlot labelPlot:"Lorenz System" Titlefont:[Font newFont:"Helvetica-Oblique" size:18] // Xmax:"30" Xlabel:"X" Xmin:"-30" // Ymax:"60" Ylabel:"Y" Ymin:"0" // axisFont:[Font newFont:"Helvetica" size:18]]; break; case STANDARD: cController = [[StanController alloc] init]; [cPlot labelPlot:"Standard Map" Titlefont:[Font newFont:"Helvetica-Oblique" size:18] Xmax:"2p" Xlabel:"q" Xmin:"0" Ymax:"2p" Ylabel:"I" Ymin:"0" axisFont:[Font newFont:"Symbol" size:18]]; break; } [cController setUp]; return self; } - appDidInit:sender { int i; plotting = 1; scoring = 0; soundMode = DSP; runMode = STOPPED; tuningMode = TWELVE; cController = nil; nextNote = 1; [cPlot setUp]; [cPlotStorage initCount:0 elementSize:sizeof(float) description:"f"]; [cScoreStorage initCount:0 elementSize:sizeof(struct ScoreArgs) description:@encode(struct ScoreArgs)]; formulas = [Formulas alloc]; // Set up DSP instruments. Default condition connects noteSenders to the DSP instrument. theOrch = [Orchestra new]; [theOrch setSamplingRate:44100.0]; [theOrch setFastResponse:YES]; [theOrch open]; dspIns = [[SynthInstrument alloc] init]; [dspIns setSynthPatchClass:[Pluck class]]; midiObj = [Midi new]; [midiObj setOutputTimed:NO]; /** aNoteSender[0] is not used. ** aNoteSender[1-4] are used for DSP. ** aNoteSender[5-8] are used for MIDI. **/ for (i=0; i<=NUMNOTES; i++) { aNoteSender[i] = [[NoteSender alloc] init]; [self addNoteSender:aNoteSender[i]]; aNoteUpdate[i] = [[Note alloc] init]; [aNoteUpdate[i] setNoteType:MK_noteUpdate]; [aNoteUpdate[i] setNoteTag:MKNoteTag()]; aNoteDur[i] = [[Note alloc] init]; [aNoteDur[i] setNoteType:MK_noteDur]; [aNoteDur[i] setNoteTag:MKNoteTag()]; if (i <= SOUNDSPLIT) { aNoteReceiver[i] = [[NoteReceiver alloc] init]; [dspIns addNoteReceiver:aNoteReceiver[i]]; [aNoteSender[i] connect:aNoteReceiver[i]]; [aNoteDur[i] setPar:MK_ampRel toDouble:0.25]; [aNoteDur[i] setPar:MK_sustain toDouble:0.5]; [aNoteDur[i] setPar:MK_pickNoise toDouble:0.01]; } else { [aNoteSender[i] connect:[midiObj channelNoteReceiver:i-SOUNDSPLIT]]; } } MKSetDeltaT(OFFSET_TIME); [Conductor setFinishWhenEmpty:NO]; [Conductor setClocked:YES]; myConductor = [[Conductor alloc] init]; [self activate]; [self pause]; [Conductor startPerformance]; [theOrch run]; [midiObj run]; return self; } - terminate:sender { [Conductor finishPerformance]; [theOrch close]; [midiObj close]; [NXApp terminate:self]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.