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 ------------------------------------
50const int32 kInitSoundCount = 32;
51const int32 kGrowth = 16;
52
53static int32 sDeviceCount = 0;
54static BGameSoundDevice* sDevice = NULL;
55static BLocker sDeviceRefCountLock("GameSound device lock");
56
57
58BGameSoundDevice*
59GetDefaultDevice()
60{
61	BAutolock _(sDeviceRefCountLock);
62
63	if (!sDevice)
64		sDevice = new BGameSoundDevice();
65
66	sDeviceCount++;
67	return sDevice;
68}
69
70
71void
72ReleaseDevice()
73{
74	BAutolock _(sDeviceRefCountLock);
75
76	sDeviceCount--;
77
78	if (sDeviceCount <= 0) {
79		delete sDevice;
80		sDevice = NULL;
81	}
82}
83
84
85// BGameSoundDevice -------------------------------------------------------
86BGameSoundDevice::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
101BGameSoundDevice::~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
114status_t
115BGameSoundDevice::InitCheck() const
116{
117	return fInitError;
118}
119
120
121const gs_audio_format&
122BGameSoundDevice::Format() const
123{
124	return fFormat;
125}
126
127
128const gs_audio_format&
129BGameSoundDevice::Format(gs_id sound) const
130{
131	return fSounds[sound - 1]->Format();
132}
133
134
135void
136BGameSoundDevice::SetInitError(status_t error)
137{
138	fInitError = error;
139}
140
141
142status_t
143BGameSoundDevice::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
169status_t
170BGameSoundDevice::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
198void
199BGameSoundDevice::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
215status_t
216BGameSoundDevice::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
232status_t
233BGameSoundDevice::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
248status_t
249BGameSoundDevice::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
264bool
265BGameSoundDevice::IsPlaying(gs_id sound)
266{
267	if (sound <= 0)
268		return false;
269	return fSounds[sound - 1]->IsPlaying();
270}
271
272
273status_t
274BGameSoundDevice::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
284status_t
285BGameSoundDevice::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
295int32
296BGameSoundDevice::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