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