home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer) / NeXT_Developer-3.3.iso / NextDeveloper / Examples / AppKit / BreakApp / SoundEffect.m < prev    next >
Encoding:
Text File  |  1993-03-25  |  5.2 KB  |  210 lines

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