1 /* 2 ToneProducer.cpp 3 4 Copyright 1999, Be Incorporated. All Rights Reserved. 5 This file may be used under the terms of the Be Sample Code License. 6 7 NOTE: to compile this code under Genki beta releases, do a search- 8 and-replace to change "B_PARAMETER" to "B_USER_EVENT+1" 9 */ 10 11 #include "ToneProducer.h" 12 #include <support/ByteOrder.h> 13 #include <media/BufferGroup.h> 14 #include <media/Buffer.h> 15 #include <media/TimeSource.h> 16 #include <media/ParameterWeb.h> 17 #include <media/MediaDefs.h> 18 #include <string.h> 19 #include <stdio.h> 20 #include <math.h> 21 22 #include <Messenger.h> 23 24 #include <Debug.h> 25 #if DEBUG 26 #define FPRINTF fprintf 27 #else 28 static inline void FPRINTF(FILE*, const char*, ...) { } 29 #endif 30 31 // parameter web handling 32 static BParameterWeb* make_parameter_web(); 33 const int32 FREQUENCY_NULL_PARAM = 1; 34 const int32 FREQUENCY_PARAM = 2; 35 const int32 GAIN_NULL_PARAM = 11; 36 const int32 GAIN_PARAM = 12; 37 const int32 WAVEFORM_NULL_PARAM = 21; 38 const int32 WAVEFORM_PARAM = 22; 39 const int32 SINE_WAVE = 90; 40 const int32 TRIANGLE_WAVE = 91; 41 const int32 SAWTOOTH_WAVE = 92; 42 43 // ---------------- 44 // ToneProducer implementation 45 46 ToneProducer::ToneProducer(BMediaAddOn* pAddOn) 47 : BMediaNode("ToneProducer"), 48 BBufferProducer(B_MEDIA_RAW_AUDIO), 49 BControllable(), 50 BMediaEventLooper(), 51 mWeb(NULL), 52 mBufferGroup(NULL), 53 mLatency(0), 54 mInternalLatency(0), 55 mOutputEnabled(true), 56 mTheta(0.0), 57 mWaveAscending(true), 58 mFrequency(440), 59 mGain(0.25), 60 mWaveform(SINE_WAVE), 61 mFramesSent(0), 62 mStartTime(0), 63 mGainLastChanged(0), 64 mFreqLastChanged(0), 65 mWaveLastChanged(0), 66 m_pAddOn(pAddOn) 67 { 68 // initialize our preferred format object 69 mPreferredFormat.type = B_MEDIA_RAW_AUDIO; 70 mPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; 71 mPreferredFormat.u.raw_audio.frame_rate = 44100; // measured in Hertz 72 mPreferredFormat.u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN; 73 74 // we'll use the consumer's preferred buffer size, if any 75 mPreferredFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size; 76 77 // 20sep99: multiple-channel support 78 mPreferredFormat.u.raw_audio.channel_count = media_raw_audio_format::wildcard.channel_count; 79 80 81 // we're not connected yet 82 mOutput.destination = media_destination::null; 83 84 // [e.moon 1dec99] 85 mOutput.format = mPreferredFormat; 86 87 // set up as much information about our output as we can 88 // +++++ wrong; can't call Node() until the node is registered! 89 mOutput.source.port = ControlPort(); 90 mOutput.source.id = 0; 91 mOutput.node = Node(); 92 ::strcpy(mOutput.name, "ToneProducer Output"); 93 } 94 95 ToneProducer::~ToneProducer() 96 { 97 // Stop the BMediaEventLooper thread 98 Quit(); 99 100 // the BControllable destructor deletes our parameter web for us; we just use 101 // a little defensive programming here and set our web pointer to be NULL. 102 mWeb = NULL; 103 } 104 105 //#pragma mark - 106 107 // BMediaNode methods 108 BMediaAddOn * 109 ToneProducer::AddOn(int32 *internal_id) const 110 { 111 // e.moon [8jun99] 112 if(m_pAddOn) { 113 *internal_id = 0; 114 return m_pAddOn; 115 } else 116 return NULL; 117 } 118 119 //#pragma mark - 120 121 // BControllable methods 122 status_t 123 ToneProducer::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize) 124 { 125 FPRINTF(stderr, "ToneProducer::GetParameterValue\n"); 126 127 // floats & int32s are the same size, so this one test of the size of the 128 // output buffer is sufficient for all of our parameters 129 if (*ioSize < sizeof(float)) return B_ERROR; 130 131 // fill in the value of the requested parameter 132 switch (id) 133 { 134 case FREQUENCY_PARAM: 135 *last_change = mFreqLastChanged; 136 *((float*) value) = mFrequency; 137 *ioSize = sizeof(float); 138 break; 139 140 case GAIN_PARAM: 141 *last_change = mGainLastChanged; 142 *((float*) value) = mGain; 143 *ioSize = sizeof(float); 144 break; 145 146 case WAVEFORM_PARAM: 147 *last_change = mWaveLastChanged; 148 *((int32*) value) = mWaveform; 149 *ioSize = sizeof(int32); 150 break; 151 152 default: 153 // Hmmm, we were asked for a parameter that we don't actually 154 // support. Report an error back to the caller. 155 FPRINTF(stderr, "\terror - asked for illegal parameter %" B_PRId32 "\n", 156 id); 157 return B_ERROR; 158 break; 159 } 160 161 return B_OK; 162 } 163 164 void 165 ToneProducer::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size) 166 { 167 switch (id) 168 { 169 case FREQUENCY_PARAM: 170 case GAIN_PARAM: 171 case WAVEFORM_PARAM: 172 { 173 // floats and int32s are the same size, so we need only check the block's size once 174 if (size > sizeof(float)) size = sizeof(float); 175 176 // submit the parameter change as a performance event, to be handled at the 177 // appropriate time 178 media_timed_event event(performance_time, _PARAMETER_EVENT, 179 NULL, BTimedEventQueue::B_NO_CLEANUP, size, id, (char*) value, size); 180 EventQueue()->AddEvent(event); 181 } 182 break; 183 184 default: 185 break; 186 } 187 } 188 189 // e.moon [17jun99] 190 status_t ToneProducer::StartControlPanel( 191 BMessenger* pMessenger) { 192 PRINT(("ToneProducer::StartControlPanel(%p)\n", pMessenger)); 193 status_t err = BControllable::StartControlPanel(pMessenger); 194 if(pMessenger && pMessenger->IsValid()) { 195 PRINT(("\tgot valid control panel\n")); 196 } 197 198 return err; 199 } 200 201 //#pragma mark - 202 203 // BBufferProducer methods 204 status_t 205 ToneProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format) 206 { 207 // FormatSuggestionRequested() is not necessarily part of the format negotiation 208 // process; it's simply an interrogation -- the caller wants to see what the node's 209 // preferred data format is, given a suggestion by the caller. 210 FPRINTF(stderr, "ToneProducer::FormatSuggestionRequested\n"); 211 212 if (!format) 213 { 214 FPRINTF(stderr, "\tERROR - NULL format pointer passed in!\n"); 215 return B_BAD_VALUE; 216 } 217 218 // this is the format we'll be returning (our preferred format) 219 *format = mPreferredFormat; 220 221 // a wildcard type is okay; we can specialize it 222 if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO; 223 224 // we only support raw audio 225 if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT; 226 else return B_OK; 227 } 228 229 status_t 230 ToneProducer::FormatProposal(const media_source& output, media_format* format) 231 { 232 // FormatProposal() is the first stage in the BMediaRoster::Connect() process. We hand 233 // out a suggested format, with wildcards for any variations we support. 234 FPRINTF(stderr, "ToneProducer::FormatProposal\n"); 235 236 // is this a proposal for our one output? 237 if (output != mOutput.source) 238 { 239 FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_SOURCE\n"); 240 return B_MEDIA_BAD_SOURCE; 241 } 242 243 // we only support floating-point raw audio, so we always return that, but we 244 // supply an error code depending on whether we found the proposal acceptable. 245 246 media_type requestedType = format->type; 247 *format = mPreferredFormat; 248 if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO)) 249 { 250 FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_FORMAT\n"); 251 return B_MEDIA_BAD_FORMAT; 252 } 253 else return B_OK; // raw audio or wildcard type, either is okay by us 254 } 255 256 status_t 257 ToneProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_) 258 { 259 FPRINTF(stderr, "ToneProducer::FormatChangeRequested\n"); 260 261 // we don't support any other formats, so we just reject any format changes. 262 return B_ERROR; 263 } 264 265 status_t 266 ToneProducer::GetNextOutput(int32* cookie, media_output* out_output) 267 { 268 FPRINTF(stderr, "ToneProducer::GetNextOutput\n"); 269 270 // we have only a single output; if we supported multiple outputs, we'd 271 // iterate over whatever data structure we were using to keep track of 272 // them. 273 if (0 == *cookie) 274 { 275 *out_output = mOutput; 276 *cookie += 1; 277 return B_OK; 278 } 279 else return B_BAD_INDEX; 280 } 281 282 status_t 283 ToneProducer::DisposeOutputCookie(int32 cookie) 284 { 285 FPRINTF(stderr, "ToneProducer::DisposeOutputCookie\n"); 286 287 // do nothing because we don't use the cookie for anything special 288 return B_OK; 289 } 290 291 status_t 292 ToneProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup) 293 { 294 FPRINTF(stderr, "ToneProducer::SetBufferGroup\n"); 295 296 // verify that we didn't get bogus arguments before we proceed 297 if (for_source != mOutput.source) return B_MEDIA_BAD_SOURCE; 298 299 // Are we being passed the buffer group we're already using? 300 if (newGroup == mBufferGroup) return B_OK; 301 302 // Ahh, someone wants us to use a different buffer group. At this point we delete 303 // the one we are using and use the specified one instead. If the specified group is 304 // NULL, we need to recreate one ourselves, and use *that*. Note that if we're 305 // caching a BBuffer that we requested earlier, we have to Recycle() that buffer 306 // *before* deleting the buffer group, otherwise we'll deadlock waiting for that 307 // buffer to be recycled! 308 delete mBufferGroup; // waits for all buffers to recycle 309 if (newGroup != NULL) 310 { 311 // we were given a valid group; just use that one from now on 312 mBufferGroup = newGroup; 313 } 314 else 315 { 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 = mOutput.format.u.raw_audio.buffer_size; 319 int32 count = int32(mLatency / BufferDuration() + 1 + 1); 320 mBufferGroup = new BBufferGroup(size, count); 321 } 322 323 return B_OK; 324 } 325 326 status_t 327 ToneProducer::GetLatency(bigtime_t* out_latency) 328 { 329 FPRINTF(stderr, "ToneProducer::GetLatency\n"); 330 331 // report our *total* latency: internal plus downstream plus scheduling 332 *out_latency = EventLatency() + SchedulingLatency(); 333 return B_OK; 334 } 335 336 status_t 337 ToneProducer::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name) 338 { 339 // PrepareToConnect() is the second stage of format negotiations that happens 340 // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat() 341 // method has been called, and that node has potentially changed the proposed 342 // format. It may also have left wildcards in the format. PrepareToConnect() 343 // *must* fully specialize the format before returning! 344 FPRINTF(stderr, "ToneProducer::PrepareToConnect\n"); 345 346 // trying to connect something that isn't our source? 347 if (what != mOutput.source) return B_MEDIA_BAD_SOURCE; 348 349 // are we already connected? 350 if (mOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED; 351 352 // the format may not yet be fully specialized (the consumer might have 353 // passed back some wildcards). Finish specializing it now, and return an 354 // error if we don't support the requested format. 355 if (format->type != B_MEDIA_RAW_AUDIO) 356 { 357 FPRINTF(stderr, "\tnon-raw-audio format?!\n"); 358 return B_MEDIA_BAD_FORMAT; 359 } 360 else if (format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_FLOAT) 361 { 362 FPRINTF(stderr, "\tnon-float-audio format?!\n"); 363 return B_MEDIA_BAD_FORMAT; 364 } 365 else if(format->u.raw_audio.channel_count > 2) { 366 format->u.raw_audio.channel_count = 2; 367 return B_MEDIA_BAD_FORMAT; 368 } 369 370 371 // !!! validate all other fields except for buffer_size here, because the consumer might have 372 // supplied different values from AcceptFormat()? 373 374 // *** e.moon [11jun99]: filling in sensible field values. 375 // Connect() doesn't take kindly to a frame_rate of 0. 376 377 if(format->u.raw_audio.frame_rate == media_raw_audio_format::wildcard.frame_rate) { 378 format->u.raw_audio.frame_rate = mPreferredFormat.u.raw_audio.frame_rate; 379 FPRINTF(stderr, "\tno frame rate provided, suggesting %.1f\n", format->u.raw_audio.frame_rate); 380 } 381 if(format->u.raw_audio.channel_count == media_raw_audio_format::wildcard.channel_count) { 382 //format->u.raw_audio.channel_count = mPreferredFormat.u.raw_audio.channel_count; 383 format->u.raw_audio.channel_count = 1; 384 FPRINTF(stderr, "\tno channel count provided, suggesting %" B_PRIu32 "\n", format->u.raw_audio.channel_count); 385 } 386 if(format->u.raw_audio.byte_order == media_raw_audio_format::wildcard.byte_order) { 387 format->u.raw_audio.byte_order = mPreferredFormat.u.raw_audio.byte_order; 388 FPRINTF(stderr, "\tno channel count provided, suggesting %s\n", 389 (format->u.raw_audio.byte_order == B_MEDIA_BIG_ENDIAN) ? "B_MEDIA_BIG_ENDIAN" : "B_MEDIA_LITTLE_ENDIAN"); 390 } 391 392 // check the buffer size, which may still be wildcarded 393 if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size) 394 { 395 format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest 396 FPRINTF(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size); 397 } 398 else 399 { 400 FPRINTF(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size); 401 } 402 403 // Now reserve the connection, and return information about it 404 mOutput.destination = where; 405 mOutput.format = *format; 406 *out_source = mOutput.source; 407 strncpy(out_name, mOutput.name, B_MEDIA_NAME_LENGTH); 408 409 char formatStr[256]; 410 string_for_format(*format, formatStr, 255); 411 FPRINTF(stderr, "\treturning format: %s\n", formatStr); 412 413 return B_OK; 414 } 415 416 void 417 ToneProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) 418 { 419 FPRINTF(stderr, "ToneProducer::Connect\n"); 420 421 // If something earlier failed, Connect() might still be called, but with a non-zero 422 // error code. When that happens we simply unreserve the connection and do 423 // nothing else. 424 if (error) 425 { 426 mOutput.destination = media_destination::null; 427 mOutput.format = mPreferredFormat; 428 return; 429 } 430 431 // old workaround for format bug: Connect() receives the format data from the 432 // input returned from BBufferConsumer::Connected(). 433 // 434 // char formatStr[256]; 435 // string_for_format(format, formatStr, 255); 436 // FPRINTF(stderr, "\trequested format: %s\n", formatStr); 437 // if(format.type != B_MEDIA_RAW_AUDIO) { 438 // // +++++ this is NOT proper behavior 439 // // but it works 440 // FPRINTF(stderr, "\tcorrupted format; falling back to last suggested format\n"); 441 // format = mOutput.format; 442 // } 443 // 444 445 // Okay, the connection has been confirmed. Record the destination and format 446 // that we agreed on, and report our connection name again. 447 mOutput.destination = destination; 448 mOutput.format = format; 449 strncpy(io_name, mOutput.name, B_MEDIA_NAME_LENGTH); 450 451 // Now that we're connected, we can determine our downstream latency. 452 // Do so, then make sure we get our events early enough. 453 media_node_id id; 454 FindLatencyFor(mOutput.destination, &mLatency, &id); 455 FPRINTF(stderr, "\tdownstream latency = %" B_PRIdBIGTIME "\n", mLatency); 456 457 // Use a dry run to see how long it takes me to fill a buffer of data 458 bigtime_t start, produceLatency; 459 size_t samplesPerBuffer = mOutput.format.u.raw_audio.buffer_size / sizeof(float); 460 size_t framesPerBuffer = samplesPerBuffer / mOutput.format.u.raw_audio.channel_count; 461 float* data = new float[samplesPerBuffer]; 462 mTheta = 0; 463 start = ::system_time(); 464 FillSineBuffer(data, framesPerBuffer, mOutput.format.u.raw_audio.channel_count==2); 465 produceLatency = ::system_time(); 466 mInternalLatency = produceLatency - start; 467 468 // +++++ e.moon [13jun99]: fiddling with latency, ick 469 mInternalLatency += 20000LL; 470 471 delete [] data; 472 FPRINTF(stderr, "\tbuffer-filling took %" B_PRIdBIGTIME 473 " usec on this machine\n", mInternalLatency); 474 SetEventLatency(mLatency + mInternalLatency); 475 476 // reset our buffer duration, etc. to avoid later calculations 477 // +++++ e.moon 11jun99: crashes w/ divide-by-zero when connecting to LoggingConsumer 478 ASSERT(mOutput.format.u.raw_audio.frame_rate); 479 480 bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(mOutput.format.u.raw_audio.frame_rate); 481 SetBufferDuration(duration); 482 483 // Set up the buffer group for our connection, as long as nobody handed us a 484 // buffer group (via SetBufferGroup()) prior to this. That can happen, for example, 485 // if the consumer calls SetOutputBuffersFor() on us from within its Connected() 486 // method. 487 if (!mBufferGroup) AllocateBuffers(); 488 } 489 490 void 491 ToneProducer::Disconnect(const media_source& what, const media_destination& where) 492 { 493 FPRINTF(stderr, "ToneProducer::Disconnect\n"); 494 495 // Make sure that our connection is the one being disconnected 496 if ((where == mOutput.destination) && (what == mOutput.source)) 497 { 498 mOutput.destination = media_destination::null; 499 mOutput.format = mPreferredFormat; 500 delete mBufferGroup; 501 mBufferGroup = NULL; 502 } 503 else 504 { 505 FPRINTF(stderr, "\tDisconnect() called with wrong source/destination (%" 506 B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32 ")\n", 507 what.id, where.id, mOutput.source.id, mOutput.destination.id); 508 } 509 } 510 511 void 512 ToneProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time) 513 { 514 FPRINTF(stderr, "ToneProducer::LateNoticeReceived\n"); 515 516 // If we're late, we need to catch up. Respond in a manner appropriate to our 517 // current run mode. 518 if (what == mOutput.source) 519 { 520 if (RunMode() == B_RECORDING) 521 { 522 // A hardware capture node can't adjust; it simply emits buffers at 523 // appropriate points. We (partially) simulate this by not adjusting 524 // our behavior upon receiving late notices -- after all, the hardware 525 // can't choose to capture "sooner".... 526 } 527 else if (RunMode() == B_INCREASE_LATENCY) 528 { 529 // We're late, and our run mode dictates that we try to produce buffers 530 // earlier in order to catch up. This argues that the downstream nodes are 531 // not properly reporting their latency, but there's not much we can do about 532 // that at the moment, so we try to start producing buffers earlier to 533 // compensate. 534 mInternalLatency += how_much; 535 if (mInternalLatency > 50000) 536 mInternalLatency = 50000; 537 SetEventLatency(mLatency + mInternalLatency); 538 539 FPRINTF(stderr, "\tincreasing latency to %" B_PRIdBIGTIME "\n", 540 mLatency + mInternalLatency); 541 } 542 else 543 { 544 // The other run modes dictate various strategies for sacrificing data quality 545 // in the interests of timely data delivery. The way *we* do this is to skip 546 // a buffer, which catches us up in time by one buffer duration. 547 size_t nSamples = mOutput.format.u.raw_audio.buffer_size / sizeof(float); 548 mFramesSent += nSamples; 549 550 FPRINTF(stderr, "\tskipping a buffer to try to catch up\n"); 551 } 552 } 553 } 554 555 void 556 ToneProducer::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_) 557 { 558 FPRINTF(stderr, "ToneProducer::EnableOutput\n"); 559 560 // If I had more than one output, I'd have to walk my list of output records to see 561 // which one matched the given source, and then enable/disable that one. But this 562 // node only has one output, so I just make sure the given source matches, then set 563 // the enable state accordingly. 564 if (what == mOutput.source) 565 { 566 mOutputEnabled = enabled; 567 } 568 } 569 570 status_t 571 ToneProducer::SetPlayRate(int32 numer, int32 denom) 572 { 573 FPRINTF(stderr, "ToneProducer::SetPlayRate\n"); 574 575 // Play rates are weird. We don't support them. Maybe we will in a 576 // later newsletter article.... 577 return B_ERROR; 578 } 579 580 status_t 581 ToneProducer::HandleMessage(int32 message, const void* data, size_t size) 582 { 583 FPRINTF(stderr, "ToneProducer::HandleMessage(%" B_PRId32 " = 0x%" B_PRIx32 584 ")\n", message, message); 585 // HandleMessage() is where you implement any private message protocols 586 // that you want to use. When messages are written to your node's control 587 // port that are not recognized by any of the node superclasses, they'll be 588 // passed to this method in your node's implementation for handling. The 589 // ToneProducer node doesn't support any private messages, so we just 590 // return an error, indicating that the message wasn't handled. 591 return B_ERROR; 592 } 593 594 void 595 ToneProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag) 596 { 597 FPRINTF(stderr, "ToneProducer::AdditionalBufferRequested\n"); 598 599 // we don't support offline mode (yet...) 600 return; 601 } 602 603 void 604 ToneProducer::LatencyChanged( 605 const media_source& source, 606 const media_destination& destination, 607 bigtime_t new_latency, 608 uint32 flags) 609 { 610 PRINT(("ToneProducer::LatencyChanged(): %" B_PRIdBIGTIME "\n", 611 new_latency)); 612 613 // something downstream changed latency, so we need to start producing 614 // buffers earlier (or later) than we were previously. Make sure that the 615 // connection that changed is ours, and adjust to the new downstream 616 // latency if so. 617 if ((source == mOutput.source) && (destination == mOutput.destination)) 618 { 619 mLatency = new_latency; 620 SetEventLatency(mLatency + mInternalLatency); 621 } 622 } 623 /* 624 // Workaround for a Metrowerks PPC compiler bug 625 status_t 626 ToneProducer::DeleteHook(BMediaNode* node) 627 { 628 return BMediaEventLooper::DeleteHook(node); 629 } 630 631 //#pragma mark - 632 */ 633 634 // BMediaEventLooper methods 635 void 636 ToneProducer::NodeRegistered() 637 { 638 FPRINTF(stderr, "ToneProducer::NodeRegistered\n"); 639 640 // Start the BMediaEventLooper thread 641 SetPriority(B_REAL_TIME_PRIORITY); 642 Run(); 643 644 // output init moved to ctor 645 // e.moon [4jun99] 646 647 // Set up our parameter web 648 mWeb = make_parameter_web(); 649 SetParameterWeb(mWeb); 650 } 651 652 void 653 ToneProducer::Start(bigtime_t performance_time) 654 { 655 PRINT(("ToneProducer::Start(%" B_PRIdBIGTIME "): now %" B_PRIdBIGTIME "\n", 656 performance_time, TimeSource()->Now())); 657 658 // send 'data available' message 659 if(mOutput.destination != media_destination::null) 660 SendDataStatus(B_DATA_AVAILABLE, mOutput.destination, performance_time); 661 662 // A bug in the current PowerPC compiler demands that we implement 663 // this, even though it just calls up to the inherited implementation. 664 BMediaEventLooper::Start(performance_time); 665 } 666 667 void 668 ToneProducer::Stop(bigtime_t performance_time, bool immediate) 669 { 670 // send 'data not available' message 671 if(mOutput.destination != media_destination::null) { 672 printf("ToneProducer: B_PRODUCER_STOPPED at %" B_PRIdBIGTIME "\n", 673 performance_time); 674 SendDataStatus(B_PRODUCER_STOPPED, mOutput.destination, performance_time); 675 } 676 677 // A bug in the current PowerPC compiler demands that we implement 678 // this, even though it just calls up to the inherited implementation. 679 BMediaEventLooper::Stop(performance_time, immediate); 680 } 681 682 void 683 ToneProducer::Seek(bigtime_t media_time, bigtime_t performance_time) 684 { 685 // A bug in the current PowerPC compiler demands that we implement 686 // this, even though it just calls up to the inherited implementation. 687 BMediaEventLooper::Seek(media_time, performance_time); 688 } 689 690 void 691 ToneProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) 692 { 693 // A bug in the current PowerPC compiler demands that we implement 694 // this, even though it just calls up to the inherited implementation. 695 BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); 696 } 697 698 status_t 699 ToneProducer::AddTimer(bigtime_t at_performance_time, int32 cookie) 700 { 701 // A bug in the current PowerPC compiler demands that we implement 702 // this, even though it just calls up to the inherited implementation. 703 return BMediaEventLooper::AddTimer(at_performance_time, cookie); 704 } 705 706 void 707 ToneProducer::SetRunMode(run_mode mode) 708 { 709 FPRINTF(stderr, "ToneProducer::SetRunMode\n"); 710 711 // We don't support offline run mode, so broadcast an error if we're set to 712 // B_OFFLINE. Unfortunately, we can't actually reject the mode change... 713 if (B_OFFLINE == mode) 714 { 715 ReportError(B_NODE_FAILED_SET_RUN_MODE); 716 } 717 } 718 719 void 720 ToneProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) 721 { 722 // FPRINTF(stderr, "ToneProducer::HandleEvent\n"); 723 switch (event->type) 724 { 725 case BTimedEventQueue::B_START: 726 // don't do anything if we're already running 727 if (RunState() != B_STARTED) 728 { 729 // We want to start sending buffers now, so we set up the buffer-sending bookkeeping 730 // and fire off the first "produce a buffer" event. 731 mFramesSent = 0; 732 mTheta = 0; 733 mStartTime = event->event_time; 734 media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER); 735 736 // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through 737 // the event queue, like this: 738 // 739 // this->HandleEvent(&firstBufferEvent, 0, false); 740 // 741 EventQueue()->AddEvent(firstBufferEvent); 742 } 743 break; 744 745 case BTimedEventQueue::B_STOP: 746 FPRINTF(stderr, "Handling B_STOP event\n"); 747 748 // When we handle a stop, we must ensure that downstream consumers don't 749 // get any more buffers from us. This means we have to flush any pending 750 // buffer-producing events from the queue. 751 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 752 break; 753 754 case _PARAMETER_EVENT: 755 { 756 size_t dataSize = size_t(event->data); 757 int32 param = int32(event->bigdata); 758 if (dataSize >= sizeof(float)) switch (param) 759 { 760 case FREQUENCY_PARAM: 761 { 762 float newValue = *((float*) event->user_data); 763 if (mFrequency != newValue) // an actual change in the value? 764 { 765 mFrequency = newValue; 766 mFreqLastChanged = TimeSource()->Now(); 767 BroadcastNewParameterValue(mFreqLastChanged, param, &mFrequency, sizeof(mFrequency)); 768 } 769 } 770 break; 771 772 case GAIN_PARAM: 773 { 774 float newValue = *((float*) event->user_data); 775 if (mGain != newValue) 776 { 777 mGain = newValue; 778 mGainLastChanged = TimeSource()->Now(); 779 BroadcastNewParameterValue(mGainLastChanged, param, &mGain, sizeof(mGain)); 780 } 781 } 782 break; 783 784 case WAVEFORM_PARAM: 785 { 786 int32 newValue = *((int32*) event->user_data); 787 if (mWaveform != newValue) 788 { 789 mWaveform = newValue; 790 mTheta = 0; // reset the generator parameters when we change waveforms 791 mWaveAscending = true; 792 mWaveLastChanged = TimeSource()->Now(); 793 BroadcastNewParameterValue(mWaveLastChanged, param, &mWaveform, sizeof(mWaveform)); 794 } 795 } 796 break; 797 798 default: 799 FPRINTF(stderr, "Hmmm... got a B_PARAMETER event for a parameter we don't have? (%" B_PRId32 ")\n", param); 800 break; 801 } 802 } 803 break; 804 805 case BTimedEventQueue::B_HANDLE_BUFFER: 806 { 807 // make sure we're both started *and* connected before delivering a buffer 808 if (RunState() == BMediaEventLooper::B_STARTED 809 && mOutput.destination != media_destination::null) { 810 // Get the next buffer of data 811 BBuffer* buffer = FillNextBuffer(event->event_time); 812 if (buffer) { 813 // send the buffer downstream if and only if output is enabled 814 status_t err = B_ERROR; 815 if (mOutputEnabled) { 816 err = SendBuffer(buffer, mOutput.source, 817 mOutput.destination); 818 } 819 if (err) { 820 // we need to recycle the buffer ourselves if output is disabled or 821 // if the call to SendBuffer() fails 822 buffer->Recycle(); 823 } 824 } 825 826 // track how much media we've delivered so far 827 size_t nFrames = mOutput.format.u.raw_audio.buffer_size / 828 (sizeof(float) * mOutput.format.u.raw_audio.channel_count); 829 mFramesSent += nFrames; 830 831 // The buffer is on its way; now schedule the next one to go 832 bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent) 833 / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0); 834 media_timed_event nextBufferEvent(nextEvent, 835 BTimedEventQueue::B_HANDLE_BUFFER); 836 EventQueue()->AddEvent(nextBufferEvent); 837 } 838 } 839 break; 840 841 default: 842 break; 843 } 844 } 845 846 /* 847 void 848 ToneProducer::CleanUpEvent(const media_timed_event *event) 849 { 850 // A bug in the current PowerPC compiler demands that we implement 851 // this, even though it just calls up to the inherited implementation. 852 BMediaEventLooper::CleanUpEvent(event); 853 } 854 855 bigtime_t 856 ToneProducer::OfflineTime() 857 { 858 // A bug in the current PowerPC compiler demands that we implement 859 // this, even though it just calls up to the inherited implementation. 860 return BMediaEventLooper::OfflineTime(); 861 } 862 863 void 864 ToneProducer::ControlLoop() 865 { 866 // A bug in the current PowerPC compiler demands that we implement 867 // this, even though it just calls up to the inherited implementation. 868 BMediaEventLooper::ControlLoop(); 869 } 870 871 //#pragma mark - 872 */ 873 874 void 875 ToneProducer::AllocateBuffers() 876 { 877 FPRINTF(stderr, "ToneProducer::AllocateBuffers\n"); 878 879 // allocate enough buffers to span our downstream latency, plus one 880 size_t size = mOutput.format.u.raw_audio.buffer_size; 881 int32 count = int32(mLatency / BufferDuration() + 1 + 1); 882 883 FPRINTF(stderr, "\tlatency = %" B_PRIdBIGTIME ", buffer duration = %" 884 B_PRIdBIGTIME "\n", mLatency, BufferDuration()); 885 FPRINTF(stderr, "\tcreating group of %" B_PRId32 " buffers, size = %" 886 B_PRIuSIZE "\n", count, size); 887 mBufferGroup = new BBufferGroup(size, count); 888 } 889 890 BBuffer* 891 ToneProducer::FillNextBuffer(bigtime_t event_time) 892 { 893 // get a buffer from our buffer group 894 BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration()); 895 896 // if we fail to get a buffer (for example, if the request times out), we skip this 897 // buffer and go on to the next, to avoid locking up the control thread 898 if (!buf) 899 { 900 return NULL; 901 } 902 903 // now fill it with data, continuing where the last buffer left off 904 // 20sep99: multichannel support 905 906 size_t numFrames = 907 mOutput.format.u.raw_audio.buffer_size / 908 (sizeof(float)*mOutput.format.u.raw_audio.channel_count); 909 bool stereo = (mOutput.format.u.raw_audio.channel_count == 2); 910 if(!stereo) { 911 ASSERT(mOutput.format.u.raw_audio.channel_count == 1); 912 } 913 // PRINT(("buffer: %ld, %ld frames, %s\n", mOutput.format.u.raw_audio.buffer_size, numFrames, stereo ? "stereo" : "mono")); 914 915 float* data = (float*) buf->Data(); 916 917 switch (mWaveform) 918 { 919 case SINE_WAVE: 920 FillSineBuffer(data, numFrames, stereo); 921 break; 922 923 case TRIANGLE_WAVE: 924 FillTriangleBuffer(data, numFrames, stereo); 925 break; 926 927 case SAWTOOTH_WAVE: 928 FillSawtoothBuffer(data, numFrames, stereo); 929 break; 930 } 931 932 // fill in the buffer header 933 media_header* hdr = buf->Header(); 934 hdr->type = B_MEDIA_RAW_AUDIO; 935 hdr->size_used = mOutput.format.u.raw_audio.buffer_size; 936 hdr->time_source = TimeSource()->ID(); 937 938 bigtime_t stamp; 939 if (RunMode() == B_RECORDING) 940 { 941 // In B_RECORDING mode, we stamp with the capture time. We're not 942 // really a hardware capture node, but we simulate it by using the (precalculated) 943 // time at which this buffer "should" have been created. 944 stamp = event_time; 945 } 946 else 947 { 948 // okay, we're in one of the "live" performance run modes. in these modes, we 949 // stamp the buffer with the time at which the buffer should be rendered to the 950 // output, not with the capture time. mStartTime is the cached value of the 951 // first buffer's performance time; we calculate this buffer's performance time as 952 // an offset from that time, based on the amount of media we've created so far. 953 // Recalculating every buffer like this avoids accumulation of error. 954 stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0); 955 } 956 hdr->start_time = stamp; 957 958 return buf; 959 } 960 961 // waveform generators - fill buffers with various waveforms 962 void 963 ToneProducer::FillSineBuffer(float *data, size_t numFrames, bool stereo) 964 { 965 966 967 // cover 2pi radians in one period 968 double dTheta = 2*M_PI * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate; 969 970 // Fill the buffer! 971 for (size_t i = 0; i < numFrames; i++, data++) 972 { 973 float val = mGain * float(sin(mTheta)); 974 *data = val; 975 if(stereo) { 976 ++data; 977 *data = val; 978 } 979 980 mTheta += dTheta; 981 if (mTheta > 2*M_PI) 982 { 983 mTheta -= 2*M_PI; 984 } 985 } 986 } 987 988 void 989 ToneProducer::FillTriangleBuffer(float *data, size_t numFrames, bool stereo) 990 { 991 // ramp from -1 to 1 and back in one period 992 double dTheta = 4.0 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate; 993 if (!mWaveAscending) dTheta = -dTheta; 994 995 // fill the buffer! 996 for (size_t i = 0; i < numFrames; i++, data++) 997 { 998 float val = mGain * mTheta; 999 *data = val; 1000 if(stereo) { 1001 ++data; 1002 *data = val; 1003 } 1004 1005 mTheta += dTheta; 1006 if (mTheta >= 1) 1007 { 1008 mTheta = 2 - mTheta; // reflect across the mTheta=1 line to preserve drift 1009 mWaveAscending = false; 1010 dTheta = -dTheta; 1011 } 1012 else if (mTheta <= -1) 1013 { 1014 mTheta = -2 - mTheta; // reflect across mTheta=-1 1015 mWaveAscending = true; 1016 dTheta = -dTheta; 1017 } 1018 } 1019 } 1020 1021 void 1022 ToneProducer::FillSawtoothBuffer(float *data, size_t numFrames, bool stereo) 1023 { 1024 // ramp from -1 to 1 in one period 1025 double dTheta = 2 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate; 1026 mWaveAscending = true; 1027 1028 // fill the buffer! 1029 for (size_t i = 0; i < numFrames; i++, data++) 1030 { 1031 float val = mGain * mTheta; 1032 *data = val; 1033 if(stereo) { 1034 ++data; 1035 *data = val; 1036 } 1037 1038 mTheta += dTheta; 1039 if (mTheta > 1) 1040 { 1041 mTheta -= 2; // back to the base of the sawtooth, including cumulative drift 1042 } 1043 } 1044 } 1045 1046 // utility - build the ToneProducer's parameter web 1047 static BParameterWeb* make_parameter_web() 1048 { 1049 FPRINTF(stderr, "make_parameter_web() called\n"); 1050 1051 BParameterWeb* web = new BParameterWeb; 1052 BParameterGroup* mainGroup = web->MakeGroup("Tone Generator Parameters"); 1053 1054 BParameterGroup* group = mainGroup->MakeGroup("Frequency"); 1055 BParameter* nullParam = group->MakeNullParameter(FREQUENCY_NULL_PARAM, B_MEDIA_NO_TYPE, "Frequency", B_GENERIC); 1056 BContinuousParameter* param = group->MakeContinuousParameter(FREQUENCY_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "Hz", 0, 2500, 0.1); 1057 nullParam->AddOutput(param); 1058 param->AddInput(nullParam); 1059 1060 group = mainGroup->MakeGroup("Amplitude"); 1061 nullParam = group->MakeNullParameter(GAIN_NULL_PARAM, B_MEDIA_NO_TYPE, "Amplitude", B_GENERIC); 1062 param = group->MakeContinuousParameter(GAIN_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "", 0, 1, 0.01); 1063 nullParam->AddOutput(param); 1064 param->AddInput(nullParam); 1065 1066 group = mainGroup->MakeGroup("Waveform"); 1067 nullParam = group->MakeNullParameter(WAVEFORM_NULL_PARAM, B_MEDIA_NO_TYPE, "Waveform", B_GENERIC); 1068 BDiscreteParameter* waveParam = group->MakeDiscreteParameter(WAVEFORM_PARAM, B_MEDIA_NO_TYPE, "", B_GENERIC); 1069 waveParam->AddItem(SINE_WAVE, "Sine wave"); 1070 waveParam->AddItem(TRIANGLE_WAVE, "Triangle"); 1071 waveParam->AddItem(SAWTOOTH_WAVE, "Sawtooth"); 1072 nullParam->AddOutput(waveParam); 1073 waveParam->AddInput(nullParam); 1074 1075 return web; 1076 } 1077