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.