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