This is TunerApp.m in view mode; [Download] [Up]
/* TunerApp.m -- copyright 1992, 1993, 1994 by C.D.Lane */
#import "TunerApp.h"
#import "DefaultsTable.h"
#import "PopUpListPatch.h"
#import "Frequency.h"
#import "ZeroFrequency.h"
#import "FFTFrequency.h"
#define VERSION __DATE__
#define HELPFILE "Tuner"
#define DEFAULTSFILE "Defaults"
#define freq(k) MKKeyNumToFreq(k)
#define keyNum(f) MKFreqToKeyNum(f, NULL, 0.0)
#define TONES (12)
#define MSECPERSEC (1000)
@implementation TunerApp : Application
+ new
{
char pathnamebuf[MAXPATHLEN];
self = [super new];
bundle = [NXBundle bundleForClass:[self class]];
if ([bundle getPath:pathnamebuf forResource:DEFAULTSFILE ofType:"strings"])
[(defaults = [[DefaultsTable alloc] initFromFile:pathnamebuf]) registerDefaults:[self appName]];
return self;
}
- appDidInit:sender
{
[[[[soundView setAutoscale:YES] setDisplayMode:SK_DISPLAY_WAVE] setAutodisplay:YES] setSound:sound];
key = lastKey = c00k;
[methods addObject:[[ZeroFrequency alloc] init]];
[methods addObject:[[FFTFrequency alloc] init]];
[self loadDefaults:sender];
[sound perform:@selector(record:) with:sender afterDelay:0 cancelPrevious:YES];
return self;
}
- appWillTerminate:sender { return [self saveDefaults:sender]; }
- willRecord:sender
{
[soundMeter run:sender];
[sound perform:@selector(stop:) with:sender afterDelay:([timeSlider floatValue] * MSECPERSEC) cancelPrevious:YES];
return self;
}
- didRecord:sender
{
int status = SND_ERR_NONE;
id list = methodPopUpList;
amplitude = [[soundMeter stop:sender] peakValue];
frequency = FREQUENCY_UNSTABLE;
method = [methods objectAt:[list indexOfItem:[list selectedItem]]];
if((status = [sample copySound:sound]) == SND_ERR_NONE) {
if((status = [sound deleteSamples]) == SND_ERR_NONE) {
if(amplitude >= [squelchSlider floatValue]) {
if((status = [sample convertToFormat:SND_FORMAT_LINEAR_16]) == SND_ERR_NONE) frequency = [method computeFrequency:sample];
#ifdef DEBUG
else (void) fprintf(stderr, "convertToFormat: %s\n", SNDSoundError(status));
#endif
}
else [[[self clearNoteName] clearCentError] clearNoteGlyph];
}
#ifdef DEBUG
else (void) fprintf(stderr, "deleteSamples: %s\n", SNDSoundError(status));
#endif
}
#ifdef DEBUG
else (void) fprintf(stderr, "copySound: %s\n", SNDSoundError(status));;
#endif
if(frequency == FREQUENCY_ERROR) [self switchMethod:"Switching methods, selected method not available!"];
else if(frequency != FREQUENCY_UNSTABLE) {
frequency *= [adjustmentSlider floatValue];
#ifdef DEBUG
(void) fprintf(stderr, "amplitude = %f\nfrequency = %f\n\n", amplitude, frequency);
#endif
if((key = keyNum(frequency)) % TONES == lastKey) [[[self showNoteName] showCentError] showNoteGlyph];
else [[self clearNoteName] clearCentError];
NXPing();
lastKey = key % TONES;
}
[sound perform:@selector(record:) with:sender afterDelay:0 cancelPrevious:YES];
return self;
}
- openHelpPanel:sender
{
NXStream *stream;
static BOOL flag = NO;
char pathnamebuf[MAXPATHLEN];
if(!flag) {
if ([bundle getPath:pathnamebuf forResource:HELPFILE ofType:"rtf"]) {
if((stream = NXMapFile(pathnamebuf, NX_READONLY)) != NULL) {
[helpScrollView readRichText:stream];
NXCloseMemory(stream, NX_FREEBUFFER);
flag = YES;
}
else return nil;
}
else return nil;
}
[[helpScrollView window] makeKeyAndOrderFront:sender];
return self;
}
- showNoteName
{
id control, list = [buttonMatrix cellList];
unsigned int i, size, state, pitch = ((key + [transpositionField intValue]) % TONES);
for(i = 0, size = [list count]; i < size; i++) {
state = (pitch == [(control = [list objectAt:i]) tag]);
if([control state] != state) [[[control setEnabled:YES] setState:state] setEnabled:NO];
}
return self;
}
- clearNoteName
{
unsigned int i, size;
id control, list = [buttonMatrix cellList];
for(i = 0, size = [list count]; i < size; i++) {
if([(control = [list objectAt:i]) state]) [[[control setEnabled:YES] setState:NO] setEnabled:NO];
}
return self;
}
- showCentError
{
double zero, minimum, maximum, correct, calibration = [calibrationField floatValue] / freq(a4k);
double error, tolerance = [toleranceSlider floatValue] + [method toleranceAtFrequency:frequency];
correct = freq(key) * calibration;
minimum = freq(key - 1) * calibration;
maximum = freq(key + 1) * calibration;
[[[centSlider setMinValue:minimum] setMaxValue:maximum] setFloatValue:frequency];
[centSlider setEnabled:YES];
zero = (((error = frequency - correct) > 0.0) ? maximum - correct : correct - minimum) * tolerance;
[flat setState:(error <= zero)];
[sharp setState:(error >= -zero)];
[attune setState:([flat state] && [sharp state])];
return self;
}
- clearCentError
{
[[[[centSlider setMinValue:freq(af4k)] setMaxValue:freq(as4k)] setFloatValue:freq(a4k)] setEnabled:NO];
[flat setState:NO];
[sharp setState:NO];
[attune setState:NO];
return self;
}
#import "GlyphTable.h"
- showNoteGlyph
{
BOOL flag;
id cell, image;
struct entry *entry;
unsigned int i, j, count = [staffMatrix cellCount];
MKKeyNum index = key + [transpositionField intValue];
if(index < START || index > END) return self;
entry = &entries[i = (unsigned int) (END - index)];
if(entry->clef != NULL && (image = [NXImage findImageNamed:entry->clef]) != nil && [staffButton image] != image) {
[staffButton setImage:image];
}
for(j = 0, flag = NO; j < count; j++) {
if(flag || (flag = (entry->notes)[j] == NULL)) image = [NXImage findImageNamed:STAFF];
else if((image = [NXImage findImageNamed:(entry->notes)[j]]) == nil) continue;
if([(cell = [staffMatrix cellAt:0 :j]) image] != image) [cell setImage:image];
}
return self;
}
- clearNoteGlyph
{
id image = [NXImage findImageNamed:STAFF];
[[staffMatrix cellList] makeObjectsPerform:@selector(setImage:) with:image];
return self;
}
- switchMethod:(const char *) reason
{
const char *title = "Zero"; // should be [[methods itemAt:0] ...]
(void) NXRunAlertPanel([self appName], reason, NULL, NULL, NULL);
[methodButton setTitle:title];
[methodPopUpList selectItem:title];
return nil;
}
- setDefault:sender
{
[sender setTag:YES];
return self;
}
- setCalibration:sender
{
[calibrationField setIntValue:[[sender selectedCell] tag]];
return [self setDefault:sender];
}
- setTransposition:sender
{
[transpositionField setIntValue:[[sender selectedCell] tag]];
return [self setDefault:sender];
}
- loadDefaults:sender;
{
const char *string = getStringDefault("Method");
[[squelchSlider setTag:NO] setFloatValue:getFloatDefault("Squelch")];
[[timeSlider setTag:NO] setFloatValue:getFloatDefault("SampleTime")];
[[toleranceSlider setTag:NO] setFloatValue:getFloatDefault("Tolerance")];
[[adjustmentSlider setTag:NO] setFloatValue:getFloatDefault("Adjustment")];
[calibrationField setIntValue:getIntDefault("Calibration")];
[[calibrationMatrix setTag:NO] selectCellWithTag:[calibrationField intValue]];
[transpositionField setIntValue:getIntDefault("Transposition")];
[[transpositionMatrix setTag:NO] selectCellWithTag:[transpositionField intValue]];
if ([methodPopUpList indexOfItem:string] != -1) {
[methodButton setTitle:string];
[methodPopUpList selectItem:string];
}
[methodMatrix setTag:NO];
return self;
}
- saveDefaults:sender
{
if([squelchSlider tag]) (void) writeDefault("Squelch", [squelchSlider stringValue]);
if([timeSlider tag]) (void) writeDefault("SampleTime", [timeSlider stringValue]);
if([toleranceSlider tag]) (void) writeDefault("Tolerance", [toleranceSlider stringValue]);
if([adjustmentSlider tag]) (void) writeDefault("Adjustment", [adjustmentSlider stringValue]);
if([calibrationMatrix tag]) (void) writeDefault("Calibration", [calibrationField stringValue]);
if([transpositionMatrix tag]) (void) writeDefault("Transposition", [transpositionField stringValue]);
if([methodMatrix tag]) (void) writeDefault("Method", [[methodMatrix selectedCell] title]);
return self;
}
- resetDefaults:sender
{
[defaults updateDefaults];
return [self loadDefaults:sender];
}
- setMethodPopUpList:anObject
{
[[(methodPopUpList = anObject) setTarget:self] setAction:@selector(setDefault:)];
methodMatrix = [methodPopUpList itemList];
return self;
}
- setVersion:anObject
{
[(version = anObject) setStringValue:VERSION];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.