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