This is Reicher.m in view mode; [Download] [Up]
/* Generated by Interface Builder */
#import <objc/Object.h>
#import "Reicher.h"
@implementation Reicher:Object
#import <appkit/appkit.h>
#import <appkit/Panel.h> // For the NXAlert panel
#import <soundkit/soundkit.h>
#import <musickit/musickit.h>
#import <musickit/Conductor.h>
#import <musickit/Performer.h>
#import "FluteIns.h"
#import <math.h>
#import "RandomIzer.h"
// all the defined values, etc. are in this file
#include "Reichvals.h"
static Orchestra *theOrch;
static SynthInstrument *theIns[NUMFLUTES];
static id theRand;
static int going = 0;
static Note *theNote[NUMFLUTES];
static Note *theNoteOn[LBREATH*NUMFLUTES][ARRNOTES][NCYCS];
static Note *theNoteOff[LBREATH*NUMFLUTES][ARRNOTES][NCYCS];
static Note *theNoteRun[NUMFLUTES][ARRNOTES][RUNNOTES];
static Note *theNoteUpdate[NUMFLUTES];
static int PITCH[ARRNOTES];
static double AMP[ARRNOTES];
static double DUR[ARRNOTES];
int perform_switch = 0;
int numflutes = NUMFLUTES;
- setup
{
int MY_outAmp = [[Note class] parName: "MY_outAmp"];
int MY_dLineLength = [[Note class] parName: "MY_dLineLength"];
int MY_delay2Length = [[Note class] parName: "MY_delay2Length"];
double D1,D2;
double panval,pan_incr;
int j;
if (going == 0) {
theRand = [RandomIzer new];
[theRand setit];
theOrch = [Orchestra new];
if (![theOrch open]) {
NXRunAlertPanel("Shoot Fire...", "Can't open the DSP", "darn.", NULL, NULL, "is it clear?");
exit(1);
}
[theOrch setSamplingRate: 22050.0];
MKSetDeltaT(.01) ;
[Orchestra setFastResponse:YES];
[Orchestra setTimed:NO];
[Conductor setFinishWhenEmpty:NO];
[Conductor useSeparateThread:YES];
[Conductor setThreadPriority:1.0];
[theOrch run];
[Conductor startPerformance];
[Conductor lockPerformance];
// doesn't matter which notes here, will get updated later
D1 = MAXLENGTH;
D2 = MAXD2LENGTH;
panval = -45.0;
pan_incr = 90.0/(numflutes-1);
for(j = 0; j < numflutes; j++) {
theIns[j] = [SynthInstrument new];
[theIns[j] setSynthPatchClass:[FluteIns class]];
[theIns[j] setSynthPatchCount:1];
theNote[j] = [Note new];
[theNote[j] setNoteType:MK_noteOn];
[theNote[j] setNoteTag:MKNoteTag()];
[theNote[j] setPar:MK_amp0 toDouble:0.0];
[theNote[j] setPar:MK_amp1 toDouble:0.0];
[theNote[j] setPar:MY_outAmp toDouble:0.3];
[theNote[j] setPar:MK_bearing toDouble:panval];
panval += pan_incr;
[theNote[j] setPar:MY_dLineLength toDouble: (MINLENGTH + (MAXLENGTH - MINLENGTH) * D1)];
[theNote[j] setPar:MY_delay2Length toDouble: (MAXD2LENGTH * D2)];
[[theIns[j] noteReceiver] receiveNote:theNote[j]];
}
[Conductor unlockPerformance];
}
return(self);
}
- StartAndStop:sender
{
int i;
int MY_outAmp = [[Note class] parName: "MY_outAmp"];
if (going == 0) {
[self setup];
switch (perform_switch) {
case 0:
[self doPulsing];
break;
case 1:
[self doCanon];
break;
case 2:
[self doPhasing];
break;
case 3:
[self doInterlock];
break;
}
going = 1;
}
else {
[Conductor lockPerformance];
for (i = 0; i < NUMFLUTES; i++) {
theNoteOff[i][0][0] = [Note new];
[theNoteOff[i][0][0] setNoteType:MK_noteOff];
[theNoteOff[i][0][0] setNoteTag:[theNote[i] noteTag]];
[theNoteOff[i][0][0] setPar:MK_amp0 toDouble:0.0];
[theNoteOff[i][0][0] setPar:MK_amp1 toDouble:0.0];
[theNoteOff[i][0][0] setPar:MY_outAmp toDouble:0.0];
[[theIns[i] noteReceiver] receiveAndFreeNote:theNoteOff[i][0][0]];
[theIns[i] free];
}
[Conductor unlockPerformance];
[Conductor finishPerformance];
[theOrch flushTimedMessages];
[theOrch free];
going = 0;
}
return(self);
}
- doPulsing;
{
int MY_dLineLength = [[Note class] parName: "MY_dLineLength"];
int MY_noiseVolume = [[Note class] parName: "MY_noiseVolume"];
int MY_delay2Length = [[Note class] parName: "MY_delay2Length"];
int MY_envelopeSlew = [[Note class] parName: "MY_envelopeSlew"];
double playspeed = 0.23;
double notedur = 0.17;
double AMP1,AMP1incr;
double D1,D2;
int numpulses,halfway;
int Reichons[NUMNOTES];
int i,j;
int val_int;
int turnflag;
double val_double;
double playtime;
[Conductor lockPerformance]; /* Prepare to send MK message */
// choose notes
// first of all, clear out the placeholder array (for dups)
for (j = 0; j < NUMNOTES; j++) Reichons[j] = 0;
// now choose a unique note value for each flute
for(j = 0; j < NUMFLUTES; j++) {
do {
val_int = [theRand GetIndex:NUMNOTES-1];
}
while (Reichons[val_int] == 1);
Reichons[val_int] = 1;
D1 = Reichnotes[val_int][0];
D2 = Reichnotes[val_int][1];
// set up a Note with this note info and send it out --
// assumption here is that the -play method is queued to be called
// at the correct time to start a new chord
theNoteUpdate[j] = [Note new];
[theNoteUpdate[j] setNoteType:MK_noteUpdate];
[theNoteUpdate[j] setNoteTag:[theNote[j] noteTag]];
[theNoteUpdate[j] setPar:MY_dLineLength toDouble: (MINLENGTH + (MAXLENGTH - MINLENGTH) * D1)];
[theNoteUpdate[j] setPar:MY_delay2Length toDouble: (MAXD2LENGTH * D2)];
[theNoteUpdate[j] setPar:MK_amp1 toDouble:0.0];
[[theIns[j] noteReceiver] receiveAndFreeNote:theNoteUpdate[j]];
}
// at this point, the flutes all are set to the correct note
// now to decide the breath for each flute, and then set up the
// phrase
for (j = 0; j < NUMFLUTES; j++) {
// choose the number of pulses
numpulses = [theRand GetIndexRangeHi:LBREATH Lo:SBREATH];
halfway = numpulses/2;
turnflag = 0;
playtime = playspeed;
AMP1 = 0.4;
val_double = [theRand GetNumberRangeHi:0.9 Lo:0.8];
AMP1incr = (val_double - AMP1)/(double)numpulses;
for (i = 0; i < numpulses; i++) {
theNoteOn[j][0][0] = [Note new];
[theNoteOn[j][0][0] setNoteType:MK_noteUpdate];
[theNoteOn[j][0][0] setNoteTag:[theNote[j] noteTag]];
[theNoteOn[j][0][0] setPar:MK_amp1 toDouble:AMP1];
[theNoteOn[j][0][0] setPar:MY_envelopeSlew toDouble:0.001];
[theNoteOn[j][0][0] setPar:MY_noiseVolume toDouble:0.007];
[[theIns[j] noteReceiver] receiveAndFreeNote:theNoteOn[j][0][0] withDelay:playtime];
theNoteOff[j][0][0] = [Note new];
[theNoteOff[j][0][0] setNoteType:MK_noteUpdate];
[theNoteOff[j][0][0] setNoteTag:[theNote[j] noteTag]];
[theNoteOff[j][0][0] setPar:MK_amp1 toDouble:0.0];
[theNoteOff[j][0][0] setPar:MY_envelopeSlew toDouble:1.0];
[theNoteOff[j][0][0] setPar:MY_noiseVolume toDouble:0.001];
[[theIns[j] noteReceiver] receiveAndFreeNote:theNoteOff[j][0][0] withDelay:playtime+notedur];
playtime = playtime + (playspeed + [theRand GetNumber:0.01]);
if ((i > halfway) && (turnflag == 0)) {
AMP1incr *= -1.0;
turnflag = 1;
}
AMP1 = AMP1 + AMP1incr;
}
}
[[Conductor defaultConductor] sel:@selector(doPulsing) to:self withDelay:playtime argCount:0];
[Conductor unlockPerformance];
return self;
}
- doCanon
{
int MY_dLineLength = [[Note class] parName: "MY_dLineLength"];
int MY_noiseVolume = [[Note class] parName: "MY_noiseVolume"];
int MY_delay2Length = [[Note class] parName: "MY_delay2Length"];
int MY_envelopeSlew = [[Note class] parName: "MY_envelopeSlew"];
int i,j,k;
int nrunnotes;
double D1,D2;
double playtime,canon_delay;
static int long_incr;
static float longdur_mult;
static int reg_incr, reg_hi, reg_lo;
static float run_prob, runprob_incr, run_direction;
static float rest_prob, restprob_incr;
static float amp_hi, amphi_incr, amp_lo, amplo_incr;
double noiseval;
[Conductor lockPerformance];
// set up:
if (going == 0) {
// set up some initial flags
long_incr = LONGSTART;
longdur_mult = (float)[theRand GetIndexRangeHi:10 Lo:6];
reg_incr = REGSTART;
reg_hi = 4;
reg_lo = 0;
runprob_incr = RUNPROBINCR;
run_prob = 0.0;
run_direction = UP;
restprob_incr = 0.0 - RESTPROBINCR;
rest_prob = 0.4;
amphi_incr = AMPHIINCR;
amp_hi = 0.8;
amplo_incr = AMPLOINCR;
amp_lo = 0.5;
}
// load up the PITCH, AMP, and DUR arrays with values:
for (j = 0; j < ARRNOTES; j++) {
// select pitch indices
if (reg_incr < REGSECTDUR) {
PITCH[j] = [theRand GetIndexRangeHi:reg_hi Lo:reg_lo];
if (reg_incr < 0) {
reg_incr = REGSTART;
if ([theRand GetNumber] < 0.5) {
reg_hi = 4;
reg_lo = 0;
}
else {
reg_hi = 16;
reg_lo = 11;
}
}
}
else {
PITCH[j] = [theRand GetIndex:16];
}
AMP[j] = [theRand GetNumberRangeHi:amp_hi Lo:amp_lo];
if ([theRand GetNumber] < rest_prob) {
AMP[j] = 0.0;
}
DUR[j] = Beats[ [theRand GetIndex:3] ];
if (long_incr < LONGSECTDUR) {
DUR[j] *= longdur_mult;
if (long_incr < 0) {
long_incr = LONGSTART;
longdur_mult = (float)[theRand GetIndexRangeHi:10 Lo:6];
}
}
}
reg_incr--;
long_incr--;
run_prob += runprob_incr;
if ( (run_prob > 1.0) || (run_prob < 0.0) )
runprob_incr = 0.0 - runprob_incr;
rest_prob += restprob_incr;
if ( (rest_prob > 0.4) || (rest_prob < 0.0) )
restprob_incr = 0.0 -restprob_incr;
amp_hi += amphi_incr;
if ( (amp_hi > 0.95) || (amp_hi < 0.75) )
amphi_incr = 0.0 - amphi_incr;
amp_lo += amplo_incr;
if ( (amp_lo > 0.65) || (amp_lo < 0.4) )
amplo_incr = 0.0 - amplo_incr;
playtime = 0.0;
for (j = 0; j < ARRNOTES; j++) {
// test and possibly do a short run here
if ( [theRand GetNumber] < run_prob ) {
// choose the number of notes in the run
nrunnotes = [theRand GetIndexRangeHi:RUNNOTES Lo:1];
// choose the run-note duration
if ( [theRand GetNumber] > 0.9 ) {
DUR[j] = SIXTEENTH;
}
else {
DUR[j] = EIGHTH;
}
// choose the run direction
if (run_prob < 0.9) {
if ( [theRand GetNumber] > 0.5 ) {
run_direction = UP;
}
else {
run_direction = DOWN;
}
}
else { // I like the sound of this
DUR[j] = SIXTEENTH;
nrunnotes = 4;
}
// now make the runs!
for (k = 0; k < nrunnotes; k++) {
if (run_direction == UP) {
if (PITCH[j] > 12) PITCH[j] = [theRand GetIndex:12];
D1 = Reichnotes[PITCH[j]+k][0];
D2 = Reichnotes[PITCH[j]+k][1];
}
else {
if (PITCH[j] < 5) PITCH[j] = [theRand GetIndexRangeHi:16 Lo:5];
D1 = Reichnotes[PITCH[j]-k][0];
D2 = Reichnotes[PITCH[j]-k][1];
}
canon_delay = 0.0;
for (i = 0; i < NUMFLUTES; i++) {
theNoteRun[i][j][k] = [Note new];
[theNoteRun[i][j][k] setNoteType:MK_noteUpdate];
[theNoteRun[i][j][k] setNoteTag:[theNote[i] noteTag]];
[theNoteRun[i][j][k] setPar:MK_amp1 toDouble:AMP[j]];
[theNoteRun[i][j][k] setPar:MY_envelopeSlew toDouble:0.001];
noiseval = [theRand GetNumberRangeHi:0.035 Lo:0.015];
[theNoteRun[i][j][k] setPar:MY_noiseVolume toDouble:noiseval];
D1 += [theRand GetPlusMinus:0.0001];
D2 += [theRand GetPlusMinus:0.0001];
[theNoteRun[i][j][k] setPar:MY_dLineLength toDouble: (MINLENGTH + (MAXLENGTH - MINLENGTH) * D1)];
[theNoteRun[i][j][k] setPar:MY_delay2Length toDouble: (MAXD2LENGTH * D2)];
[[theIns[i] noteReceiver] receiveAndFreeNote:theNoteRun[i][j][k] withDelay:playtime+canon_delay];
canon_delay += (CANONPT + [theRand GetNumber:0.001]);
}
playtime += DUR[j];
}
}
// play a "normal" note
else {
canon_delay = 0.0;
for (i = 0; i < NUMFLUTES; i++) {
theNoteOn[i][j][0] = [Note new];
[theNoteOn[i][j][0] setNoteType:MK_noteUpdate];
[theNoteOn[i][j][0] setNoteTag:[theNote[i] noteTag]];
[theNoteOn[i][j][0] setPar:MK_amp1 toDouble:AMP[j]];
[theNoteOn[i][j][0] setPar:MY_envelopeSlew toDouble:0.001];
noiseval = [theRand GetNumberRangeHi:0.035 Lo:0.015];
[theNoteOn[i][j][0] setPar:MY_noiseVolume toDouble:noiseval];
D1 = Reichnotes[PITCH[j]][0];
D2 = Reichnotes[PITCH[j]][1];
D1 += [theRand GetPlusMinus:0.0001];
D2 += [theRand GetPlusMinus:0.0001];
[theNoteOn[i][j][0] setPar:MY_dLineLength toDouble: (MINLENGTH + (MAXLENGTH - MINLENGTH) * D1)];
[theNoteOn[i][j][0] setPar:MY_delay2Length toDouble: (MAXD2LENGTH * D2)];
[[theIns[i] noteReceiver] receiveAndFreeNote:theNoteOn[i][j][0] withDelay:playtime+canon_delay];
theNoteOff[i][j][0] = [Note new];
[theNoteOff[i][j][0] setNoteType:MK_noteUpdate];
[theNoteOff[i][j][0] setNoteTag:[theNote[i] noteTag]];
[theNoteOff[i][j][0] setPar:MK_amp1 toDouble:0.0];
[theNoteOff[i][j][0] setPar:MY_envelopeSlew toDouble:0.001];
[theNoteOff[i][j][0] setPar:MY_noiseVolume toDouble:0.0];
[[theIns[i] noteReceiver] receiveAndFreeNote:theNoteOff[i][j][0] withDelay:playtime+canon_delay+DUR[j]];
canon_delay += (CANONPT + [theRand GetNumber:0.001]);
}
playtime = 0.2 + playtime + (DUR[j] + [theRand GetNumber:0.01]);
}
}
[[Conductor defaultConductor] sel:@selector(doCanon) to:self withDelay:playtime argCount:0];
[Conductor unlockPerformance];
return self;
}
- doPhasing
{
int MY_dLineLength = [[Note class] parName: "MY_dLineLength"];
int MY_noiseVolume = [[Note class] parName: "MY_noiseVolume"];
int MY_delay2Length = [[Note class] parName: "MY_delay2Length"];
int MY_envelopeSlew = [[Note class] parName: "MY_envelopeSlew"];
double D1,D2;
int i,j;
double playtime;
static double phase_delay;
static int slurflags[ARRNOTES];
float noiseval;
[Conductor lockPerformance];
if (going == 0) {
// load up the PITCH, AMP, slurflags, and DUR arrays with values:
for (j = 0; j < ARRNOTES; j++) {
// select pitch indices from all the notes
PITCH[j] = [theRand GetIndex:17];
AMP[j] = [theRand GetNumberRangeHi:0.99 Lo:0.7];
if (PITCH[j] > 15) {
PITCH[j] = 0;
AMP[j] = 0.0;
}
DUR[j] = Beats[ [theRand GetIndex:3] ];
slurflags[j] = 0;
if ( [theRand GetNumber] < SLURPROB) slurflags[j] = 1;
}
phase_delay = 0.0;
}
// queue up the notes for the two flutes
playtime = 0.0;
for (j = 0; j < ARRNOTES; j++) {
for (i = 0; i < numflutes; i++) {
theNoteOn[i][j][0] = [Note new];
[theNoteOn[i][j][0] setNoteType:MK_noteUpdate];
[theNoteOn[i][j][0] setNoteTag:[theNote[i] noteTag]];
[theNoteOn[i][j][0] setPar:MK_amp1 toDouble:AMP[j]];
[theNoteOn[i][j][0] setPar:MY_envelopeSlew toDouble:0.001];
noiseval = [theRand GetNumberRangeHi:0.035 Lo:0.015];
[theNoteOn[i][j][0] setPar:MY_noiseVolume toDouble:noiseval];
D1 = Reichnotes[PITCH[j]][0];
D2 = Reichnotes[PITCH[j]][1];
D1 += [theRand GetPlusMinus:0.0001];
D2 += [theRand GetPlusMinus:0.0001];
[theNoteOn[i][j][0] setPar:MY_dLineLength toDouble: (MINLENGTH + (MAXLENGTH - MINLENGTH) * D1)];
[theNoteOn[i][j][0] setPar:MY_delay2Length toDouble: (MAXD2LENGTH * D2)];
[[theIns[i] noteReceiver] receiveAndFreeNote:theNoteOn[i][j][0] withDelay:playtime + ((float)i * phase_delay)];
if (slurflags[j] == 0) {
theNoteOff[i][j][0] = [Note new];
[theNoteOff[i][j][0] setNoteType:MK_noteUpdate];
[theNoteOff[i][j][0] setNoteTag:[theNote[i] noteTag]];
[theNoteOff[i][j][0] setPar:MK_amp1 toDouble:0.0];
[theNoteOff[i][j][0] setPar:MY_envelopeSlew toDouble:0.001];
[theNoteOff[i][j][0] setPar:MY_noiseVolume toDouble:0.0];
[[theIns[i] noteReceiver] receiveAndFreeNote:theNoteOff[i][j][0] withDelay:playtime+ ((float)i * phase_delay) + DUR[j]];
}
}
playtime = 0.2 + playtime + (DUR[j] + [theRand GetNumber:0.01]);
}
phase_delay += (PHASEINCR + [theRand GetNumber:0.001]);
[[Conductor defaultConductor] sel:@selector(doPhasing) to:self withDelay:playtime argCount:0];
[Conductor unlockPerformance];
return self;
}
-doInterlock
{
int MY_dLineLength = [[Note class] parName: "MY_dLineLength"];
int MY_noiseVolume = [[Note class] parName: "MY_noiseVolume"];
int MY_delay2Length = [[Note class] parName: "MY_delay2Length"];
int MY_envelopeSlew = [[Note class] parName: "MY_envelopeSlew"];
int i,j,k;
double playtime;
static int reg_incr, reg_hi, reg_lo;
static float amp_hi, amphi_incr, amp_lo, amplo_incr;
float noiseval;
double D1,D2;
int cycles;
float cycle_delay;
[Conductor lockPerformance];
if (going == 0) {
// set up some initial flags
reg_incr = REGSTART;
reg_hi = 4;
reg_lo = 0;
amphi_incr = AMPHIINCR;
amp_hi = 0.8;
amplo_incr = AMPLOINCR;
amp_lo = 0.5;
}
for (i = 0; i < NUMFLUTES; i++) {
// load up the PITCH, AMP, and DUR arrays with new values:
for (j = 0; j < CYCARRNOTES; j++) {
// select pitch indices
if (reg_incr < REGSECTDUR) {
PITCH[j] = [theRand GetIndexRangeHi:reg_hi Lo:reg_lo];
if (reg_incr < 0) {
reg_incr = REGSTART;
if ([theRand GetNumber] < 0.5) {
reg_hi = 4;
reg_lo = 0;
}
else {
reg_hi = 16;
reg_lo = 12;
}
}
}
else {
PITCH[j] = [theRand GetIndex:16];
}
AMP[j] = [theRand GetNumberRangeHi:amp_hi Lo:amp_lo];
DUR[j] = Beats[ [theRand GetIndex:3] ];
}
cycles = [theRand GetIndexRangeHi:NCYCS Lo:3];
cycle_delay = (CYCMEASURE * (float)(i * 4)) + (QUARTER * (float)i);
for (k = 0; k < cycles; k++) {
playtime = 0.0;
for (j = 0; j < CYCARRNOTES; j++) {
theNoteOn[i][j][k] = [Note new];
[theNoteOn[i][j][k] setNoteType:MK_noteUpdate];
[theNoteOn[i][j][k] setNoteTag:[theNote[i] noteTag]];
[theNoteOn[i][j][k] setPar:MK_amp1 toDouble:AMP[j]];
[theNoteOn[i][j][k] setPar:MY_envelopeSlew toDouble:0.001];
noiseval = [theRand GetNumberRangeHi:0.035 Lo:0.015];
[theNoteOn[i][j][k] setPar:MY_noiseVolume toDouble:noiseval];
D1 = Reichnotes[PITCH[j]][0];
D2 = Reichnotes[PITCH[j]][1];
D1 += [theRand GetPlusMinus:0.0001];
D2 += [theRand GetPlusMinus:0.0001];
[theNoteOn[i][j][k] setPar:MY_dLineLength toDouble: (MINLENGTH + (MAXLENGTH - MINLENGTH) * D1)];
[theNoteOn[i][j][k] setPar:MY_delay2Length toDouble: (MAXD2LENGTH * D2)];
[[theIns[i] noteReceiver] receiveAndFreeNote:theNoteOn[i][j][k] withDelay:playtime+cycle_delay];
theNoteOff[i][j][k] = [Note new];
[theNoteOff[i][j][k] setNoteType:MK_noteUpdate];
[theNoteOff[i][j][k] setNoteTag:[theNote[i] noteTag]];
[theNoteOff[i][j][k] setPar:MK_amp1 toDouble:0.0];
[theNoteOff[i][j][k] setPar:MY_envelopeSlew toDouble:0.001];
[theNoteOff[i][j][k] setPar:MY_noiseVolume toDouble:0.0];
[[theIns[i] noteReceiver] receiveAndFreeNote:theNoteOff[i][j][k] withDelay:playtime+cycle_delay+DUR[j]];
playtime = playtime + (DUR[j] + [theRand GetNumber:0.01]);
}
cycle_delay += (CYCMEASURE + [theRand GetNumber:0.001]);
}
}
reg_incr--;
amp_hi += amphi_incr;
if ( (amp_hi > 0.95) || (amp_hi < 0.75) )
amphi_incr = 0.0 - amphi_incr;
amp_lo += amplo_incr;
if ( (amp_lo > 0.65) || (amp_lo < 0.4) )
amplo_incr = 0.0 - amplo_incr;
[[Conductor defaultConductor] sel:@selector(doInterlock) to:self withDelay:(CYCMEASURE * 7) argCount:0];
[Conductor unlockPerformance];
return self;
}
- setPulsing:sender
{
perform_switch = 0;
numflutes = NUMFLUTES;
return(self);
}
- setCanon:sender
{
perform_switch = 1;
numflutes = NUMFLUTES;
return(self);
}
- setPhasing:sender
{
perform_switch = 2;
numflutes = 2;
return(self);
}
- setInterlock:sender
{
perform_switch = 3;
numflutes = NUMFLUTES;
return(self);
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.