1 //------------------------------------------------------------------------------
2 // Copyright (c) 2001-2002, Haiku
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21 //
22 // File Name: BGameSoundDevice.cpp
23 // Author: Christopher ML Zumwalt May (zummy@users.sf.net)
24 // Description: Manages the game producer. The class may change with out
25 // notice and was only inteneded for use by the GameKit at
26 // this time. Use at your own risk.
27 //------------------------------------------------------------------------------
28
29
30 #include "GameSoundDevice.h"
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35
36 #include <Autolock.h>
37 #include <List.h>
38 #include <Locker.h>
39 #include <MediaAddOn.h>
40 #include <MediaRoster.h>
41 #include <MediaTheme.h>
42 #include <TimeSource.h>
43
44 #include "GameSoundBuffer.h"
45 #include "GameProducer.h"
46 #include "GSUtility.h"
47
48
49 // BGameSoundDevice definitions ------------------------------------
50 const int32 kInitSoundCount = 32;
51 const int32 kGrowth = 16;
52
53 static int32 sDeviceCount = 0;
54 static BGameSoundDevice* sDevice = NULL;
55 static BLocker sDeviceRefCountLock("GameSound device lock");
56
57
58 BGameSoundDevice*
GetDefaultDevice()59 GetDefaultDevice()
60 {
61 BAutolock _(sDeviceRefCountLock);
62
63 if (!sDevice)
64 sDevice = new BGameSoundDevice();
65
66 sDeviceCount++;
67 return sDevice;
68 }
69
70
71 void
ReleaseDevice()72 ReleaseDevice()
73 {
74 BAutolock _(sDeviceRefCountLock);
75
76 sDeviceCount--;
77
78 if (sDeviceCount <= 0) {
79 delete sDevice;
80 sDevice = NULL;
81 }
82 }
83
84
85 // BGameSoundDevice -------------------------------------------------------
BGameSoundDevice()86 BGameSoundDevice::BGameSoundDevice()
87 :
88 fIsConnected(false),
89 fSoundCount(kInitSoundCount)
90 {
91 memset(&fFormat, 0, sizeof(gs_audio_format));
92
93 fInitError = B_OK;
94
95 fSounds = new GameSoundBuffer*[kInitSoundCount];
96 for (int32 i = 0; i < kInitSoundCount; i++)
97 fSounds[i] = NULL;
98 }
99
100
~BGameSoundDevice()101 BGameSoundDevice::~BGameSoundDevice()
102 {
103 // We need to stop all the sounds before we stop the mixer
104 for (int32 i = 0; i < fSoundCount; i++) {
105 if (fSounds[i])
106 fSounds[i]->StopPlaying();
107 delete fSounds[i];
108 }
109
110 delete[] fSounds;
111 }
112
113
114 status_t
InitCheck() const115 BGameSoundDevice::InitCheck() const
116 {
117 return fInitError;
118 }
119
120
121 const gs_audio_format&
Format() const122 BGameSoundDevice::Format() const
123 {
124 return fFormat;
125 }
126
127
128 const gs_audio_format&
Format(gs_id sound) const129 BGameSoundDevice::Format(gs_id sound) const
130 {
131 return fSounds[sound - 1]->Format();
132 }
133
134
135 void
SetInitError(status_t error)136 BGameSoundDevice::SetInitError(status_t error)
137 {
138 fInitError = error;
139 }
140
141
142 status_t
CreateBuffer(gs_id * sound,const gs_audio_format * format,const void * data,int64 frames)143 BGameSoundDevice::CreateBuffer(gs_id* sound, const gs_audio_format* format,
144 const void* data, int64 frames)
145 {
146 if (frames <= 0 || !sound)
147 return B_BAD_VALUE;
148
149 // Make sure BMediaRoster is created before we AllocateSound()
150 BMediaRoster* roster = BMediaRoster::Roster();
151
152 status_t err = B_MEDIA_TOO_MANY_BUFFERS;
153 int32 position = AllocateSound();
154
155 if (position >= 0) {
156 fSounds[position] = new SimpleSoundBuffer(format, data, frames);
157
158 media_node systemMixer;
159 roster->GetAudioMixer(&systemMixer);
160 err = fSounds[position]->Connect(&systemMixer);
161 }
162
163 if (err == B_OK)
164 *sound = gs_id(position + 1);
165 return err;
166 }
167
168
169 status_t
CreateBuffer(gs_id * sound,const void * object,const gs_audio_format * format,size_t inBufferFrameCount,size_t inBufferCount)170 BGameSoundDevice::CreateBuffer(gs_id* sound, const void* object,
171 const gs_audio_format* format, size_t inBufferFrameCount,
172 size_t inBufferCount)
173 {
174 if (!object || !sound)
175 return B_BAD_VALUE;
176
177 // Make sure BMediaRoster is created before we AllocateSound()
178 BMediaRoster* roster = BMediaRoster::Roster();
179
180 status_t err = B_MEDIA_TOO_MANY_BUFFERS;
181 int32 position = AllocateSound();
182
183 if (position >= 0) {
184 fSounds[position] = new StreamingSoundBuffer(format, object,
185 inBufferFrameCount, inBufferCount);
186
187 media_node systemMixer;
188 roster->GetAudioMixer(&systemMixer);
189 err = fSounds[position]->Connect(&systemMixer);
190 }
191
192 if (err == B_OK)
193 *sound = gs_id(position + 1);
194 return err;
195 }
196
197
198 void
ReleaseBuffer(gs_id sound)199 BGameSoundDevice::ReleaseBuffer(gs_id sound)
200 {
201 if (sound <= 0)
202 return;
203
204 if (fSounds[sound - 1]) {
205 // We must stop playback befor destroying the sound or else
206 // we may receive fatal errors.
207 fSounds[sound - 1]->StopPlaying();
208
209 delete fSounds[sound - 1];
210 fSounds[sound - 1] = NULL;
211 }
212 }
213
214
215 status_t
Buffer(gs_id sound,gs_audio_format * format,void * & data)216 BGameSoundDevice::Buffer(gs_id sound, gs_audio_format* format, void*& data)
217 {
218 if (!format || sound <= 0)
219 return B_BAD_VALUE;
220
221 memcpy(format, &fSounds[sound - 1]->Format(), sizeof(gs_audio_format));
222 if (fSounds[sound - 1]->Data()) {
223 data = malloc(format->buffer_size);
224 memcpy(data, fSounds[sound - 1]->Data(), format->buffer_size);
225 } else
226 data = NULL;
227
228 return B_OK;
229 }
230
231
232 status_t
StartPlaying(gs_id sound)233 BGameSoundDevice::StartPlaying(gs_id sound)
234 {
235 if (sound <= 0)
236 return B_BAD_VALUE;
237
238 if (!fSounds[sound - 1]->IsPlaying()) {
239 // tell the producer to start playing the sound
240 return fSounds[sound - 1]->StartPlaying();
241 }
242
243 fSounds[sound - 1]->Reset();
244 return EALREADY;
245 }
246
247
248 status_t
StopPlaying(gs_id sound)249 BGameSoundDevice::StopPlaying(gs_id sound)
250 {
251 if (sound <= 0)
252 return B_BAD_VALUE;
253
254 if (fSounds[sound - 1]->IsPlaying()) {
255 // Tell the producer to stop play this sound
256 fSounds[sound - 1]->Reset();
257 return fSounds[sound - 1]->StopPlaying();
258 }
259
260 return EALREADY;
261 }
262
263
264 bool
IsPlaying(gs_id sound)265 BGameSoundDevice::IsPlaying(gs_id sound)
266 {
267 if (sound <= 0)
268 return false;
269 return fSounds[sound - 1]->IsPlaying();
270 }
271
272
273 status_t
GetAttributes(gs_id sound,gs_attribute * attributes,size_t attributeCount)274 BGameSoundDevice::GetAttributes(gs_id sound, gs_attribute* attributes,
275 size_t attributeCount)
276 {
277 if (!fSounds[sound - 1])
278 return B_ERROR;
279
280 return fSounds[sound - 1]->GetAttributes(attributes, attributeCount);
281 }
282
283
284 status_t
SetAttributes(gs_id sound,gs_attribute * attributes,size_t attributeCount)285 BGameSoundDevice::SetAttributes(gs_id sound, gs_attribute* attributes,
286 size_t attributeCount)
287 {
288 if (!fSounds[sound - 1])
289 return B_ERROR;
290
291 return fSounds[sound - 1]->SetAttributes(attributes, attributeCount);
292 }
293
294
295 int32
AllocateSound()296 BGameSoundDevice::AllocateSound()
297 {
298 for (int32 i = 0; i < fSoundCount; i++)
299 if (!fSounds[i])
300 return i;
301
302 // we need to allocate new space for the sound
303 GameSoundBuffer ** sounds = new GameSoundBuffer*[fSoundCount + kGrowth];
304 for (int32 i = 0; i < fSoundCount; i++)
305 sounds[i] = fSounds[i];
306
307 for (int32 i = fSoundCount; i < fSoundCount + kGrowth; i++)
308 sounds[i] = NULL;
309
310 // replace the old list
311 delete [] fSounds;
312 fSounds = sounds;
313 fSoundCount += kGrowth;
314
315 return fSoundCount - kGrowth;
316 }
317
318