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
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 = BLocker("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	status_t err = B_MEDIA_TOO_MANY_BUFFERS;
150	int32 position = AllocateSound();
151
152	if (position >= 0) {
153		media_node systemMixer;
154		BMediaRoster::Roster()->GetAudioMixer(&systemMixer);
155		fSounds[position] = new SimpleSoundBuffer(format, data, frames);
156		err = fSounds[position]->Connect(&systemMixer);
157	}
158
159	if (err == B_OK)
160		*sound = gs_id(position + 1);
161	return err;
162}
163
164
165status_t
166BGameSoundDevice::CreateBuffer(gs_id* sound, const void* object,
167	const gs_audio_format* format, size_t inBufferFrameCount,
168	size_t inBufferCount)
169{
170	if (!object || !sound)
171		return B_BAD_VALUE;
172
173	status_t err = B_MEDIA_TOO_MANY_BUFFERS;
174	int32 position = AllocateSound();
175
176	if (position >= 0) {
177		media_node systemMixer;
178		BMediaRoster::Roster()->GetAudioMixer(&systemMixer);
179		fSounds[position] = new StreamingSoundBuffer(format, object,
180			inBufferFrameCount, inBufferCount);
181		err = fSounds[position]->Connect(&systemMixer);
182	}
183
184	if (err == B_OK)
185		*sound = gs_id(position + 1);
186	return err;
187}
188
189
190void
191BGameSoundDevice::ReleaseBuffer(gs_id sound)
192{
193	if (sound <= 0)
194		return;
195
196	if (fSounds[sound - 1]) {
197		// We must stop playback befor destroying the sound or else
198		// we may receive fatal errors.
199		fSounds[sound - 1]->StopPlaying();
200
201		delete fSounds[sound - 1];
202		fSounds[sound - 1] = NULL;
203	}
204}
205
206
207status_t
208BGameSoundDevice::Buffer(gs_id sound, gs_audio_format* format, 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	if (fSounds[sound - 1]->Data()) {
215		data = malloc(format->buffer_size);
216		memcpy(data, fSounds[sound - 1]->Data(), format->buffer_size);
217	} else
218		data = NULL;
219
220	return B_OK;
221}
222
223
224status_t
225BGameSoundDevice::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
240status_t
241BGameSoundDevice::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
256bool
257BGameSoundDevice::IsPlaying(gs_id sound)
258{
259	if (sound <= 0)
260		return false;
261	return fSounds[sound - 1]->IsPlaying();
262}
263
264
265status_t
266BGameSoundDevice::GetAttributes(gs_id sound, gs_attribute* attributes,
267	size_t attributeCount)
268{
269	if (!fSounds[sound - 1])
270		return B_ERROR;
271
272	return fSounds[sound - 1]->GetAttributes(attributes, attributeCount);
273}
274
275
276status_t
277BGameSoundDevice::SetAttributes(gs_id sound, gs_attribute* attributes,
278	size_t attributeCount)
279{
280	if (!fSounds[sound - 1])
281		return B_ERROR;
282
283	return fSounds[sound - 1]->SetAttributes(attributes, attributeCount);
284}
285
286
287int32
288BGameSoundDevice::AllocateSound()
289{
290	for (int32 i = 0; i < fSoundCount; i++)
291		if (!fSounds[i])
292			return i;
293
294	// we need to allocate new space for the sound
295	GameSoundBuffer ** sounds = new GameSoundBuffer*[fSoundCount + kGrowth];
296	for (int32 i = 0; i < fSoundCount; i++)
297		sounds[i] = fSounds[i];
298
299	for (int32 i = fSoundCount; i < fSoundCount + kGrowth; i++)
300		sounds[i] = NULL;
301
302	// replace the old list
303	delete [] fSounds;
304	fSounds = sounds;
305	fSoundCount += kGrowth;
306
307	return fSoundCount - kGrowth;
308}
309
310