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