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.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN; 72 73 // we'll use the consumer's preferred buffer size and framerate, if any 74 mPreferredFormat.u.raw_audio.frame_rate = media_raw_audio_format::wildcard.frame_rate; 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 = 44100.0f; 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 //#pragma mark - 625 626 // BMediaEventLooper methods 627 void 628 ToneProducer::NodeRegistered() 629 { 630 FPRINTF(stderr, "ToneProducer::NodeRegistered\n"); 631 632 // output init moved to ctor 633 // e.moon [4jun99] 634 635 // Set up our parameter web 636 mWeb = make_parameter_web(); 637 SetParameterWeb(mWeb); 638 639 // Start the BMediaEventLooper thread 640 SetPriority(B_REAL_TIME_PRIORITY); 641 Run(); 642 } 643 644 void 645 ToneProducer::Start(bigtime_t performance_time) 646 { 647 PRINT(("ToneProducer::Start(%" B_PRIdBIGTIME "): now %" B_PRIdBIGTIME "\n", 648 performance_time, TimeSource()->Now())); 649 650 // send 'data available' message 651 if(mOutput.destination != media_destination::null) 652 SendDataStatus(B_DATA_AVAILABLE, mOutput.destination, performance_time); 653 654 // A bug in the current PowerPC compiler demands that we implement 655 // this, even though it just calls up to the inherited implementation. 656 BMediaEventLooper::Start(performance_time); 657 } 658 659 void 660 ToneProducer::Stop(bigtime_t performance_time, bool immediate) 661 { 662 // send 'data not available' message 663 if(mOutput.destination != media_destination::null) { 664 printf("ToneProducer: B_PRODUCER_STOPPED at %" B_PRIdBIGTIME "\n", 665 performance_time); 666 SendDataStatus(B_PRODUCER_STOPPED, mOutput.destination, performance_time); 667 } 668 669 // A bug in the current PowerPC compiler demands that we implement 670 // this, even though it just calls up to the inherited implementation. 671 BMediaEventLooper::Stop(performance_time, immediate); 672 } 673 674 void 675 ToneProducer::SetRunMode(run_mode mode) 676 { 677 FPRINTF(stderr, "ToneProducer::SetRunMode\n"); 678 679 // We don't support offline run mode, so broadcast an error if we're set to 680 // B_OFFLINE. Unfortunately, we can't actually reject the mode change... 681 if (B_OFFLINE == mode) 682 { 683 ReportError(B_NODE_FAILED_SET_RUN_MODE); 684 } 685 } 686 687 void 688 ToneProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) 689 { 690 // FPRINTF(stderr, "ToneProducer::HandleEvent\n"); 691 switch (event->type) 692 { 693 case BTimedEventQueue::B_START: 694 // don't do anything if we're already running 695 if (RunState() != B_STARTED) 696 { 697 // We want to start sending buffers now, so we set up the buffer-sending bookkeeping 698 // and fire off the first "produce a buffer" event. 699 mFramesSent = 0; 700 mTheta = 0; 701 mStartTime = event->event_time; 702 media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER); 703 704 // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through 705 // the event queue, like this: 706 // 707 // this->HandleEvent(&firstBufferEvent, 0, false); 708 // 709 EventQueue()->AddEvent(firstBufferEvent); 710 } 711 break; 712 713 case BTimedEventQueue::B_STOP: 714 FPRINTF(stderr, "Handling B_STOP event\n"); 715 716 // When we handle a stop, we must ensure that downstream consumers don't 717 // get any more buffers from us. This means we have to flush any pending 718 // buffer-producing events from the queue. 719 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 720 break; 721 722 case _PARAMETER_EVENT: 723 { 724 size_t dataSize = size_t(event->data); 725 int32 param = int32(event->bigdata); 726 if (dataSize >= sizeof(float)) switch (param) 727 { 728 case FREQUENCY_PARAM: 729 { 730 float newValue = *((float*) event->user_data); 731 if (mFrequency != newValue) // an actual change in the value? 732 { 733 mFrequency = newValue; 734 mFreqLastChanged = TimeSource()->Now(); 735 BroadcastNewParameterValue(mFreqLastChanged, param, &mFrequency, sizeof(mFrequency)); 736 } 737 } 738 break; 739 740 case GAIN_PARAM: 741 { 742 float newValue = *((float*) event->user_data); 743 if (mGain != newValue) 744 { 745 mGain = newValue; 746 mGainLastChanged = TimeSource()->Now(); 747 BroadcastNewParameterValue(mGainLastChanged, param, &mGain, sizeof(mGain)); 748 } 749 } 750 break; 751 752 case WAVEFORM_PARAM: 753 { 754 int32 newValue = *((int32*) event->user_data); 755 if (mWaveform != newValue) 756 { 757 mWaveform = newValue; 758 mTheta = 0; // reset the generator parameters when we change waveforms 759 mWaveAscending = true; 760 mWaveLastChanged = TimeSource()->Now(); 761 BroadcastNewParameterValue(mWaveLastChanged, param, &mWaveform, sizeof(mWaveform)); 762 } 763 } 764 break; 765 766 default: 767 FPRINTF(stderr, "Hmmm... got a B_PARAMETER event for a parameter we don't have? (%" B_PRId32 ")\n", param); 768 break; 769 } 770 } 771 break; 772 773 case BTimedEventQueue::B_HANDLE_BUFFER: 774 { 775 // make sure we're both started *and* connected before delivering a buffer 776 if (RunState() == BMediaEventLooper::B_STARTED 777 && mOutput.destination != media_destination::null) { 778 // Get the next buffer of data 779 BBuffer* buffer = FillNextBuffer(event->event_time); 780 if (buffer) { 781 // send the buffer downstream if and only if output is enabled 782 status_t err = B_ERROR; 783 if (mOutputEnabled) { 784 err = SendBuffer(buffer, mOutput.source, 785 mOutput.destination); 786 } 787 if (err) { 788 // we need to recycle the buffer ourselves if output is disabled or 789 // if the call to SendBuffer() fails 790 buffer->Recycle(); 791 } 792 } 793 794 // track how much media we've delivered so far 795 size_t nFrames = mOutput.format.u.raw_audio.buffer_size / 796 (sizeof(float) * mOutput.format.u.raw_audio.channel_count); 797 mFramesSent += nFrames; 798 799 // The buffer is on its way; now schedule the next one to go 800 bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent) 801 / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0); 802 media_timed_event nextBufferEvent(nextEvent, 803 BTimedEventQueue::B_HANDLE_BUFFER); 804 EventQueue()->AddEvent(nextBufferEvent); 805 } 806 } 807 break; 808 809 default: 810 break; 811 } 812 } 813 814 //#pragma mark - 815 816 void 817 ToneProducer::AllocateBuffers() 818 { 819 FPRINTF(stderr, "ToneProducer::AllocateBuffers\n"); 820 821 // allocate enough buffers to span our downstream latency, plus one 822 size_t size = mOutput.format.u.raw_audio.buffer_size; 823 int32 count = int32(mLatency / BufferDuration() + 1 + 1); 824 825 FPRINTF(stderr, "\tlatency = %" B_PRIdBIGTIME ", buffer duration = %" 826 B_PRIdBIGTIME "\n", mLatency, BufferDuration()); 827 FPRINTF(stderr, "\tcreating group of %" B_PRId32 " buffers, size = %" 828 B_PRIuSIZE "\n", count, size); 829 mBufferGroup = new BBufferGroup(size, count); 830 } 831 832 BBuffer* 833 ToneProducer::FillNextBuffer(bigtime_t event_time) 834 { 835 // get a buffer from our buffer group 836 BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration()); 837 838 // if we fail to get a buffer (for example, if the request times out), we skip this 839 // buffer and go on to the next, to avoid locking up the control thread 840 if (!buf) 841 { 842 return NULL; 843 } 844 845 // now fill it with data, continuing where the last buffer left off 846 // 20sep99: multichannel support 847 848 size_t numFrames = 849 mOutput.format.u.raw_audio.buffer_size / 850 (sizeof(float)*mOutput.format.u.raw_audio.channel_count); 851 bool stereo = (mOutput.format.u.raw_audio.channel_count == 2); 852 if(!stereo) { 853 ASSERT(mOutput.format.u.raw_audio.channel_count == 1); 854 } 855 // PRINT(("buffer: %ld, %ld frames, %s\n", mOutput.format.u.raw_audio.buffer_size, numFrames, stereo ? "stereo" : "mono")); 856 857 float* data = (float*) buf->Data(); 858 859 switch (mWaveform) 860 { 861 case SINE_WAVE: 862 FillSineBuffer(data, numFrames, stereo); 863 break; 864 865 case TRIANGLE_WAVE: 866 FillTriangleBuffer(data, numFrames, stereo); 867 break; 868 869 case SAWTOOTH_WAVE: 870 FillSawtoothBuffer(data, numFrames, stereo); 871 break; 872 } 873 874 // fill in the buffer header 875 media_header* hdr = buf->Header(); 876 hdr->type = B_MEDIA_RAW_AUDIO; 877 hdr->size_used = mOutput.format.u.raw_audio.buffer_size; 878 hdr->time_source = TimeSource()->ID(); 879 880 bigtime_t stamp; 881 if (RunMode() == B_RECORDING) 882 { 883 // In B_RECORDING mode, we stamp with the capture time. We're not 884 // really a hardware capture node, but we simulate it by using the (precalculated) 885 // time at which this buffer "should" have been created. 886 stamp = event_time; 887 } 888 else 889 { 890 // okay, we're in one of the "live" performance run modes. in these modes, we 891 // stamp the buffer with the time at which the buffer should be rendered to the 892 // output, not with the capture time. mStartTime is the cached value of the 893 // first buffer's performance time; we calculate this buffer's performance time as 894 // an offset from that time, based on the amount of media we've created so far. 895 // Recalculating every buffer like this avoids accumulation of error. 896 stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0); 897 } 898 hdr->start_time = stamp; 899 900 return buf; 901 } 902 903 // waveform generators - fill buffers with various waveforms 904 void 905 ToneProducer::FillSineBuffer(float *data, size_t numFrames, bool stereo) 906 { 907 908 909 // cover 2pi radians in one period 910 double dTheta = 2*M_PI * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate; 911 912 // Fill the buffer! 913 for (size_t i = 0; i < numFrames; i++, data++) 914 { 915 float val = mGain * float(sin(mTheta)); 916 *data = val; 917 if(stereo) { 918 ++data; 919 *data = val; 920 } 921 922 mTheta += dTheta; 923 if (mTheta > 2*M_PI) 924 { 925 mTheta -= 2*M_PI; 926 } 927 } 928 } 929 930 void 931 ToneProducer::FillTriangleBuffer(float *data, size_t numFrames, bool stereo) 932 { 933 // ramp from -1 to 1 and back in one period 934 double dTheta = 4.0 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate; 935 if (!mWaveAscending) dTheta = -dTheta; 936 937 // fill the buffer! 938 for (size_t i = 0; i < numFrames; i++, data++) 939 { 940 float val = mGain * mTheta; 941 *data = val; 942 if(stereo) { 943 ++data; 944 *data = val; 945 } 946 947 mTheta += dTheta; 948 if (mTheta >= 1) 949 { 950 mTheta = 2 - mTheta; // reflect across the mTheta=1 line to preserve drift 951 mWaveAscending = false; 952 dTheta = -dTheta; 953 } 954 else if (mTheta <= -1) 955 { 956 mTheta = -2 - mTheta; // reflect across mTheta=-1 957 mWaveAscending = true; 958 dTheta = -dTheta; 959 } 960 } 961 } 962 963 void 964 ToneProducer::FillSawtoothBuffer(float *data, size_t numFrames, bool stereo) 965 { 966 // ramp from -1 to 1 in one period 967 double dTheta = 2 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate; 968 mWaveAscending = true; 969 970 // fill the buffer! 971 for (size_t i = 0; i < numFrames; i++, data++) 972 { 973 float val = mGain * mTheta; 974 *data = val; 975 if(stereo) { 976 ++data; 977 *data = val; 978 } 979 980 mTheta += dTheta; 981 if (mTheta > 1) 982 { 983 mTheta -= 2; // back to the base of the sawtooth, including cumulative drift 984 } 985 } 986 } 987 988 // utility - build the ToneProducer's parameter web 989 static BParameterWeb* make_parameter_web() 990 { 991 FPRINTF(stderr, "make_parameter_web() called\n"); 992 993 BParameterWeb* web = new BParameterWeb; 994 BParameterGroup* mainGroup = web->MakeGroup("Tone Generator Parameters"); 995 996 BParameterGroup* group = mainGroup->MakeGroup("Frequency"); 997 BParameter* nullParam = group->MakeNullParameter(FREQUENCY_NULL_PARAM, B_MEDIA_NO_TYPE, "Frequency", B_GENERIC); 998 BContinuousParameter* param = group->MakeContinuousParameter(FREQUENCY_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "Hz", 0, 2500, 0.1); 999 nullParam->AddOutput(param); 1000 param->AddInput(nullParam); 1001 1002 group = mainGroup->MakeGroup("Amplitude"); 1003 nullParam = group->MakeNullParameter(GAIN_NULL_PARAM, B_MEDIA_NO_TYPE, "Amplitude", B_GENERIC); 1004 param = group->MakeContinuousParameter(GAIN_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "", 0, 1, 0.01); 1005 nullParam->AddOutput(param); 1006 param->AddInput(nullParam); 1007 1008 group = mainGroup->MakeGroup("Waveform"); 1009 nullParam = group->MakeNullParameter(WAVEFORM_NULL_PARAM, B_MEDIA_NO_TYPE, "Waveform", B_GENERIC); 1010 BDiscreteParameter* waveParam = group->MakeDiscreteParameter(WAVEFORM_PARAM, B_MEDIA_NO_TYPE, "", B_GENERIC); 1011 waveParam->AddItem(SINE_WAVE, "Sine wave"); 1012 waveParam->AddItem(TRIANGLE_WAVE, "Triangle"); 1013 waveParam->AddItem(SAWTOOTH_WAVE, "Sawtooth"); 1014 nullParam->AddOutput(waveParam); 1015 waveParam->AddInput(nullParam); 1016 1017 return web; 1018 } 1019