xref: /haiku/src/kits/game/GameSoundDevice.cpp (revision 90ca02568835b140b0e59de496a7f1f1d3513f67)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
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 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include <List.h>
34 #include <MediaRoster.h>
35 #include <MediaAddOn.h>
36 #include <TimeSource.h>
37 #include <MediaTheme.h>
38 
39 #include "GSUtility.h"
40 #include "GameSoundDevice.h"
41 #include "GameSoundBuffer.h"
42 #include "GameProducer.h"
43 
44 // BGameSoundDevice definitions ------------------------------------
45 const int32 kInitSoundCount = 32;
46 const int32 kGrowth = 16;
47 
48 static int32 deviceCount = 0;
49 static BGameSoundDevice* theDevice = NULL;
50 
51 BGameSoundDevice * GetDefaultDevice()
52 {
53 	if (!theDevice)
54 		theDevice = new BGameSoundDevice();
55 
56 	deviceCount++;
57 	return theDevice;
58 }
59 
60 void ReleaseDevice()
61 {
62 	deviceCount--;
63 
64 	if (deviceCount <= 0) {
65 		delete theDevice;
66 		theDevice = NULL;
67 	}
68 }
69 
70 // BGameSoundDevice -------------------------------------------------------
71 BGameSoundDevice::BGameSoundDevice()
72 	:   fIsConnected(false),
73 		fSoundCount(kInitSoundCount)
74 {
75 	fConnection = new Connection;
76 	memset(&fFormat, 0, sizeof(gs_audio_format));
77 
78 	fInitError = Connect();
79 
80 	fSounds = new GameSoundBuffer*[kInitSoundCount];
81 	for (int32 i = 0; i < kInitSoundCount; i++)
82 		fSounds[i] = NULL;
83 }
84 
85 
86 BGameSoundDevice::~BGameSoundDevice()
87 {
88 	BMediaRoster* r = BMediaRoster::Roster();
89 
90 	// We need to stop all the sounds before we stop the mixer
91 	for (int32 i = 0; i < fSoundCount; i++) {
92 		if (fSounds[i])
93 			fSounds[i]->StopPlaying();
94 		delete fSounds[i];
95 	}
96 
97 	if (fIsConnected) {
98 		// stop the nodes if they are running
99 		r->StopNode(fConnection->producer, 0, true);		// synchronous stop
100 
101 		// Ordinarily we'd stop *all* of the nodes in the chain at this point.  However,
102 		// one of the nodes is the System Mixer, and stopping the Mixer is a Bad Idea (tm).
103 		// So, we just disconnect from it, and release our references to the nodes that
104 		// we're using.  We *are* supposed to do that even for global nodes like the Mixer.
105 		r->Disconnect(fConnection->producer.node, fConnection->source,
106 							fConnection->consumer.node, fConnection->destination);
107 
108 		r->ReleaseNode(fConnection->producer);
109 		r->ReleaseNode(fConnection->consumer);
110 	}
111 
112 	delete [] fSounds;
113 	delete fConnection;
114 }
115 
116 
117 status_t
118 BGameSoundDevice::InitCheck() const
119 {
120 	return fInitError;
121 }
122 
123 
124 const gs_audio_format &
125 BGameSoundDevice::Format() const
126 {
127 	return fFormat;
128 }
129 
130 
131 const gs_audio_format &
132 BGameSoundDevice::Format(gs_id sound) const
133 {
134 	return fSounds[sound-1]->Format();
135 }
136 
137 
138 void
139 BGameSoundDevice::SetInitError(status_t error)
140 {
141 	fInitError = error;
142 }
143 
144 
145 status_t
146 BGameSoundDevice::CreateBuffer(gs_id * sound,
147 								const gs_audio_format * format,
148 								const void * data,
149 								int64 frames)
150 {
151 	if (frames <= 0 || !sound)
152 		return B_BAD_VALUE;
153 
154 	status_t err = B_MEDIA_TOO_MANY_BUFFERS;
155 	int32 position = AllocateSound();
156 
157 	if (position >= 0) {
158 		fSounds[position] = new SimpleSoundBuffer(format, data, frames);
159 		err = fSounds[position]->Connect(&fConnection->producer);
160 	}
161 
162 	*sound = gs_id(position+1);
163 	return err;
164 }
165 
166 
167 status_t
168 BGameSoundDevice::CreateBuffer(gs_id * sound,
169 								const void * object,
170 								const gs_audio_format * format)
171 {
172 	if (!object || !sound)
173 		return B_BAD_VALUE;
174 
175 	status_t err = B_MEDIA_TOO_MANY_BUFFERS;
176 	int32 position = AllocateSound();
177 
178 	if (position >= 0) {
179 		fSounds[position] = new StreamingSoundBuffer(format, object);
180 		err = fSounds[position]->Connect(&fConnection->producer);
181 	}
182 
183 	*sound = gs_id(position+1);
184 	return err;
185 }
186 
187 
188 void
189 BGameSoundDevice::ReleaseBuffer(gs_id sound)
190 {
191 	if (sound <= 0)
192 		return;
193 
194 	if (fSounds[sound-1]) {
195 		// We must stop playback befor destroying the sound or else
196 		// we may recieve fatel errors.
197 		fSounds[sound-1]->StopPlaying();
198 
199 		delete fSounds[sound-1];
200 		fSounds[sound-1] = NULL;
201 	}
202 }
203 
204 
205 status_t
206 BGameSoundDevice::Buffer(gs_id sound,
207 						 gs_audio_format * format,
208 						 void * data)
209 {
210 	if (!format || sound <= 0)
211 		return B_BAD_VALUE;
212 
213 	memcpy(format, &fSounds[sound-1]->Format(), sizeof(gs_audio_format));
214 
215 	if (fSounds[sound-1]->Data()) {
216 		data = malloc(format->buffer_size);
217 		memcpy(data, fSounds[sound-1]->Data(), format->buffer_size);
218 	} else
219 		data = NULL;
220 
221 	return B_OK;
222 }
223 
224 status_t
225 BGameSoundDevice::StartPlaying(gs_id sound)
226 {
227 	if (sound <= 0)
228 		return B_BAD_VALUE;
229 
230 	if (!fSounds[sound-1]->IsPlaying()) {
231 		// tell the producer to start playing the sound
232 		return fSounds[sound-1]->StartPlaying();
233 	}
234 
235 	fSounds[sound-1]->Reset();
236 	return EALREADY;
237 }
238 
239 
240 status_t
241 BGameSoundDevice::StopPlaying(gs_id sound)
242 {
243 	if (sound <= 0)
244 		return B_BAD_VALUE;
245 
246 	if (fSounds[sound-1]->IsPlaying()) {
247 		// Tell the producer to stop play this sound
248 		fSounds[sound-1]->Reset();
249 		return fSounds[sound-1]->StopPlaying();
250 	}
251 
252 	return EALREADY;
253 }
254 
255 
256 bool
257 BGameSoundDevice::IsPlaying(gs_id sound)
258 {
259 	if (sound <= 0)
260 		return false;
261 	return fSounds[sound-1]->IsPlaying();
262 }
263 
264 
265 status_t
266 BGameSoundDevice::GetAttributes(gs_id sound,
267 								gs_attribute * attributes,
268 								size_t attributeCount)
269 {
270 	if (!fSounds[sound-1])
271 		return B_ERROR;
272 
273 	return fSounds[sound-1]->GetAttributes(attributes, attributeCount);
274 }
275 
276 
277 status_t
278 BGameSoundDevice::SetAttributes(gs_id sound,
279 								gs_attribute * attributes,
280 								size_t attributeCount)
281 {
282 	if (!fSounds[sound-1])
283 		return B_ERROR;
284 
285 	return fSounds[sound-1]->SetAttributes(attributes, attributeCount);
286 }
287 
288 
289 status_t
290 BGameSoundDevice::Connect()
291 {
292 	BMediaRoster* r = BMediaRoster::Roster();
293 	status_t err;
294 
295 	// create your own audio mixer
296 	dormant_node_info mixer_dormant_info;
297 	int32 mixer_count = 1; // for now, we only care about the first  we find.
298 	err = r->GetDormantNodes(&mixer_dormant_info, &mixer_count, 0, 0, 0, B_SYSTEM_MIXER, 0);
299 	if (err != B_OK)
300 		return err;
301 
302 	//fMixer = new media_node;
303 	err = r->InstantiateDormantNode(mixer_dormant_info, &fConnection->producer);
304 	if (err != B_OK)
305 		return err;
306 
307 	// retieve the system's audio mixer
308 	err = r->GetAudioMixer(&fConnection->consumer);
309 	if (err != B_OK)
310 		return err;
311 
312 	int32 count = 1;
313 	media_input mixerInput;
314 	err = r->GetFreeInputsFor(fConnection->consumer, &mixerInput, 1, &count);
315 	if (err != B_OK)
316 		return err;
317 
318 	count = 1;
319 	media_output mixerOutput;
320 	err = r->GetFreeOutputsFor(fConnection->producer, &mixerOutput, 1, &count);
321 	if (err != B_OK)
322 		return err;
323 
324 	media_format format(mixerOutput.format);
325 	err = r->Connect(mixerOutput.source, mixerInput.destination, &format, &mixerOutput, &mixerInput);
326 	if (err != B_OK)
327 		return err;
328 
329 	// set the producer's time source to be the "default" time source, which
330 	// the Mixer uses too.
331 	r->GetTimeSource(&fConnection->timeSource);
332 	r->SetTimeSourceFor(fConnection->producer.node, fConnection->timeSource.node);
333 
334 	// Start our mixer's time source if need be. Chances are, it won't need to be,
335 	// but if we forget to do this, our mixer might not do anything at all.
336 	BTimeSource* mixerTimeSource = r->MakeTimeSourceFor(fConnection->producer);
337 	if (!mixerTimeSource)
338 		return B_ERROR;
339 
340 	if (!mixerTimeSource->IsRunning()) {
341 		status_t err = r->StartNode(mixerTimeSource->Node(), BTimeSource::RealTime());
342 		if (err != B_OK) {
343 			mixerTimeSource->Release();
344 			return err;
345 		}
346 	}
347 
348 	// Start up our mixer
349 	bigtime_t tpNow = mixerTimeSource->Now();
350 	err = r->StartNode(fConnection->producer, tpNow + 10000);
351 	mixerTimeSource->Release();
352 	if (err != B_OK)
353 		return err;
354 
355 	// the inputs and outputs might have been reassigned during the
356 	// nodes' negotiation of the Connect().  That's why we wait until
357 	// after Connect() finishes to save their contents.
358 	fConnection->format = format;
359 	fConnection->source = mixerOutput.source;
360 	fConnection->destination = mixerInput.destination;
361 
362 	// Set an appropriate run mode for the producer
363 	r->SetRunModeNode(fConnection->producer, BMediaNode::B_INCREASE_LATENCY);
364 
365 	media_to_gs_format(&fFormat, &format.u.raw_audio);
366 	fIsConnected = true;
367 	return B_OK;
368 }
369 
370 
371 int32
372 BGameSoundDevice::AllocateSound()
373 {
374 	for (int32 i = 0; i < fSoundCount; i++)
375 		if (!fSounds[i])
376 			return i;
377 
378 	// we need to allocate new space for the sound
379 	GameSoundBuffer ** sounds = new GameSoundBuffer*[fSoundCount + kGrowth];
380 	for (int32 i = 0; i < fSoundCount; i++)
381 		sounds[i] = fSounds[i];
382 
383 	for (int32 i = fSoundCount; i < fSoundCount + kGrowth; i++)
384 		sounds[i] = NULL;
385 
386 	// replace the old list
387 	delete [] fSounds;
388 	fSounds = sounds;
389 	fSoundCount += kGrowth;
390 
391 	return fSoundCount - kGrowth;
392 }
393 
394