This is MLPluckMaster.m in view mode; [Download] [Up]
// Copyright (1995) by melonSoft Ralf Suckow Berlin, All Rights Reserved // Note: sometimes we use the Class * type instead of id since // the appkit includes the soundkit, which has different definitions // for the activate and info methods. #import "MLPluckMaster.h" #import "MLParameters.h" #define PLUCK_ML_FILE_FORMAT "%lf %lf %lf\n" #define EXTENSION "pluck-ml" #define TMP_FILE "/tmp/plucktempfile_%s.snd" @implementation MLPluckMaster - appDidInit:sender { static char name[80]; sprintf (name, VOICE_PRODUCER_NAME, EXTENSION); [[NXConnection registerRoot:self withName:name] runFromAppKit]; // initialize self return self; } - showError:(const char *)text with:(const char *)parameter { NXRunAlertPanel ("Pluck Voice", text, NULL, NULL, NULL, parameter); return self; } - getPosition:(double *)position attenuation:(double *)attenuation durationFactor:(double *)durationFactor fromFile:(const char *)inputFile { // scanning the ASCII pluck-ml file // its syntax is extremely simple: just three floating point numbers, // one for the default position, one for the default attenuation, // and one for the default durationFactor NXStream * stream; // default values for the default values - if the scan is unsuccessful *position = 0.1; *attenuation = 0.5; *durationFactor = 1; NX_DURING stream = NXMapFile (inputFile, NX_READONLY); if (NXScanf (stream, PLUCK_ML_FILE_FORMAT, position, attenuation, durationFactor) != 3) [self showError:"bad content of file `%s'" with:inputFile]; NXCloseMemory (stream, NX_FREEBUFFER); NX_HANDLER [self showError:"I/O error -- Can't read from file `%s'" with:inputFile]; NX_ENDHANDLER return self; } - (double)getParameter:(const char *)name default:(double)dValue from:params { NXAtom atomName; atomName = NXUniqueString (name); if ([params contains:atomName]) return [params doubleValueFor:atomName]; else return dValue; } - restrict:(double *)value toMin:(double)min max:(double)max { if (*value < min) *value = min; if (*value > max) *value = max; return self; } - (BOOL)makeFile:(const char *)outputFile withFrequency:(double)frequency volume:(double)volume duration:(double)duration position:(double)position attenuation:(double)attenuation durationFactor:(double)durationFactor { int fd; SNDSoundStruct * soundStruct; int length; short int * buf; [self restrict:&frequency toMin: 10 max: 2500]; [self restrict:&volume toMin: 0 max: 1]; [self restrict:&duration toMin: 0 max: 20]; [self restrict:&position toMin: 0 max: 0.5]; [self restrict:&attenuation toMin:0.46 max: 0.5]; [self restrict:&durationFactor toMin: 0 max: 100]; // we use level 2 here since SNDWriteHeader uses it fd = open (outputFile, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd == -1) { [self showError:"I/O error -- Can't write to file `%s'" with:outputFile]; return NO; } // see MLPluckAlgorithm.m for the following method length = [self calculateSamples:&buf withFrequency:frequency volume:volume duration:duration position:position attenuation:attenuation durationFactor:durationFactor]; if (!buf || !length) { close (fd); return NO; } // write header SNDAlloc(&soundStruct, 0, SND_FORMAT_LINEAR_16, SND_RATE_HIGH, 1, 4); strcpy (soundStruct->info, "mls"); soundStruct->dataSize = length * sizeof (short int); SNDWriteHeader (fd, soundStruct); soundStruct->dataSize = 0; SNDFree (soundStruct); // write data SNDSwapHostToSound (buf, buf, length, 1, SND_FORMAT_LINEAR_16); write (fd, buf, length * sizeof (short int)); free (buf); close (fd); return YES; } - (BOOL)makeFile:(const char *)outputFile fromVoice:(const char *)inputFile parameters:parameters { double defaultPosition, defaultAttenuation, defaultDurationFactor; double position, attenuation, duration, frequency, volume, durationFactor; [self getPosition:&defaultPosition attenuation:&defaultAttenuation durationFactor:&defaultDurationFactor fromFile:inputFile]; position = [self getParameter:"position" default:defaultPosition from:parameters]; attenuation = [self getParameter:"attenuation" default:defaultAttenuation from:parameters]; durationFactor = [self getParameter:"durationFactor" default:defaultDurationFactor from:parameters]; frequency = [self getParameter:"freq" default:220 from:parameters]; volume = [self getParameter:"volume" default:0.5 from:parameters]; duration = [self getParameter:"duration" default:0.3 from:parameters]; return [self makeFile:outputFile withFrequency:frequency volume:volume duration:duration position:position attenuation:attenuation durationFactor:durationFactor]; } // <MLVoiceProduction> protocol - (oneway void)voice:(const char *)documentFile makeFile:(const char *)outputFile from:(const char *)parameters forClient:(id <MLProductionClient>)sender request:(int)number; { BOOL ok; id params; BOOL flag; params = [[MLParameters alloc] init]; if (![params readFromMathString:parameters syntaxOK:&flag]) ok = NO; else ok = [self makeFile:outputFile fromVoice:documentFile parameters:params]; [sender server:self didRequest:number success:(int)ok]; [params free]; // trying to free the strings -- does this always work ? if (documentFile) free ((char *)documentFile); if (outputFile) free ((char *)outputFile); if (parameters) free ((char *)parameters); } // support for double clicking the .pluck-ml file - opens the file in editor - (BOOL)appAcceptsAnotherFile:sender { return YES; } - (int)app:sender openFile:(const char *)path type:(const char *)type { double position, attenuation, durationFactor; [self getPosition:&position attenuation:&attenuation durationFactor:&durationFactor fromFile:path]; [positionField setDoubleValue:position]; [attenuationField setDoubleValue:attenuation]; [durationFactorField setDoubleValue:durationFactor]; [[positionField window] makeKeyAndOrderFront:self]; // [self perform:@selector(doOpen:) with:(id)NXUniqueString (path) // afterDelay:0 cancelPrevious:NO]; return YES; } // - doOpen:path // { // // It is a better idea to forward it to Edit even if somebody is // // using emacs or any other editor // // [[Application workspace] openFile:(const char *)path // withApplication:"Edit"]; // return self; // } - saveSound:sender { id savepanel; savepanel = [SavePanel new]; [savepanel setRequiredFileType:"snd"]; if ([savepanel runModal]) [self makeFile:[savepanel filename] withFrequency:[frequencyField doubleValue] volume:[volumeField doubleValue] duration:[durationField doubleValue] position:[positionField doubleValue] attenuation:[attenuationField doubleValue] durationFactor:[durationFactorField doubleValue]]; return self; } - saveSettings:sender { id savepanel; NXStream * stream; savepanel = [SavePanel new]; [savepanel setRequiredFileType:EXTENSION]; if ([savepanel runModal]) { NX_DURING stream = NXOpenMemory (NULL, 0, NX_WRITEONLY); NXPrintf (stream, PLUCK_ML_FILE_FORMAT, [positionField doubleValue], [attenuationField doubleValue], [durationFactorField doubleValue]); NXSaveToFile (stream, [savepanel filename]); NXCloseMemory (stream, NX_FREEBUFFER); NX_HANDLER [self showError:"I/O error -- Can't save to file `%s'" with:[savepanel filename]]; NX_ENDHANDLER } return self; } - play:sender { char path [MAXPATHLEN + 1]; int error; sprintf (path, TMP_FILE, NXUserName ()); if ([self makeFile:path withFrequency:[frequencyField doubleValue] volume:[volumeField doubleValue] duration:[durationField doubleValue] position:[positionField doubleValue] attenuation:[attenuationField doubleValue] durationFactor:[durationFactorField doubleValue]]) { error = SNDPlaySoundfile (path, 0); if (error != SND_ERR_NONE) [self showError:"Sound error: %s" with:SNDSoundError (error)]; } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.