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 = 4096; // 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 fOutput.destination = media_destination::null; 222 fOutput.format = fPreferredFormat; 223 return; 224 } 225 226 // Okay, the connection has been confirmed. Record the destination and format 227 // that we agreed on, and report our connection name again. 228 fOutput.destination = destination; 229 fOutput.format = format; 230 strncpy(io_name, fOutput.name, B_MEDIA_NAME_LENGTH); 231 232 // Now that we're connected, we can determine our downstream latency. 233 // Do so, then make sure we get our events early enough. 234 media_node_id id; 235 FindLatencyFor(fOutput.destination, &fLatency, &id); 236 237 // Use a dry run to see how long it takes me to fill a buffer of data 238 239 // The first step to setup the buffer 240 bigtime_t start, produceLatency; 241 int32 frames = int32(fOutput.format.u.raw_audio.buffer_size / fFrameSize); 242 float* data = new float[frames * 2]; 243 244 // Second, fill the buffer 245 start = ::system_time(); 246 for (int32 i = 0; i < frames; i++) { 247 data[i*2] = 0.8 * float(i/frames); 248 data[i*2+1] = 0.8 * float(i/frames); 249 } 250 produceLatency = ::system_time(); 251 252 // Third, calculate the latency 253 fInternalLatency = produceLatency - start; 254 SetEventLatency(fLatency + fInternalLatency); 255 256 // Finaily, clean up 257 delete [] data; 258 259 // reset our buffer duration, etc. to avoid later calculations 260 bigtime_t duration = bigtime_t(1000000) * frames / bigtime_t(fOutput.format.u.raw_audio.frame_rate); 261 SetBufferDuration(duration); 262 263 // Set up the buffer group for our connection, as long as nobody handed us a 264 // buffer group (via SetBufferGroup()) prior to this. 265 if (!fBufferGroup) { 266 size_t size = fOutput.format.u.raw_audio.buffer_size; 267 int32 count = int32(fLatency / BufferDuration() + 2); 268 fBufferGroup = new BBufferGroup(size, count); 269 } 270 } 271 272 273 void 274 GameProducer::Disconnect(const media_source& what, const media_destination& where) 275 { 276 // Make sure that our connection is the one being disconnected 277 if ((where == fOutput.destination) && (what == fOutput.source)) { 278 fOutput.destination = media_destination::null; 279 fOutput.format = fPreferredFormat; 280 delete fBufferGroup; 281 fBufferGroup = NULL; 282 } 283 } 284 285 286 status_t 287 GameProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_) 288 { 289 // we don't support any other formats, so we just reject any format changes. 290 return B_ERROR; 291 } 292 293 294 status_t 295 GameProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup) 296 { 297 // verify that we didn't get bogus arguments before we proceed 298 if (for_source != fOutput.source) 299 return B_MEDIA_BAD_SOURCE; 300 301 // Are we being passed the buffer group we're already using? 302 if (newGroup == fBufferGroup) 303 return B_OK; 304 305 // Ahh, someone wants us to use a different buffer group. At this point we delete 306 // the one we are using and use the specified one instead. If the specified group is 307 // NULL, we need to recreate one ourselves, and use *that*. Note that if we're 308 // caching a BBuffer that we requested earlier, we have to Recycle() that buffer 309 // *before* deleting the buffer group, otherwise we'll deadlock waiting for that 310 // buffer to be recycled! 311 delete fBufferGroup; // waits for all buffers to recycle 312 if (newGroup != NULL) { 313 // we were given a valid group; just use that one from now on 314 fBufferGroup = newGroup; 315 } else { 316 // we were passed a NULL group pointer; that means we construct 317 // our own buffer group to use from now on 318 size_t size = fOutput.format.u.raw_audio.buffer_size; 319 int32 count = int32(fLatency / BufferDuration() + 2); 320 fBufferGroup = new BBufferGroup(size, count); 321 } 322 323 return B_OK; 324 } 325 326 327 status_t 328 GameProducer::GetLatency(bigtime_t* out_latency) 329 { 330 // report our *total* latency: internal plus downstream plus scheduling 331 *out_latency = EventLatency() + SchedulingLatency(); 332 return B_OK; 333 } 334 335 336 void 337 GameProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time) 338 { 339 // If we're late, we need to catch up. Respond in a manner appropriate to our 340 // current run mode. 341 if (what == fOutput.source) { 342 if (RunMode() == B_RECORDING) { 343 // A hardware capture node can't adjust; it simply emits buffers at 344 // appropriate points. We (partially) simulate this by not adjusting 345 // our behavior upon receiving late notices -- after all, the hardware 346 // can't choose to capture "sooner".... 347 } else if (RunMode() == B_INCREASE_LATENCY) { 348 // We're late, and our run mode dictates that we try to produce buffers 349 // earlier in order to catch up. This argues that the downstream nodes are 350 // not properly reporting their latency, but there's not much we can do about 351 // that at the moment, so we try to start producing buffers earlier to 352 // compensate. 353 fInternalLatency += how_much; 354 SetEventLatency(fLatency + fInternalLatency); 355 } else { 356 // The other run modes dictate various strategies for sacrificing data quality 357 // in the interests of timely data delivery. The way *we* do this is to skip 358 // a buffer, which catches us up in time by one buffer duration. 359 size_t nSamples = fOutput.format.u.raw_audio.buffer_size / fFrameSize; 360 fFramesSent += nSamples; 361 } 362 } 363 } 364 365 366 void 367 GameProducer::LatencyChanged(const media_source& source, const media_destination& destination, bigtime_t new_latency, uint32 flags) 368 { 369 // something downstream changed latency, so we need to start producing 370 // buffers earlier (or later) than we were previously. Make sure that the 371 // connection that changed is ours, and adjust to the new downstream 372 // latency if so. 373 if ((source == fOutput.source) && (destination == fOutput.destination)) { 374 fLatency = new_latency; 375 SetEventLatency(fLatency + fInternalLatency); 376 } 377 } 378 379 380 status_t 381 GameProducer::SetPlayRate(int32 numer, int32 denom) 382 { 383 // Play rates are weird. We don't support them 384 return B_ERROR; 385 } 386 387 388 status_t 389 GameProducer::HandleMessage(int32 message, const void* data, size_t size) 390 { 391 // We currently do not handle private messages 392 return B_ERROR; 393 } 394 395 396 void 397 GameProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag) 398 { 399 // we don't support offline mode (yet...) 400 return; 401 } 402 403 404 // BMediaEventLooper methods 405 void 406 GameProducer::NodeRegistered() 407 { 408 // Start the BMediaEventLooper thread 409 SetPriority(B_REAL_TIME_PRIORITY); 410 Run(); 411 412 // set up as much information about our output as we can 413 fOutput.source.port = ControlPort(); 414 fOutput.source.id = 0; 415 fOutput.node = Node(); 416 ::strcpy(fOutput.name, "GameProducer Output"); 417 } 418 419 420 void 421 GameProducer::SetRunMode(run_mode mode) 422 { 423 // We don't support offline run mode, so broadcast an error if we're set to 424 // B_OFFLINE. Unfortunately, we can't actually reject the mode change... 425 if (B_OFFLINE == mode) { 426 ReportError(B_NODE_FAILED_SET_RUN_MODE); 427 } 428 } 429 430 431 void 432 GameProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) 433 { 434 // FPRINTF(stderr, "ToneProducer::HandleEvent\n"); 435 switch (event->type) 436 { 437 case BTimedEventQueue::B_START: 438 // don't do anything if we're already running 439 if (RunState() != B_STARTED) { 440 // We are going to start sending buffers so setup the needed bookkeeping 441 fFramesSent = 0; 442 fStartTime = event->event_time; 443 media_timed_event firstBufferEvent(fStartTime, BTimedEventQueue::B_HANDLE_BUFFER); 444 445 // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through 446 // the event queue, like this: 447 // 448 // this->HandleEvent(&firstBufferEvent, 0, false); 449 // 450 EventQueue()->AddEvent(firstBufferEvent); 451 } 452 break; 453 454 case BTimedEventQueue::B_STOP: 455 // When we handle a stop, we must ensure that downstream consumers don't 456 // get any more buffers from us. This means we have to flush any pending 457 // buffer-producing events from the queue. 458 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 459 break; 460 461 case BTimedEventQueue::B_HANDLE_BUFFER: 462 { 463 // make sure we're both started *and* connected before delivering a buffer 464 if ((RunState() == BMediaEventLooper::B_STARTED) 465 && (fOutput.destination != media_destination::null)) { 466 // Get the next buffer of data 467 BBuffer* buffer = FillNextBuffer(event->event_time); 468 if (buffer) { 469 // send the buffer downstream if and only if output is enabled 470 status_t err = B_ERROR; 471 if (fOutputEnabled) 472 err = SendBuffer(buffer, fOutput.destination); 473 if (err) { 474 // we need to recycle the buffer ourselves if output is disabled or 475 // if the call to SendBuffer() fails 476 buffer->Recycle(); 477 } 478 } 479 480 // track how much media we've delivered so far 481 size_t nFrames = fOutput.format.u.raw_audio.buffer_size / fFrameSize; 482 fFramesSent += nFrames; 483 484 // The buffer is on its way; now schedule the next one to go 485 bigtime_t nextEvent = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0); 486 media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER); 487 EventQueue()->AddEvent(nextBufferEvent); 488 } 489 } 490 break; 491 492 default: 493 break; 494 } 495 } 496 497 498 BBuffer* 499 GameProducer::FillNextBuffer(bigtime_t event_time) 500 { 501 // get a buffer from our buffer group 502 BBuffer* buf = fBufferGroup->RequestBuffer(fOutput.format.u.raw_audio.buffer_size, BufferDuration()); 503 504 // if we fail to get a buffer (for example, if the request times out), we skip this 505 // buffer and go on to the next, to avoid locking up the control thread 506 if (!buf) 507 return NULL; 508 509 // we need to discribe the buffer 510 int64 frames = int64(fOutput.format.u.raw_audio.buffer_size / fFrameSize); 511 memset(buf->Data(), 0, fOutput.format.u.raw_audio.buffer_size); 512 513 // now fill the buffer with data, continuing where the last buffer left off 514 fObject->Play(buf->Data(), frames); 515 516 // fill in the buffer header 517 media_header* hdr = buf->Header(); 518 hdr->type = B_MEDIA_RAW_AUDIO; 519 hdr->size_used = fOutput.format.u.raw_audio.buffer_size; 520 hdr->time_source = TimeSource()->ID(); 521 522 bigtime_t stamp; 523 if (RunMode() == B_RECORDING) { 524 // In B_RECORDING mode, we stamp with the capture time. We're not 525 // really a hardware capture node, but we simulate it by using the (precalculated) 526 // time at which this buffer "should" have been created. 527 stamp = event_time; 528 } else { 529 // okay, we're in one of the "live" performance run modes. in these modes, we 530 // stamp the buffer with the time at which the buffer should be rendered to the 531 // output, not with the capture time. fStartTime is the cached value of the 532 // first buffer's performance time; we calculate this buffer's performance time as 533 // an offset from that time, based on the amount of media we've created so far. 534 // Recalculating every buffer like this avoids accumulation of error. 535 stamp = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0); 536 } 537 hdr->start_time = stamp; 538 539 return buf; 540 } 541 542