|
|
1.1 ! root 1: /* ! 2: * SoundEffect.m, class to play sounds ! 3: * Originally by Terry Donahue, modified by Ali Ozer ! 4: * ! 5: * SoundEffect is a class which conveniently groups the 3.0 ! 6: * sound stream functionality with sound data using the Sound ! 7: * class. ! 8: * ! 9: * You may freely copy, distribute and reuse the code in this example. ! 10: * NeXT disclaims any warranty of any kind, expressed or implied, ! 11: * as to its fitness for any particular use. ! 12: */ ! 13: ! 14: #import "SoundEffect.h" ! 15: #import <soundkit/NXSoundOut.h> ! 16: #import <appkit/nextstd.h> ! 17: #import <objc/List.h> ! 18: ! 19: @implementation SoundEffect ! 20: ! 21: static BOOL soundEnabled = NO; ! 22: ! 23: #define DEFAULTMAXSOUNDSTREAMS 20 ! 24: ! 25: static List *soundStreams = nil; // List of currently idle sound streams ! 26: static unsigned int soundStreamsAllocated = 0; // Total number of sound streams allocated ! 27: static unsigned int maxSoundStreams = DEFAULTMAXSOUNDSTREAMS; // Max allowed ! 28: ! 29: // After calling this, you may call soundEnabled to check to see if it was successful. ! 30: ! 31: + (void)setSoundEnabled:(BOOL)flag ! 32: { ! 33: if (flag && !soundEnabled) { ! 34: NXPlayStream *testStream = [self soundStream]; ! 35: if (testStream) { ! 36: soundEnabled = YES; ! 37: [self releaseSoundStream:testStream]; ! 38: } else { ! 39: NXLogError ("Can't enable sounds."); ! 40: } ! 41: } else if (!flag && soundEnabled) { ! 42: soundEnabled = flag; ! 43: soundStreamsAllocated -= [soundStreams count]; ! 44: [soundStreams freeObjects]; ! 45: } ! 46: } ! 47: ! 48: + (BOOL)soundEnabled ! 49: { ! 50: return soundEnabled; ! 51: } ! 52: ! 53: // These two methods let the client set/get the maximum number of ! 54: // sound streams to allocate. If this number is exceeded, sound requests ! 55: // are simply not honored until sound streams are freed up. ! 56: ! 57: + (void)setMaxSoundStreams:(unsigned int)max ! 58: { ! 59: maxSoundStreams = max; ! 60: } ! 61: ! 62: + (unsigned int)maxSoundStreams ! 63: { ! 64: return maxSoundStreams; ! 65: } ! 66: ! 67: // This method returns a sound stream to be used in playing a sound. ! 68: // Sound streams allocated through this method should be given back ! 69: // via releaseSoundStream:. Note that this is for internal use only; ! 70: // it however might be overridden if necessary. ! 71: ! 72: + (NXPlayStream *)soundStream ! 73: { ! 74: static BOOL cantPlaySounds = NO; ! 75: static NXSoundOut *dev = nil; // We only have one instance of this... ! 76: NXPlayStream *newStream = nil; ! 77: ! 78: if (cantPlaySounds) return nil; // If we've tried before and failed, just give up. ! 79: ! 80: if (!dev && !(dev = [[NXSoundOut alloc] init])) { // We allocate this from the default zone so that ! 81: NXLogError ("Couldn't create NXSoundOut"); // freeing this zone won't accidentally blast it ! 82: cantPlaySounds = YES; ! 83: return nil; ! 84: } ! 85: ! 86: if (!soundStreams) { ! 87: soundStreams = [[List alloc] init]; ! 88: } ! 89: ! 90: if (![soundStreams count]) { ! 91: if (soundStreamsAllocated < maxSoundStreams) { ! 92: newStream = [[NXPlayStream alloc] initOnDevice:dev]; ! 93: soundStreamsAllocated++; ! 94: } ! 95: } else { ! 96: newStream = [soundStreams removeLastObject]; ! 97: } ! 98: ! 99: if (newStream) { ! 100: if (![newStream isActive] && ([newStream activate] != NX_SoundDeviceErrorNone)) { ! 101: [newStream free]; ! 102: newStream = nil; ! 103: soundStreamsAllocated--; ! 104: } ! 105: } ! 106: ! 107: return newStream; ! 108: } ! 109: ! 110: // When a sound stream is released, put it on the idle list unless sounds were disabled; ! 111: // then just free it. ! 112: ! 113: + (void)releaseSoundStream:(NXPlayStream *)soundStream ! 114: { ! 115: if ([self soundEnabled]) { ! 116: [soundStreams addObject:soundStream]; ! 117: } else { ! 118: [soundStream free]; // This also deactivates. ! 119: soundStreamsAllocated--; ! 120: } ! 121: } ! 122: ! 123: // This method lets you create new instances of SoundEffect. If the specified ! 124: // sound file does not exist, the allocated instance is freed and nil is returned. ! 125: ! 126: - initFromSection:(const char *)path ! 127: { ! 128: [super init]; ! 129: ! 130: if (!(sound = [[Sound allocFromZone:[self zone]] initFromSection:path])) { ! 131: NXLogError ("Couldn't load sound from %s", path); ! 132: [self free]; ! 133: return nil; ! 134: } ! 135: ! 136: return self; ! 137: } ! 138: ! 139: // Free frees the SoundEffect. If this sound effect is being played at the time, ! 140: // the free is delayed and happens as soon as all pending sounds are finished. ! 141: ! 142: - free ! 143: { ! 144: if (flags.refCount) { ! 145: flags.freeWhenDone = YES; ! 146: return nil; ! 147: } else { ! 148: if (sound) [sound free]; ! 149: return [super free]; ! 150: } ! 151: } ! 152: ! 153: // These two methods play the sound effect. ! 154: ! 155: - play ! 156: { ! 157: return [self play:1.0 pan:0.0]; ! 158: } ! 159: ! 160: - play:(float)volume pan:(float)pan ! 161: { ! 162: float left, right; ! 163: NXPlayStream *soundStream; ! 164: ! 165: if (![[self class] soundEnabled]) { ! 166: return self; ! 167: } ! 168: ! 169: if (!(soundStream = [[self class] soundStream])) { ! 170: return self; ! 171: } ! 172: ! 173: [soundStream setDelegate:self]; ! 174: ! 175: left = right = volume; ! 176: if (pan > 0.0) left *= 1.0 - pan; ! 177: else if (pan < 0.0) right *= 1.0 + pan; ! 178: [soundStream setGainLeft:left right:right]; ! 179: if ([soundStream playBuffer:(void *)[sound data] ! 180: size:(unsigned int)[sound dataSize] ! 181: tag:0 ! 182: channelCount:(unsigned int)[sound channelCount] ! 183: samplingRate:[sound samplingRate]] == NX_SoundDeviceErrorNone) { ! 184: flags.refCount++; ! 185: } else { ! 186: [[self class] releaseSoundStream:soundStream]; ! 187: } ! 188: ! 189: return self; ! 190: } ! 191: ! 192: // Delegate methods for internal use only. ! 193: ! 194: - soundStream:sender didCompleteBuffer:(int)tag ! 195: { ! 196: flags.refCount--; ! 197: [[self class] releaseSoundStream:sender]; ! 198: if (flags.freeWhenDone && flags.refCount == 0) { ! 199: [self free]; ! 200: } ! 201: return sender; ! 202: } ! 203: ! 204: - soundStreamDidAbort:sender deviceReserved:(BOOL)flag ! 205: { ! 206: return [self soundStream:sender didCompleteBuffer:0]; ! 207: } ! 208: ! 209: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.