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