1 /*****************************************************************************/ 2 // GameProdcure.h 3 // 4 // This produce creates audio buffer on behalf of the GameKit. 5 // 6 // Copyright (c) 2001 OpenBeOS Project 7 // 8 // Permission is hereby granted, free of charge, to any person obtaining a 9 // copy of this software and associated documentation files (the "Software"), 10 // to deal in the Software without restriction, including without limitation 11 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 // and/or sell copies of the Software, and to permit persons to whom the 13 // Software is furnished to do so, subject to the following conditions: 14 // 15 // The above copyright notice and this permission notice shall be included 16 // in all copies or substantial portions of the Software. 17 // 18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 // DEALINGS IN THE SOFTWARE. 25 // 26 // File Name: GameProducer.cpp 27 // Author: Christopher ML Zumwalt May (zummy@users.sf.net) 28 // Description: A MediaKit producer node which mixes sound from the GameKit 29 // and sends them to the audio mixer 30 /*****************************************************************************/ 31 32 // Standard Includes ----------------------------------------------------------- 33 #include <string.h> 34 #include <stdio.h> 35 36 // System Includes ------------------------------------------------------------- 37 #include <ByteOrder.h> 38 #include <BufferGroup.h> 39 #include <Buffer.h> 40 #include <List.h> 41 #include <TimeSource.h> 42 #include <MediaDefs.h> 43 44 // Project Includes ------------------------------------------------------------ 45 #include "GameSoundBuffer.h" 46 #include "GameSoundDevice.h" 47 #include "GSUtility.h" 48 49 // Local Includes -------------------------------------------------------------- 50 #include "GameProducer.h" 51 52 // Local Defines --------------------------------------------------------------- 53 struct _gs_play 54 { 55 gs_id sound; 56 bool * hook; 57 58 _gs_play * next; 59 _gs_play * previous; 60 }; 61 62 GameProducer::GameProducer(GameSoundBuffer * object, 63 const gs_audio_format * format) 64 : BMediaNode("GameProducer.h"), 65 BBufferProducer(B_MEDIA_RAW_AUDIO), 66 BMediaEventLooper(), 67 fBufferGroup(NULL), 68 fLatency(0), 69 fInternalLatency(0), 70 fOutputEnabled(true) 71 { 72 // initialize our preferred format object 73 fPreferredFormat.type = B_MEDIA_RAW_AUDIO; 74 fPreferredFormat.u.raw_audio.format = format->format; 75 fPreferredFormat.u.raw_audio.channel_count = format->channel_count; 76 fPreferredFormat.u.raw_audio.frame_rate = format->frame_rate; // measured in Hertz 77 fPreferredFormat.u.raw_audio.byte_order = format->byte_order; 78 // fPreferredFormat.u.raw_audio.channel_mask = B_CHANNEL_LEFT & B_CHANNEL_RIGHT; 79 // fPreferredFormat.u.raw_audio.valid_bits = 32; 80 // fPreferredFormat.u.raw_audio.matrix_mask = B_MATRIX_AMBISONIC_WXYZ; 81 82 // we'll use the consumer's preferred buffer size, if any 83 fPreferredFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size; 84 85 // we're not connected yet 86 fOutput.destination = media_destination::null; 87 fOutput.format = fPreferredFormat; 88 89 fFrameSize = get_sample_size(format->format) * format->channel_count; 90 fObject = object; 91 } 92 93 94 GameProducer::~GameProducer() 95 { 96 // Stop the BMediaEventLooper thread 97 Quit(); 98 } 99 100 101 // BMediaNode methods 102 BMediaAddOn * 103 GameProducer::AddOn(int32 *internal_id) const 104 { 105 return NULL; 106 } 107 108 109 // BBufferProducer methods 110 status_t 111 GameProducer::GetNextOutput(int32* cookie, media_output* out_output) 112 { 113 // we currently support only one output 114 if (0 != *cookie) return B_BAD_INDEX; 115 116 *out_output = fOutput; 117 *cookie += 1; 118 return B_OK; 119 } 120 121 122 status_t 123 GameProducer::DisposeOutputCookie(int32 cookie) 124 { 125 // do nothing because our cookie is only an integer 126 return B_OK; 127 } 128 129 130 void 131 GameProducer::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_) 132 { 133 // If I had more than one output, I'd have to walk my list of output records to see 134 // which one matched the given source, and then enable/disable that one. But this 135 // node only has one output, so I just make sure the given source matches, then set 136 // the enable state accordingly. 137 if (what == fOutput.source) 138 { 139 fOutputEnabled = enabled; 140 } 141 } 142 143 144 status_t 145 GameProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format) 146 { 147 // insure that we received a format 148 if (!format) 149 return B_BAD_VALUE; 150 151 // returning our preferred format 152 *format = fPreferredFormat; 153 154 // our format is supported 155 if (type == B_MEDIA_UNKNOWN_TYPE) return B_OK; 156 157 // we only support raw audo 158 return (type != B_MEDIA_RAW_AUDIO) ? B_MEDIA_BAD_FORMAT : B_OK; 159 } 160 161 162 status_t 163 GameProducer::FormatProposal(const media_source& output, media_format* format) 164 { 165 // doest the proposed output match our output? 166 if (output != fOutput.source) return B_MEDIA_BAD_SOURCE; 167 168 // return our preferred format 169 *format = fPreferredFormat; 170 171 // we will reject the proposal if the format is not audio 172 media_type requestedType = format->type; 173 if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO)) 174 return B_MEDIA_BAD_FORMAT; 175 176 return B_OK; // raw audio or wildcard type, either is okay by us 177 } 178 179 180 status_t 181 GameProducer::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name) 182 { 183 // The format has been processed by the consumer at this point. We need 184 // to insure the format is still acceptable and any wild care are filled in. 185 186 // trying to connect something that isn't our source? 187 if (what != fOutput.source) return B_MEDIA_BAD_SOURCE; 188 189 // are we already connected? 190 if (fOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED; 191 192 // the format may not yet be fully specialized (the consumer might have 193 // passed back some wildcards). Finish specializing it now, and return an 194 // error if we don't support the requested format. 195 if (format->type != B_MEDIA_RAW_AUDIO) 196 return B_MEDIA_BAD_FORMAT; 197 198 if (format->u.raw_audio.format != fPreferredFormat.u.raw_audio.format) 199 return B_MEDIA_BAD_FORMAT; 200 201 // check the buffer size, which may still be wildcarded 202 if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size) 203 format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest 204 205 // Now reserve the connection, and return information about it 206 fOutput.destination = where; 207 fOutput.format = *format; 208 *out_source = fOutput.source; 209 strncpy(out_name, fOutput.name, B_MEDIA_NAME_LENGTH); 210 return B_OK; 211 } 212 213 214 void 215 GameProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) 216 { 217 // If something earlier failed, Connect() might still be called, but with a non-zero 218 // error code. When that happens we simply unreserve the connection and do 219 // nothing else. 220 if (error) 221 { 222 fOutput.destination = media_destination::null; 223 fOutput.format = fPreferredFormat; 224 } 225 else 226 { 227 // Okay, the connection has been confirmed. Record the destination and format 228 // that we agreed on, and report our connection name again. 229 fOutput.destination = destination; 230 fOutput.format = format; 231 strncpy(io_name, fOutput.name, B_MEDIA_NAME_LENGTH); 232 233 // Now that we're connected, we can determine our downstream latency. 234 // Do so, then make sure we get our events early enough. 235 media_node_id id; 236 FindLatencyFor(fOutput.destination, &fLatency, &id); 237 238 // Use a dry run to see how long it takes me to fill a buffer of data 239 240 // The first step to setup the buffer 241 bigtime_t start, produceLatency; 242 int32 frames = int32(fOutput.format.u.raw_audio.buffer_size / fFrameSize); 243 float* data = new float[frames * 2]; 244 245 // Second, fill the buffer 246 start = ::system_time(); 247 for(int32 i = 0; i < frames; i++) 248 { 249 data[i*2] = 0.8 * float(i/frames); 250 data[i*2+1] = 0.8 * float(i/frames); 251 } 252 produceLatency = ::system_time(); 253 254 // Thid, calculate the latency 255 fInternalLatency = produceLatency - start; 256 SetEventLatency(fLatency + fInternalLatency); 257 258 // Finaily, clean up 259 delete [] data; 260 261 // reset our buffer duration, etc. to avoid later calculations 262 bigtime_t duration = bigtime_t(1000000) * frames / bigtime_t(fOutput.format.u.raw_audio.frame_rate); 263 SetBufferDuration(duration); 264 265 // Set up the buffer group for our connection, as long as nobody handed us a 266 // buffer group (via SetBufferGroup()) prior to this. 267 if (!fBufferGroup) 268 { 269 size_t size = fOutput.format.u.raw_audio.buffer_size; 270 int32 count = int32(fLatency / BufferDuration() + 2); 271 fBufferGroup = new BBufferGroup(size, count); 272 } 273 } 274 } 275 276 277 void 278 GameProducer::Disconnect(const media_source& what, const media_destination& where) 279 { 280 // Make sure that our connection is the one being disconnected 281 if ((where == fOutput.destination) && (what == fOutput.source)) 282 { 283 fOutput.destination = media_destination::null; 284 fOutput.format = fPreferredFormat; 285 delete fBufferGroup; 286 fBufferGroup = NULL; 287 } 288 } 289 290 291 status_t 292 GameProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_) 293 { 294 // we don't support any other formats, so we just reject any format changes. 295 return B_ERROR; 296 } 297 298 299 status_t 300 GameProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup) 301 { 302 // verify that we didn't get bogus arguments before we proceed 303 if (for_source != fOutput.source) return B_MEDIA_BAD_SOURCE; 304 305 // Are we being passed the buffer group we're already using? 306 if (newGroup == fBufferGroup) return B_OK; 307 308 // Ahh, someone wants us to use a different buffer group. At this point we delete 309 // the one we are using and use the specified one instead. If the specified group is 310 // NULL, we need to recreate one ourselves, and use *that*. Note that if we're 311 // caching a BBuffer that we requested earlier, we have to Recycle() that buffer 312 // *before* deleting the buffer group, otherwise we'll deadlock waiting for that 313 // buffer to be recycled! 314 delete fBufferGroup; // waits for all buffers to recycle 315 if (newGroup != NULL) 316 { 317 // we were given a valid group; just use that one from now on 318 fBufferGroup = newGroup; 319 } 320 else 321 { 322 // we were passed a NULL group pointer; that means we construct 323 // our own buffer group to use from now on 324 size_t size = fOutput.format.u.raw_audio.buffer_size; 325 int32 count = int32(fLatency / BufferDuration() + 2); 326 fBufferGroup = new BBufferGroup(size, count); 327 } 328 329 return B_OK; 330 } 331 332 333 status_t 334 GameProducer::GetLatency(bigtime_t* out_latency) 335 { 336 // report our *total* latency: internal plus downstream plus scheduling 337 *out_latency = EventLatency() + SchedulingLatency(); 338 return B_OK; 339 } 340 341 342 void 343 GameProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time) 344 { 345 // If we're late, we need to catch up. Respond in a manner appropriate to our 346 // current run mode. 347 if (what == fOutput.source) 348 { 349 if (RunMode() == B_RECORDING) 350 { 351 // A hardware capture node can't adjust; it simply emits buffers at 352 // appropriate points. We (partially) simulate this by not adjusting 353 // our behavior upon receiving late notices -- after all, the hardware 354 // can't choose to capture "sooner".... 355 } 356 else if (RunMode() == B_INCREASE_LATENCY) 357 { 358 // We're late, and our run mode dictates that we try to produce buffers 359 // earlier in order to catch up. This argues that the downstream nodes are 360 // not properly reporting their latency, but there's not much we can do about 361 // that at the moment, so we try to start producing buffers earlier to 362 // compensate. 363 fInternalLatency += how_much; 364 SetEventLatency(fLatency + fInternalLatency); 365 } 366 else 367 { 368 // The other run modes dictate various strategies for sacrificing data quality 369 // in the interests of timely data delivery. The way *we* do this is to skip 370 // a buffer, which catches us up in time by one buffer duration. 371 size_t nSamples = fOutput.format.u.raw_audio.buffer_size / fFrameSize; 372 fFramesSent += nSamples; 373 } 374 } 375 } 376 377 378 void 379 GameProducer::LatencyChanged(const media_source& source, const media_destination& destination, bigtime_t new_latency, uint32 flags) 380 { 381 // something downstream changed latency, so we need to start producing 382 // buffers earlier (or later) than we were previously. Make sure that the 383 // connection that changed is ours, and adjust to the new downstream 384 // latency if so. 385 if ((source == fOutput.source) && (destination == fOutput.destination)) 386 { 387 fLatency = new_latency; 388 SetEventLatency(fLatency + fInternalLatency); 389 } 390 } 391 392 393 status_t 394 GameProducer::SetPlayRate(int32 numer, int32 denom) 395 { 396 // Play rates are weird. We don't support them 397 return B_ERROR; 398 } 399 400 401 status_t 402 GameProducer::HandleMessage(int32 message, const void* data, size_t size) 403 { 404 // We currently do not handle private messages 405 return B_ERROR; 406 } 407 408 409 void 410 GameProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag) 411 { 412 // we don't support offline mode (yet...) 413 return; 414 } 415 416 417 // BMediaEventLooper methods 418 void 419 GameProducer::NodeRegistered() 420 { 421 // Start the BMediaEventLooper thread 422 SetPriority(B_REAL_TIME_PRIORITY); 423 Run(); 424 425 // set up as much information about our output as we can 426 fOutput.source.port = ControlPort(); 427 fOutput.source.id = 0; 428 fOutput.node = Node(); 429 ::strcpy(fOutput.name, "GameProducer Output"); 430 } 431 432 433 void 434 GameProducer::SetRunMode(run_mode mode) 435 { 436 // We don't support offline run mode, so broadcast an error if we're set to 437 // B_OFFLINE. Unfortunately, we can't actually reject the mode change... 438 if (B_OFFLINE == mode) 439 { 440 ReportError(B_NODE_FAILED_SET_RUN_MODE); 441 } 442 } 443 444 445 void 446 GameProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) 447 { 448 // FPRINTF(stderr, "ToneProducer::HandleEvent\n"); 449 switch (event->type) 450 { 451 case BTimedEventQueue::B_START: 452 // don't do anything if we're already running 453 if (RunState() != B_STARTED) 454 { 455 // We are going to start sending buffers so setup the needed bookkeeping 456 fFramesSent = 0; 457 fStartTime = event->event_time; 458 media_timed_event firstBufferEvent(fStartTime, BTimedEventQueue::B_HANDLE_BUFFER); 459 460 // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through 461 // the event queue, like this: 462 // 463 // this->HandleEvent(&firstBufferEvent, 0, false); 464 // 465 EventQueue()->AddEvent(firstBufferEvent); 466 } 467 break; 468 469 case BTimedEventQueue::B_STOP: 470 // When we handle a stop, we must ensure that downstream consumers don't 471 // get any more buffers from us. This means we have to flush any pending 472 // buffer-producing events from the queue. 473 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 474 break; 475 476 case BTimedEventQueue::B_HANDLE_BUFFER: 477 { 478 // make sure we're both started *and* connected before delivering a buffer 479 if ((RunState() == BMediaEventLooper::B_STARTED) && (fOutput.destination != media_destination::null)) 480 { 481 // Get the next buffer of data 482 BBuffer* buffer = FillNextBuffer(event->event_time); 483 if (buffer) 484 { 485 // send the buffer downstream if and only if output is enabled 486 status_t err = B_ERROR; 487 if (fOutputEnabled) err = SendBuffer(buffer, fOutput.destination); 488 if (err) 489 { 490 // we need to recycle the buffer ourselves if output is disabled or 491 // if the call to SendBuffer() fails 492 buffer->Recycle(); 493 } 494 } 495 496 // track how much media we've delivered so far 497 size_t nFrames = fOutput.format.u.raw_audio.buffer_size / fFrameSize; 498 fFramesSent += nFrames; 499 500 // The buffer is on its way; now schedule the next one to go 501 bigtime_t nextEvent = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0); 502 media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER); 503 EventQueue()->AddEvent(nextBufferEvent); 504 } 505 } 506 break; 507 508 default: 509 break; 510 } 511 } 512 513 514 // -------------------------------- 515 // GameProducer 516 BBuffer* 517 GameProducer::FillNextBuffer(bigtime_t event_time) 518 { 519 // get a buffer from our buffer group 520 BBuffer* buf = fBufferGroup->RequestBuffer(fOutput.format.u.raw_audio.buffer_size, BufferDuration()); 521 522 // if we fail to get a buffer (for example, if the request times out), we skip this 523 // buffer and go on to the next, to avoid locking up the control thread 524 if (!buf) return NULL; 525 526 // we need to discribe the buffer 527 int64 frames = int64(fOutput.format.u.raw_audio.buffer_size / fFrameSize); 528 memset(buf->Data(), 0, fOutput.format.u.raw_audio.buffer_size); 529 530 // now fill the buffer with data, continuing where the last buffer left off 531 fObject->Play(buf->Data(), frames); 532 533 // fill in the buffer header 534 media_header* hdr = buf->Header(); 535 hdr->type = B_MEDIA_RAW_AUDIO; 536 hdr->size_used = fOutput.format.u.raw_audio.buffer_size; 537 hdr->time_source = TimeSource()->ID(); 538 539 bigtime_t stamp; 540 if (RunMode() == B_RECORDING) 541 { 542 // In B_RECORDING mode, we stamp with the capture time. We're not 543 // really a hardware capture node, but we simulate it by using the (precalculated) 544 // time at which this buffer "should" have been created. 545 stamp = event_time; 546 } 547 else 548 { 549 // okay, we're in one of the "live" performance run modes. in these modes, we 550 // stamp the buffer with the time at which the buffer should be rendered to the 551 // output, not with the capture time. fStartTime is the cached value of the 552 // first buffer's performance time; we calculate this buffer's performance time as 553 // an offset from that time, based on the amount of media we've created so far. 554 // Recalculating every buffer like this avoids accumulation of error. 555 stamp = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0); 556 } 557 hdr->start_time = stamp; 558 559 return buf; 560 } 561 562 563