Line data Source code
1 0 : /*
2 :
3 : OOALSoundChannel.m
4 :
5 : OOALSound - OpenAL sound implementation for Oolite.
6 : Copyright (C) 2006-2013 Jens Ayton
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : of this software and associated documentation files (the "Software"), to deal
10 : in the Software without restriction, including without limitation the rights
11 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : copies of the Software, and to permit persons to whom the Software is
13 : furnished to do so, subject to the following conditions:
14 :
15 : The above copyright notice and this permission notice shall be included in all
16 : copies or substantial portions of the Software.
17 :
18 : THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : SOFTWARE.
25 :
26 : */
27 :
28 : #import "OOALSoundChannel.h"
29 : #import "OOALSound.h"
30 : #import "OOLogging.h"
31 : #import "OOMaths.h"
32 :
33 : @interface OOSoundChannel (Private)
34 :
35 0 : - (BOOL) enqueueBuffer:(OOSound *)sound;
36 0 : - (void) hasStopped;
37 0 : - (void) getNextSoundBuffer;
38 :
39 : @end
40 :
41 :
42 : @implementation OOSoundChannel
43 :
44 0 : - (id) init
45 : {
46 : if ((self = [super init]))
47 : {
48 : ALuint error;
49 : OOAL(alGenSources(1,&_source));
50 : if ((error = alGetError()) != AL_NO_ERROR)
51 : {
52 : OOLog(kOOLogSoundInitError, @"%@", @"Could not create OpenAL source");
53 : [self release];
54 : self = nil;
55 : }
56 : else
57 : {
58 : // sources are all relative to listener, defaulting to zero vector
59 : OOAL(alSourcei(_source, AL_SOURCE_RELATIVE, AL_TRUE));
60 : OOAL(alSource3f(_source, AL_POSITION, 0.0f, 0.0f, 0.0f));
61 : }
62 : }
63 : return self;
64 : }
65 :
66 :
67 0 : - (void) dealloc
68 : {
69 : [self hasStopped]; // make sure buffers are dequeued and deleted
70 : OOAL(alDeleteSources(1, &_source));
71 : [super dealloc];
72 : }
73 :
74 : - (void) update
75 : {
76 : // Check if we've reached the end of a sound.
77 : if (_sound != nil)
78 : {
79 : ALint check;
80 : OOAL(alGetSourcei(_source,AL_SOURCE_STATE,&check));
81 : if (check == AL_STOPPED)
82 : {
83 : [self hasStopped];
84 : }
85 : else if ([_sound soundIncomplete]) // streaming and not finished loading
86 : {
87 : OOLog(@"sound.buffer", @"Incomplete, trying next for %@", [_sound name]);
88 : [self getNextSoundBuffer];
89 : }
90 : else if (_loop)
91 : {
92 : OOLog(@"sound.buffer", @"Looping, trying restart for %@", [_sound name]);
93 : // sound is complete, but needs to be looped, so start it again
94 : [_sound rewind];
95 : [self getNextSoundBuffer];
96 : }
97 : }
98 : }
99 :
100 :
101 0 : - (void) getNextSoundBuffer
102 : {
103 : if (!_bigSound)
104 : {
105 : // we've only loaded one buffer so far
106 : _bigSound = YES;
107 : _lastBuffer = _buffer;
108 : [self enqueueBuffer:_sound];
109 : }
110 : else
111 : {
112 : // _lastBuffer has something in it, so only queue up
113 : // another one if we've finished with that
114 : ALint processed = 0;
115 : OOAL(alGetSourcei(_source, AL_BUFFERS_PROCESSED, &processed));
116 : if (processed > 0) // slot free
117 : {
118 : // dequeue and delete lastBuffer
119 : ALuint buffer;
120 : OOAL(alSourceUnqueueBuffers(_source, 1, &buffer));
121 : assert(buffer == _lastBuffer);
122 : OOAL(alDeleteBuffers(1,&_lastBuffer));
123 : // shuffle along, and grab the next bit
124 : _lastBuffer = _buffer;
125 : [self enqueueBuffer:_sound];
126 : }
127 : }
128 : }
129 :
130 :
131 : - (void) setDelegate:(id)delegate
132 : {
133 : _delegate = delegate;
134 : }
135 :
136 :
137 : - (OOSoundChannel *) next
138 : {
139 : return _next;
140 : }
141 :
142 :
143 : - (void) setNext:(OOSoundChannel *)next
144 : {
145 : _next = next;
146 : }
147 :
148 :
149 : - (void) setPosition:(Vector) vector
150 : {
151 : OOAL(alSource3f(_source, AL_POSITION, vector.x, vector.y, vector.z));
152 : }
153 :
154 :
155 : - (void) setGain:(float) gain
156 : {
157 : OOAL(alSourcef(_source, AL_GAIN, gain));
158 : }
159 :
160 :
161 : - (BOOL) playSound:(OOSound *)sound looped:(BOOL)loop
162 : {
163 : if (sound == nil) return NO;
164 :
165 : if (_sound != nil) [self stop];
166 :
167 : _loop = loop;
168 : _bigSound = NO;
169 : [sound rewind];
170 : if ([self enqueueBuffer:sound])
171 : {
172 : _sound = [sound retain];
173 : return YES;
174 : }
175 : else
176 : {
177 : return NO;
178 : }
179 : }
180 :
181 :
182 : - (void) stop
183 : {
184 : if (_sound != nil)
185 : {
186 : OOAL(alSourceStop(_source));
187 : OOAL(alSourcei(_source, AL_BUFFER, AL_NONE));
188 : [self hasStopped];
189 : }
190 : }
191 :
192 :
193 0 : - (void) hasStopped
194 : {
195 : ALint queued;
196 : OOAL(alGetSourcei(_source, AL_BUFFERS_QUEUED, &queued));
197 :
198 : while (queued--)
199 : {
200 : ALuint buffer;
201 : OOAL(alSourceUnqueueBuffers(_source, 1, &buffer));
202 : }
203 :
204 : OOAL(alDeleteBuffers(1,&_buffer));
205 : if (_bigSound)
206 : {
207 : // then we have two buffers to cleanup
208 : OOAL(alDeleteBuffers(1,&_lastBuffer));
209 : }
210 : _bigSound = NO;
211 :
212 :
213 : OOSound *sound = _sound;
214 : _sound = nil;
215 :
216 : if (nil != _delegate && [_delegate respondsToSelector:@selector(channel:didFinishPlayingSound:)])
217 : {
218 : [_delegate channel:self didFinishPlayingSound:sound];
219 : }
220 : [sound release];
221 : }
222 :
223 :
224 0 : - (BOOL) enqueueBuffer:(OOSound *)sound
225 : {
226 : // get sound data
227 : _buffer = [sound soundBuffer];
228 : // bind sound data to buffer
229 : OOAL(alSourceQueueBuffers(_source, 1, &_buffer));
230 : ALuint error;
231 : if ((error = alGetError()) != AL_NO_ERROR)
232 : {
233 : OOLog(@"ov.debug", @"Error %u queueing buffers (_source: %u (%p), _buffer: %u (%p))",
234 : error, _source, &_source, _buffer, &_buffer);
235 : return NO;
236 : }
237 : ALint playing = 0;
238 : OOAL(alGetSourcei(_source,AL_SOURCE_STATE,&playing));
239 : if (playing != AL_PLAYING)
240 : {
241 : OOAL(alSourcePlay(_source));
242 : if ((error = alGetError()) != AL_NO_ERROR)
243 : {
244 : OOLog(@"ov.debug",@"Error %d playing source",error);
245 : return NO;
246 : }
247 : }
248 : return YES;
249 : }
250 :
251 :
252 :
253 : - (OOSound *)sound
254 : {
255 : return _sound;
256 : }
257 :
258 : @end
|