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