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* 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 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 ------------------------------------------------------- 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 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 115 BGameSoundDevice::InitCheck() const 116 { 117 return fInitError; 118 } 119 120 121 const gs_audio_format& 122 BGameSoundDevice::Format() const 123 { 124 return fFormat; 125 } 126 127 128 const gs_audio_format& 129 BGameSoundDevice::Format(gs_id sound) const 130 { 131 return fSounds[sound - 1]->Format(); 132 } 133 134 135 void 136 BGameSoundDevice::SetInitError(status_t error) 137 { 138 fInitError = error; 139 } 140 141 142 status_t 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 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 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 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 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 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 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 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 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 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