|
|
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.