1 /* 2 * Copyright (c) 1999-2000, Eric Moon. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions, and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions, and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 32 // FlangerNode.cpp 33 // e.moon 16jun99 34 35 #include "FlangerNode.h" 36 37 #include "AudioBuffer.h" 38 #include "SoundUtils.h" 39 40 #include <Buffer.h> 41 #include <BufferGroup.h> 42 #include <ByteOrder.h> 43 #include <Catalog.h> 44 #include <Debug.h> 45 #include <ParameterWeb.h> 46 #include <TimeSource.h> 47 48 #include <cstdio> 49 #include <cstdlib> 50 #include <cstring> 51 #include <cmath> 52 53 #undef B_TRANSLATION_CONTEXT 54 #define B_TRANSLATION_CONTEXT "CortexAddOnsFlanger" 55 56 // -------------------------------------------------------- // 57 // local helpers 58 // -------------------------------------------------------- // 59 60 float calc_sweep_delta( 61 const media_raw_audio_format& format, 62 float fRate); 63 64 float calc_sweep_base( 65 const media_raw_audio_format& format, 66 float fDelay, float fDepth); 67 68 float calc_sweep_factor( 69 const media_raw_audio_format& format, 70 float fDepth); 71 72 // -------------------------------------------------------- // 73 // constants 74 // -------------------------------------------------------- // 75 76 // input-ID symbols 77 enum input_id_t { 78 ID_AUDIO_INPUT 79 }; 80 81 // output-ID symbols 82 enum output_id_t { 83 ID_AUDIO_MIX_OUTPUT 84 //ID_AUDIO_WET_OUTPUT ... 85 }; 86 87 // parameter ID 88 enum param_id_t { 89 P_MIX_RATIO_LABEL = 100, 90 P_MIX_RATIO, 91 92 P_SWEEP_RATE_LABEL = 200, 93 P_SWEEP_RATE, 94 95 P_DELAY_LABEL = 300, 96 P_DELAY, 97 98 P_DEPTH_LABEL = 400, 99 P_DEPTH, 100 101 P_FEEDBACK_LABEL = 500, 102 P_FEEDBACK 103 }; 104 105 const float FlangerNode::s_fMaxDelay = 100.0; 106 const char* const FlangerNode::s_nodeName = "FlangerNode"; 107 108 109 // -------------------------------------------------------- // 110 // ctor/dtor 111 // -------------------------------------------------------- // 112 113 FlangerNode::~FlangerNode() { 114 // shut down 115 Quit(); 116 117 // free delay buffer 118 if(m_pDelayBuffer) 119 delete m_pDelayBuffer; 120 } 121 122 FlangerNode::FlangerNode(BMediaAddOn* pAddOn) : 123 // * init base classes 124 BMediaNode(s_nodeName), // (virtual base) 125 BBufferConsumer(B_MEDIA_RAW_AUDIO), 126 BBufferProducer(B_MEDIA_RAW_AUDIO), 127 BControllable(), 128 BMediaEventLooper(), 129 130 // * init connection state 131 m_outputEnabled(true), 132 m_downstreamLatency(0), 133 m_processingLatency(0), 134 135 // * init filter state 136 m_pDelayBuffer(0), 137 138 // * init add-on stuff 139 m_pAddOn(pAddOn) { 140 141 // PRINT(( 142 // "\n" 143 // "--*-- FlangerNode() [%s] --*--\n\n", 144 // __BUILD_DATE)); 145 // 146 // the rest of the initialization happens in NodeRegistered(). 147 } 148 149 150 // -------------------------------------------------------- // 151 // *** BMediaNode 152 // -------------------------------------------------------- // 153 154 status_t FlangerNode::HandleMessage( 155 int32 code, 156 const void* pData, 157 size_t size) { 158 159 // pass off to each base class 160 if( 161 BBufferConsumer::HandleMessage(code, pData, size) && 162 BBufferProducer::HandleMessage(code, pData, size) && 163 BControllable::HandleMessage(code, pData, size) && 164 BMediaNode::HandleMessage(code, pData, size)) 165 BMediaNode::HandleBadMessage(code, pData, size); 166 167 // +++++ return error on bad message? 168 return B_OK; 169 } 170 171 BMediaAddOn* FlangerNode::AddOn( 172 int32* poID) const { 173 174 if(m_pAddOn) 175 *poID = 0; 176 return m_pAddOn; 177 } 178 179 void FlangerNode::SetRunMode( 180 run_mode mode) { 181 182 // disallow offline mode for now 183 // +++++ 184 if(mode == B_OFFLINE) 185 ReportError(B_NODE_FAILED_SET_RUN_MODE); 186 187 // +++++ any other work to do? 188 189 // hand off 190 BMediaEventLooper::SetRunMode(mode); 191 } 192 193 // -------------------------------------------------------- // 194 // *** BMediaEventLooper 195 // -------------------------------------------------------- // 196 197 void FlangerNode::HandleEvent( 198 const media_timed_event* pEvent, 199 bigtime_t howLate, 200 bool realTimeEvent) { 201 202 ASSERT(pEvent); 203 204 switch(pEvent->type) { 205 case BTimedEventQueue::B_PARAMETER: 206 handleParameterEvent(pEvent); 207 break; 208 209 case BTimedEventQueue::B_START: 210 handleStartEvent(pEvent); 211 break; 212 213 case BTimedEventQueue::B_STOP: 214 handleStopEvent(pEvent); 215 break; 216 217 default: 218 ignoreEvent(pEvent); 219 break; 220 } 221 } 222 223 // "The Media Server calls this hook function after the node has 224 // been registered. This is derived from BMediaNode; BMediaEventLooper 225 // implements it to call Run() automatically when the node is registered; 226 // if you implement NodeRegistered() you should call through to 227 // BMediaEventLooper::NodeRegistered() after you've done your custom 228 // operations." 229 230 void FlangerNode::NodeRegistered() { 231 232 PRINT(("FlangerNode::NodeRegistered()\n")); 233 234 // figure preferred ('template') format 235 m_preferredFormat.type = B_MEDIA_RAW_AUDIO; 236 getPreferredFormat(m_preferredFormat); 237 238 // initialize current format 239 m_format.type = B_MEDIA_RAW_AUDIO; 240 m_format.u.raw_audio = media_raw_audio_format::wildcard; 241 242 // init input 243 m_input.destination.port = ControlPort(); 244 m_input.destination.id = ID_AUDIO_INPUT; 245 m_input.node = Node(); 246 m_input.source = media_source::null; 247 m_input.format = m_format; 248 strlcpy(m_input.name, B_TRANSLATE("Audio input"), B_MEDIA_NAME_LENGTH); 249 250 // init output 251 m_output.source.port = ControlPort(); 252 m_output.source.id = ID_AUDIO_MIX_OUTPUT; 253 m_output.node = Node(); 254 m_output.destination = media_destination::null; 255 m_output.format = m_format; 256 strlcpy(m_output.name, B_TRANSLATE("Mix output"), B_MEDIA_NAME_LENGTH); 257 258 // init parameters 259 initParameterValues(); 260 initParameterWeb(); 261 262 // Start the BMediaEventLooper thread 263 SetPriority(B_REAL_TIME_PRIORITY); 264 Run(); 265 } 266 267 // "Augment OfflineTime() to compute the node's current time; it's called 268 // by the Media Kit when it's in offline mode. Update any appropriate 269 // internal information as well, then call through to the BMediaEventLooper 270 // implementation." 271 272 bigtime_t FlangerNode::OfflineTime() { 273 // +++++ 274 return 0LL; 275 } 276 277 // -------------------------------------------------------- // 278 // *** BBufferConsumer 279 // -------------------------------------------------------- // 280 281 status_t FlangerNode::AcceptFormat( 282 const media_destination& destination, 283 media_format* pioFormat) { 284 285 PRINT(("FlangerNode::AcceptFormat()\n")); 286 287 // sanity checks 288 if(destination != m_input.destination) { 289 PRINT(("\tbad destination\n")); 290 return B_MEDIA_BAD_DESTINATION; 291 } 292 if(pioFormat->type != B_MEDIA_RAW_AUDIO) { 293 PRINT(("\tnot B_MEDIA_RAW_AUDIO\n")); 294 return B_MEDIA_BAD_FORMAT; 295 } 296 297 validateProposedFormat( 298 (m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format) ? 299 m_format : m_preferredFormat, 300 *pioFormat); 301 return B_OK; 302 } 303 304 // "If you're writing a node, and receive a buffer with the B_SMALL_BUFFER 305 // flag set, you must recycle the buffer before returning." 306 307 void FlangerNode::BufferReceived( 308 BBuffer* pBuffer) { 309 ASSERT(pBuffer); 310 311 // check buffer destination 312 if(pBuffer->Header()->destination != 313 m_input.destination.id) { 314 PRINT(("FlangerNode::BufferReceived():\n" 315 "\tBad destination.\n")); 316 pBuffer->Recycle(); 317 return; 318 } 319 320 if(pBuffer->Header()->time_source != TimeSource()->ID()) { 321 PRINT(("* timesource mismatch\n")); 322 } 323 324 // check output 325 if(m_output.destination == media_destination::null || 326 !m_outputEnabled) { 327 pBuffer->Recycle(); 328 return; 329 } 330 331 // process and retransmit buffer 332 filterBuffer(pBuffer); 333 334 status_t err = SendBuffer(pBuffer, m_output.source, m_output.destination); 335 if (err < B_OK) { 336 PRINT(("FlangerNode::BufferReceived():\n" 337 "\tSendBuffer() failed: %s\n", strerror(err))); 338 pBuffer->Recycle(); 339 } 340 // sent! 341 } 342 343 // * make sure to fill in poInput->format with the contents of 344 // pFormat; as of R4.5 the Media Kit passes poInput->format to 345 // the producer in BBufferProducer::Connect(). 346 347 status_t 348 FlangerNode::Connected(const media_source& source, 349 const media_destination& destination, const media_format& format, 350 media_input* poInput) 351 { 352 PRINT(("FlangerNode::Connected()\n" 353 "\tto source %" B_PRId32 "\n", source.id)); 354 355 // sanity check 356 if(destination != m_input.destination) { 357 PRINT(("\tbad destination\n")); 358 return B_MEDIA_BAD_DESTINATION; 359 } 360 if(m_input.source != media_source::null) { 361 PRINT(("\talready connected\n")); 362 return B_MEDIA_ALREADY_CONNECTED; 363 } 364 365 // initialize input 366 m_input.source = source; 367 m_input.format = format; 368 *poInput = m_input; 369 370 // store format (this now constrains the output format) 371 m_format = format; 372 373 return B_OK; 374 } 375 376 void FlangerNode::Disconnected( 377 const media_source& source, 378 const media_destination& destination) { 379 380 PRINT(("FlangerNode::Disconnected()\n")); 381 382 // sanity checks 383 if(m_input.source != source) { 384 PRINT(("\tsource mismatch: expected ID %" B_PRId32 ", got %" B_PRId32 385 "\n", m_input.source.id, source.id)); 386 return; 387 } 388 if(destination != m_input.destination) { 389 PRINT(("\tdestination mismatch: expected ID %" B_PRId32 ", got %" 390 B_PRId32 "\n", m_input.destination.id, destination.id)); 391 return; 392 } 393 394 // mark disconnected 395 m_input.source = media_source::null; 396 397 // no output? clear format: 398 if(m_output.destination == media_destination::null) { 399 m_format.u.raw_audio = media_raw_audio_format::wildcard; 400 } 401 402 m_input.format = m_format; 403 404 } 405 406 void FlangerNode::DisposeInputCookie( 407 int32 cookie) {} 408 409 // "You should implement this function so your node will know that the data 410 // format is going to change. Note that this may be called in response to 411 // your AcceptFormat() call, if your AcceptFormat() call alters any wildcard 412 // fields in the specified format. 413 // 414 // Because FormatChanged() is called by the producer, you don't need to (and 415 // shouldn't) ask it if the new format is acceptable. 416 // 417 // If the format change isn't possible, return an appropriate error from 418 // FormatChanged(); this error will be passed back to the producer that 419 // initiated the new format negotiation in the first place." 420 421 status_t FlangerNode::FormatChanged( 422 const media_source& source, 423 const media_destination& destination, 424 int32 changeTag, 425 const media_format& newFormat) { 426 427 // flat-out deny format changes 428 return B_MEDIA_BAD_FORMAT; 429 } 430 431 status_t FlangerNode::GetLatencyFor( 432 const media_destination& destination, 433 bigtime_t* poLatency, 434 media_node_id* poTimeSource) { 435 436 PRINT(("FlangerNode::GetLatencyFor()\n")); 437 438 // sanity check 439 if(destination != m_input.destination) { 440 PRINT(("\tbad destination\n")); 441 return B_MEDIA_BAD_DESTINATION; 442 } 443 444 *poLatency = m_downstreamLatency + m_processingLatency; 445 PRINT(("\treturning %" B_PRIdBIGTIME "\n", *poLatency)); 446 *poTimeSource = TimeSource()->ID(); 447 return B_OK; 448 } 449 450 status_t FlangerNode::GetNextInput( 451 int32* pioCookie, 452 media_input* poInput) { 453 454 if(*pioCookie) 455 return B_BAD_INDEX; 456 457 ++*pioCookie; 458 *poInput = m_input; 459 return B_OK; 460 } 461 462 void FlangerNode::ProducerDataStatus( 463 const media_destination& destination, 464 int32 status, 465 bigtime_t tpWhen) { 466 467 PRINT(("FlangerNode::ProducerDataStatus()\n")); 468 469 // sanity check 470 if(destination != m_input.destination) { 471 PRINT(("\tbad destination\n")); 472 } 473 474 if(m_output.destination != media_destination::null) { 475 // pass status downstream 476 status_t err = SendDataStatus( 477 status, 478 m_output.destination, 479 tpWhen); 480 if(err < B_OK) { 481 PRINT(("\tSendDataStatus(): %s\n", strerror(err))); 482 } 483 } 484 } 485 486 // "This function is provided to aid in supporting media formats in which the 487 // outer encapsulation layer doesn't supply timing information. Producers will 488 // tag the buffers they generate with seek tags; these tags can be used to 489 // locate key frames in the media data." 490 491 status_t FlangerNode::SeekTagRequested( 492 const media_destination& destination, 493 bigtime_t targetTime, 494 uint32 flags, 495 media_seek_tag* poSeekTag, 496 bigtime_t* poTaggedTime, 497 uint32* poFlags) { 498 499 PRINT(("FlangerNode::SeekTagRequested()\n" 500 "\tNot implemented.\n")); 501 return B_ERROR; 502 } 503 504 // -------------------------------------------------------- // 505 // *** BBufferProducer 506 // -------------------------------------------------------- // 507 508 // "When a consumer calls BBufferConsumer::RequestAdditionalBuffer(), this 509 // function is called as a result. Its job is to call SendBuffer() to 510 // immediately send the next buffer to the consumer. The previousBufferID, 511 // previousTime, and previousTag arguments identify the last buffer the 512 // consumer received. Your node should respond by sending the next buffer 513 // after the one described. 514 // 515 // The previousTag may be NULL. 516 // Return B_OK if all is well; otherwise return an appropriate error code." 517 518 void FlangerNode::AdditionalBufferRequested( 519 const media_source& source, 520 media_buffer_id previousBufferID, 521 bigtime_t previousTime, 522 const media_seek_tag* pPreviousTag) { 523 524 PRINT(("FlangerNode::AdditionalBufferRequested\n" 525 "\tOffline mode not implemented.")); 526 } 527 528 void FlangerNode::Connect( 529 status_t status, 530 const media_source& source, 531 const media_destination& destination, 532 const media_format& format, 533 char* pioName) { 534 535 PRINT(("FlangerNode::Connect()\n")); 536 status_t err; 537 538 // connection failed? 539 if(status < B_OK) { 540 PRINT(("\tStatus: %s\n", strerror(status))); 541 // 'unreserve' the output 542 m_output.destination = media_destination::null; 543 return; 544 } 545 546 // connection established: 547 strncpy(pioName, m_output.name, B_MEDIA_NAME_LENGTH); 548 m_output.destination = destination; 549 m_format = format; 550 551 // figure downstream latency 552 media_node_id timeSource; 553 err = FindLatencyFor(m_output.destination, &m_downstreamLatency, &timeSource); 554 if(err < B_OK) { 555 PRINT(("\t!!! FindLatencyFor(): %s\n", strerror(err))); 556 } 557 PRINT(("\tdownstream latency = %" B_PRIdBIGTIME "\n", m_downstreamLatency)); 558 559 // prepare the filter 560 initFilter(); 561 562 // figure processing time 563 m_processingLatency = calcProcessingLatency(); 564 PRINT(("\tprocessing latency = %" B_PRIdBIGTIME "\n", m_processingLatency)); 565 566 // store summed latency 567 SetEventLatency(m_downstreamLatency + m_processingLatency); 568 569 if(m_input.source != media_source::null) { 570 // pass new latency upstream 571 err = SendLatencyChange( 572 m_input.source, 573 m_input.destination, 574 EventLatency() + SchedulingLatency()); 575 if(err < B_OK) 576 PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err))); 577 } 578 579 // cache buffer duration 580 SetBufferDuration( 581 buffer_duration( 582 m_format.u.raw_audio)); 583 } 584 585 void FlangerNode::Disconnect( 586 const media_source& source, 587 const media_destination& destination) { 588 589 PRINT(("FlangerNode::Disconnect()\n")); 590 591 // sanity checks 592 if(source != m_output.source) { 593 PRINT(("\tbad source\n")); 594 return; 595 } 596 if(destination != m_output.destination) { 597 PRINT(("\tbad destination\n")); 598 return; 599 } 600 601 // clean up 602 m_output.destination = media_destination::null; 603 604 // no input? clear format: 605 if(m_input.source == media_source::null) { 606 m_format.u.raw_audio = media_raw_audio_format::wildcard; 607 } 608 609 m_output.format = m_format; 610 611 // +++++ other cleanup goes here 612 } 613 614 status_t FlangerNode::DisposeOutputCookie( 615 int32 cookie) { 616 return B_OK; 617 } 618 619 void FlangerNode::EnableOutput( 620 const media_source& source, 621 bool enabled, 622 int32* _deprecated_) { 623 PRINT(("FlangerNode::EnableOutput()\n")); 624 if(source != m_output.source) { 625 PRINT(("\tbad source\n")); 626 return; 627 } 628 629 m_outputEnabled = enabled; 630 } 631 632 status_t FlangerNode::FormatChangeRequested( 633 const media_source& source, 634 const media_destination& destination, 635 media_format* pioFormat, 636 int32* _deprecated_) { 637 638 // deny 639 PRINT(("FlangerNode::FormatChangeRequested()\n" 640 "\tNot supported.\n")); 641 642 return B_MEDIA_BAD_FORMAT; 643 } 644 645 status_t FlangerNode::FormatProposal( 646 const media_source& source, 647 media_format* pioFormat) { 648 649 PRINT(("FlangerNode::FormatProposal()\n")); 650 651 if(source != m_output.source) { 652 PRINT(("\tbad source\n")); 653 return B_MEDIA_BAD_SOURCE; 654 } 655 656 if(pioFormat->type != B_MEDIA_RAW_AUDIO) { 657 PRINT(("\tbad type\n")); 658 return B_MEDIA_BAD_FORMAT; 659 } 660 661 validateProposedFormat( 662 (m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format) ? 663 m_format : 664 m_preferredFormat, 665 *pioFormat); 666 return B_OK; 667 } 668 669 status_t FlangerNode::FormatSuggestionRequested( 670 media_type type, 671 int32 quality, 672 media_format* poFormat) { 673 674 PRINT(("FlangerNode::FormatSuggestionRequested()\n")); 675 if(type != B_MEDIA_RAW_AUDIO) { 676 PRINT(("\tbad type\n")); 677 return B_MEDIA_BAD_FORMAT; 678 } 679 680 if(m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format) 681 *poFormat = m_format; 682 else 683 *poFormat = m_preferredFormat; 684 return B_OK; 685 } 686 687 status_t FlangerNode::GetLatency( 688 bigtime_t* poLatency) { 689 690 PRINT(("FlangerNode::GetLatency()\n")); 691 *poLatency = EventLatency() + SchedulingLatency(); 692 PRINT(("\treturning %" B_PRIdBIGTIME "\n", *poLatency)); 693 694 return B_OK; 695 } 696 697 status_t FlangerNode::GetNextOutput( 698 int32* pioCookie, 699 media_output* poOutput) { 700 701 if(*pioCookie) 702 return B_BAD_INDEX; 703 704 ++*pioCookie; 705 *poOutput = m_output; 706 707 return B_OK; 708 } 709 710 // "This hook function is called when a BBufferConsumer that's receiving data 711 // from you determines that its latency has changed. It will call its 712 // BBufferConsumer::SendLatencyChange() function, and in response, the Media 713 // Server will call your LatencyChanged() function. The source argument 714 // indicates your output that's involved in the connection, and destination 715 // specifies the input on the consumer to which the connection is linked. 716 // newLatency is the consumer's new latency. The flags are currently unused." 717 void FlangerNode::LatencyChanged( 718 const media_source& source, 719 const media_destination& destination, 720 bigtime_t newLatency, 721 uint32 flags) { 722 723 PRINT(("FlangerNode::LatencyChanged()\n")); 724 725 if(source != m_output.source) { 726 PRINT(("\tBad source.\n")); 727 return; 728 } 729 if(destination != m_output.destination) { 730 PRINT(("\tBad destination.\n")); 731 return; 732 } 733 734 m_downstreamLatency = newLatency; 735 SetEventLatency(m_downstreamLatency + m_processingLatency); 736 737 if(m_input.source != media_source::null) { 738 // pass new latency upstream 739 status_t err = SendLatencyChange( 740 m_input.source, 741 m_input.destination, 742 EventLatency() + SchedulingLatency()); 743 if(err < B_OK) 744 PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err))); 745 } 746 } 747 748 void FlangerNode::LateNoticeReceived( 749 const media_source& source, 750 bigtime_t howLate, 751 bigtime_t tpWhen) { 752 753 PRINT(("FlangerNode::LateNoticeReceived()\n" 754 "\thowLate == %" B_PRIdBIGTIME "\n" 755 "\twhen == %" B_PRIdBIGTIME "\n", howLate, tpWhen)); 756 757 if(source != m_output.source) { 758 PRINT(("\tBad source.\n")); 759 return; 760 } 761 762 if(m_input.source == media_source::null) { 763 PRINT(("\t!!! No input to blame.\n")); 764 return; 765 } 766 767 // +++++ check run mode? 768 769 // pass the buck, since this node doesn't schedule buffer 770 // production 771 NotifyLateProducer( 772 m_input.source, 773 howLate, 774 tpWhen); 775 } 776 777 // PrepareToConnect() is the second stage of format negotiations that happens 778 // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat() 779 // method has been called, and that node has potentially changed the proposed 780 // format. It may also have left wildcards in the format. PrepareToConnect() 781 // *must* fully specialize the format before returning! 782 783 status_t FlangerNode::PrepareToConnect( 784 const media_source& source, 785 const media_destination& destination, 786 media_format* pioFormat, 787 media_source* poSource, 788 char* poName) { 789 790 char formatStr[256]; 791 string_for_format(*pioFormat, formatStr, 255); 792 PRINT(("FlangerNode::PrepareToConnect()\n" 793 "\tproposed format: %s\n", formatStr)); 794 795 if(source != m_output.source) { 796 PRINT(("\tBad source.\n")); 797 return B_MEDIA_BAD_SOURCE; 798 } 799 if(m_output.destination != media_destination::null) { 800 PRINT(("\tAlready connected.\n")); 801 return B_MEDIA_ALREADY_CONNECTED; 802 } 803 804 if(pioFormat->type != B_MEDIA_RAW_AUDIO) { 805 PRINT(("\tBad format type.\n")); 806 return B_MEDIA_BAD_FORMAT; 807 } 808 809 // do a final validity check: 810 status_t err = validateProposedFormat( 811 (m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format) ? 812 m_format : 813 m_preferredFormat, 814 *pioFormat); 815 816 if(err < B_OK) { 817 // no go 818 return err; 819 } 820 821 // fill in wildcards 822 specializeOutputFormat(*pioFormat); 823 824 // reserve the output 825 m_output.destination = destination; 826 m_output.format = *pioFormat; 827 828 // pass back source & output name 829 *poSource = m_output.source; 830 strncpy(poName, m_output.name, B_MEDIA_NAME_LENGTH); 831 832 return B_OK; 833 } 834 835 status_t FlangerNode::SetBufferGroup( 836 const media_source& source, 837 BBufferGroup* pGroup) { 838 839 PRINT(("FlangerNode::SetBufferGroup()\n")); 840 if(source != m_output.source) { 841 PRINT(("\tBad source.\n")); 842 return B_MEDIA_BAD_SOURCE; 843 } 844 845 if(m_input.source == media_source::null) { 846 PRINT(("\tNo producer to send buffers to.\n")); 847 return B_ERROR; 848 } 849 850 // +++++ is this right? buffer-group selection gets 851 // all asynchronous and weird... 852 int32 changeTag; 853 return SetOutputBuffersFor( 854 m_input.source, 855 m_input.destination, 856 pGroup, 857 0, &changeTag); 858 } 859 860 status_t FlangerNode::SetPlayRate( 861 int32 numerator, 862 int32 denominator) { 863 // not supported 864 return B_ERROR; 865 } 866 867 status_t FlangerNode::VideoClippingChanged( 868 const media_source& source, 869 int16 numShorts, 870 int16* pClipData, 871 const media_video_display_info& display, 872 int32* poFromChangeTag) { 873 // not sane 874 return B_ERROR; 875 } 876 877 // -------------------------------------------------------- // 878 // *** BControllable 879 // -------------------------------------------------------- // 880 881 status_t FlangerNode::GetParameterValue( 882 int32 id, 883 bigtime_t* poLastChangeTime, 884 void* poValue, 885 size_t* pioSize) { 886 887 // PRINT(("FlangerNode::GetParameterValue()\n")); 888 889 // all parameters are floats 890 if(*pioSize < sizeof(float)) { 891 return B_NO_MEMORY; 892 } 893 894 *pioSize = sizeof(float); 895 switch(id) { 896 case P_MIX_RATIO: 897 *(float*)poValue = m_fMixRatio; 898 *poLastChangeTime = m_tpMixRatioChanged; 899 break; 900 901 case P_SWEEP_RATE: 902 *(float*)poValue = m_fSweepRate; 903 *poLastChangeTime = m_tpSweepRateChanged; 904 break; 905 906 case P_DELAY: 907 *(float*)poValue = m_fDelay; 908 *poLastChangeTime = m_tpDelayChanged; 909 break; 910 911 case P_DEPTH: 912 *(float*)poValue = m_fDepth; 913 *poLastChangeTime = m_tpDepthChanged; 914 break; 915 916 case P_FEEDBACK: 917 *(float*)poValue = m_fFeedback; 918 *poLastChangeTime = m_tpFeedbackChanged; 919 break; 920 921 default: 922 return B_ERROR; 923 } 924 925 return B_OK; 926 } 927 928 void FlangerNode::SetParameterValue( 929 int32 id, 930 bigtime_t changeTime, 931 const void* pValue, 932 size_t size) { 933 934 switch(id) { 935 case P_MIX_RATIO: 936 case P_SWEEP_RATE: 937 case P_DELAY: 938 case P_DEPTH: 939 case P_FEEDBACK: { 940 if(size < sizeof(float)) 941 break; 942 943 // this is from ToneProducer. it's fishy. 944 // if(size > sizeof(float)) 945 // size = sizeof(float); 946 947 media_timed_event ev( 948 changeTime, 949 BTimedEventQueue::B_PARAMETER, 950 0, 951 BTimedEventQueue::B_NO_CLEANUP, 952 size, 953 id, 954 (char*)pValue, size); 955 EventQueue()->AddEvent(ev); 956 break; 957 } 958 } 959 } 960 961 // -------------------------------------------------------- // 962 // *** HandleEvent() impl 963 // -------------------------------------------------------- // 964 965 void FlangerNode::handleParameterEvent( 966 const media_timed_event* pEvent) { 967 968 float value = *(float*)pEvent->user_data; 969 int32 id = pEvent->bigdata; 970 size_t size = pEvent->data; 971 bigtime_t now = TimeSource()->Now(); 972 973 switch(id) { 974 case P_MIX_RATIO: 975 if(value == m_fMixRatio) 976 break; 977 978 // set 979 m_fMixRatio = value; 980 m_tpMixRatioChanged = now; 981 // broadcast 982 BroadcastNewParameterValue( 983 now, 984 id, 985 &m_fMixRatio, 986 size); 987 break; 988 989 case P_SWEEP_RATE: 990 if(value == m_fSweepRate) 991 break; 992 993 // set 994 m_fSweepRate = value; 995 m_tpSweepRateChanged = now; 996 997 if(m_output.destination != media_destination::null) { 998 m_fThetaInc = calc_sweep_delta( 999 m_format.u.raw_audio, 1000 m_fSweepRate); 1001 } 1002 1003 // broadcast 1004 BroadcastNewParameterValue( 1005 now, 1006 id, 1007 &m_fSweepRate, 1008 size); 1009 break; 1010 1011 case P_DELAY: 1012 if(value == m_fDelay) 1013 break; 1014 1015 // set 1016 m_fDelay = value; 1017 m_tpDelayChanged = now; 1018 1019 if(m_output.destination != media_destination::null) { 1020 m_fSweepBase = calc_sweep_base( 1021 m_format.u.raw_audio, 1022 m_fDelay, m_fDepth); 1023 } 1024 1025 // broadcast 1026 BroadcastNewParameterValue( 1027 now, 1028 id, 1029 &m_fDelay, 1030 size); 1031 break; 1032 1033 case P_DEPTH: 1034 if(value == m_fDepth) 1035 break; 1036 1037 // set 1038 m_fDepth = value; 1039 m_tpDepthChanged = now; 1040 1041 if(m_output.destination != media_destination::null) { 1042 m_fSweepBase = calc_sweep_base( 1043 m_format.u.raw_audio, 1044 m_fDelay, m_fDepth); 1045 m_fSweepFactor = calc_sweep_factor( 1046 m_format.u.raw_audio, 1047 m_fDepth); 1048 } 1049 1050 // broadcast 1051 BroadcastNewParameterValue( 1052 now, 1053 id, 1054 &m_fDepth, 1055 size); 1056 break; 1057 1058 case P_FEEDBACK: 1059 if(value == m_fFeedback) 1060 break; 1061 1062 // set 1063 m_fFeedback = value; 1064 m_tpFeedbackChanged = now; 1065 // broadcast 1066 BroadcastNewParameterValue( 1067 now, 1068 id, 1069 &m_fFeedback, 1070 size); 1071 break; 1072 } 1073 } 1074 1075 void FlangerNode::handleStartEvent( 1076 const media_timed_event* pEvent) { 1077 PRINT(("FlangerNode::handleStartEvent\n")); 1078 1079 startFilter(); 1080 } 1081 1082 void FlangerNode::handleStopEvent( 1083 const media_timed_event* pEvent) { 1084 PRINT(("FlangerNode::handleStopEvent\n")); 1085 1086 stopFilter(); 1087 } 1088 1089 void FlangerNode::ignoreEvent( 1090 const media_timed_event* pEvent) { 1091 PRINT(("FlangerNode::ignoreEvent\n")); 1092 1093 } 1094 1095 1096 // -------------------------------------------------------- // 1097 // *** internal operations 1098 // -------------------------------------------------------- // 1099 1100 1101 // figure the preferred format: any fields left as wildcards 1102 // are negotiable 1103 void FlangerNode::getPreferredFormat( 1104 media_format& ioFormat) { 1105 ASSERT(ioFormat.type == B_MEDIA_RAW_AUDIO); 1106 1107 ioFormat.u.raw_audio = media_raw_audio_format::wildcard; 1108 ioFormat.u.raw_audio.channel_count = 1; 1109 ioFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; 1110 1111 // ioFormat.u.raw_audio.frame_rate = 44100.0; 1112 // ioFormat.u.raw_audio.buffer_size = 0x1000; 1113 } 1114 1115 // test the given template format against a proposed format. 1116 // specialize wildcards for fields where the template contains 1117 // non-wildcard data; write required fields into proposed format 1118 // if they mismatch. 1119 // Returns B_OK if the proposed format doesn't conflict with the 1120 // template, or B_MEDIA_BAD_FORMAT otherwise. 1121 1122 status_t FlangerNode::validateProposedFormat( 1123 const media_format& preferredFormat, 1124 media_format& ioProposedFormat) { 1125 1126 char formatStr[256]; 1127 PRINT(("FlangerNode::validateProposedFormat()\n")); 1128 1129 ASSERT(preferredFormat.type == B_MEDIA_RAW_AUDIO); 1130 1131 string_for_format(preferredFormat, formatStr, 255); 1132 PRINT(("\ttemplate format: %s\n", formatStr)); 1133 1134 string_for_format(ioProposedFormat, formatStr, 255); 1135 PRINT(("\tproposed format: %s\n", formatStr)); 1136 1137 status_t err = B_OK; 1138 1139 if(ioProposedFormat.type != B_MEDIA_RAW_AUDIO) { 1140 // out of the ballpark 1141 ioProposedFormat = preferredFormat; 1142 return B_MEDIA_BAD_FORMAT; 1143 } 1144 1145 // wildcard format 1146 const media_raw_audio_format& wild = media_raw_audio_format::wildcard; 1147 // proposed format 1148 media_raw_audio_format& f = ioProposedFormat.u.raw_audio; 1149 // template format 1150 const media_raw_audio_format& pref = preferredFormat.u.raw_audio; 1151 1152 if(pref.frame_rate != wild.frame_rate) { 1153 if(f.frame_rate != pref.frame_rate) { 1154 if(f.frame_rate != wild.frame_rate) 1155 err = B_MEDIA_BAD_FORMAT; 1156 f.frame_rate = pref.frame_rate; 1157 } 1158 } 1159 1160 if(pref.channel_count != wild.channel_count) { 1161 if(f.channel_count != pref.channel_count) { 1162 if(f.channel_count != wild.channel_count) 1163 err = B_MEDIA_BAD_FORMAT; 1164 f.channel_count = pref.channel_count; 1165 } 1166 } 1167 1168 if(pref.format != wild.format) { 1169 if(f.format != pref.format) { 1170 if(f.format != wild.format) 1171 err = B_MEDIA_BAD_FORMAT; 1172 f.format = pref.format; 1173 } 1174 } 1175 1176 if(pref.byte_order != wild.byte_order) { 1177 if(f.byte_order != pref.byte_order) { 1178 if(f.byte_order != wild.byte_order) 1179 err = B_MEDIA_BAD_FORMAT; 1180 f.byte_order = pref.byte_order; 1181 } 1182 } 1183 1184 if(pref.buffer_size != wild.buffer_size) { 1185 if(f.buffer_size != pref.buffer_size) { 1186 if(f.buffer_size != wild.buffer_size) 1187 err = B_MEDIA_BAD_FORMAT; 1188 f.buffer_size = pref.buffer_size; 1189 } 1190 } 1191 1192 if(err != B_OK) { 1193 string_for_format(ioProposedFormat, formatStr, 255); 1194 PRINT(( 1195 "\tformat conflict; suggesting:\n\tformat %s\n", formatStr)); 1196 } 1197 1198 return err; 1199 } 1200 1201 // fill in wildcards in the given format. 1202 // (assumes the format passes validateProposedFormat().) 1203 void FlangerNode::specializeOutputFormat( 1204 media_format& ioFormat) { 1205 1206 char formatStr[256]; 1207 string_for_format(ioFormat, formatStr, 255); 1208 PRINT(("FlangerNode::specializeOutputFormat()\n" 1209 "\tinput format: %s\n", formatStr)); 1210 1211 ASSERT(ioFormat.type == B_MEDIA_RAW_AUDIO); 1212 1213 // carpal_tunnel_paranoia 1214 media_raw_audio_format& f = ioFormat.u.raw_audio; 1215 const media_raw_audio_format& w = media_raw_audio_format::wildcard; 1216 1217 if (f.frame_rate == w.frame_rate) 1218 f.frame_rate = 44100.0; 1219 if (f.channel_count == w.channel_count) { 1220 //+++++ tweaked 15sep99 1221 if (m_input.source != media_source::null) 1222 f.channel_count = m_input.format.u.raw_audio.channel_count; 1223 else 1224 f.channel_count = 1; 1225 } 1226 if (f.format == w.format) 1227 f.format = media_raw_audio_format::B_AUDIO_FLOAT; 1228 if (f.byte_order == w.byte_order) 1229 f.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN; 1230 if (f.buffer_size == w.buffer_size) 1231 f.buffer_size = 2048; 1232 1233 string_for_format(ioFormat, formatStr, 255); 1234 PRINT(("\toutput format: %s\n", formatStr)); 1235 } 1236 1237 // set parameters to their default settings 1238 void FlangerNode::initParameterValues() { 1239 m_fMixRatio = 0.5; 1240 m_tpMixRatioChanged = 0LL; 1241 1242 m_fSweepRate = 0.1; 1243 m_tpSweepRateChanged = 0LL; 1244 1245 m_fDelay = 10.0; 1246 m_tpDelayChanged = 0LL; 1247 1248 m_fDepth = 25.0; 1249 m_tpDepthChanged = 0LL; 1250 1251 m_fFeedback = 0.1; 1252 m_tpFeedbackChanged = 0LL; 1253 } 1254 1255 // create and register a parameter web 1256 void FlangerNode::initParameterWeb() { 1257 BParameterWeb* pWeb = new BParameterWeb(); 1258 BParameterGroup* pTopGroup = pWeb->MakeGroup( 1259 B_TRANSLATE("FlangerNode parameters")); 1260 1261 BNullParameter* label; 1262 BContinuousParameter* value; 1263 BParameterGroup* g; 1264 1265 // mix ratio 1266 g = pTopGroup->MakeGroup(B_TRANSLATE("Mix ratio")); 1267 label = g->MakeNullParameter( 1268 P_MIX_RATIO_LABEL, 1269 B_MEDIA_NO_TYPE, 1270 B_TRANSLATE("Mix ratio"), 1271 B_GENERIC); 1272 1273 value = g->MakeContinuousParameter( 1274 P_MIX_RATIO, 1275 B_MEDIA_NO_TYPE, 1276 "", 1277 B_GAIN, "", 0.0, 1.0, 0.05); 1278 label->AddOutput(value); 1279 value->AddInput(label); 1280 1281 // sweep rate 1282 g = pTopGroup->MakeGroup(B_TRANSLATE("Sweep rate")); 1283 label = g->MakeNullParameter( 1284 P_SWEEP_RATE_LABEL, 1285 B_MEDIA_NO_TYPE, 1286 B_TRANSLATE("Sweep rate"), 1287 B_GENERIC); 1288 1289 value = g->MakeContinuousParameter( 1290 P_SWEEP_RATE, 1291 B_MEDIA_NO_TYPE, 1292 "", 1293 B_GAIN, "Hz", 0.01, 10.0, 0.01); 1294 label->AddOutput(value); 1295 value->AddInput(label); 1296 1297 // sweep range: minimum delay 1298 g = pTopGroup->MakeGroup(B_TRANSLATE("Delay")); 1299 label = g->MakeNullParameter( 1300 P_DELAY_LABEL, 1301 B_MEDIA_NO_TYPE, 1302 B_TRANSLATE("Delay"), 1303 B_GENERIC); 1304 1305 value = g->MakeContinuousParameter( 1306 P_DELAY, 1307 B_MEDIA_NO_TYPE, 1308 "", 1309 B_GAIN, "ms", 0.1, s_fMaxDelay/2.0, 0.1); 1310 label->AddOutput(value); 1311 value->AddInput(label); 1312 1313 // sweep range: maximum 1314 g = pTopGroup->MakeGroup(B_TRANSLATE("Depth")); 1315 label = g->MakeNullParameter( 1316 P_DEPTH_LABEL, 1317 B_MEDIA_NO_TYPE, 1318 B_TRANSLATE("Depth"), 1319 B_GENERIC); 1320 1321 value = g->MakeContinuousParameter( 1322 P_DEPTH, 1323 B_MEDIA_NO_TYPE, 1324 "", 1325 B_GAIN, "ms", 1.0, s_fMaxDelay/4.0, 0.1); 1326 label->AddOutput(value); 1327 value->AddInput(label); 1328 1329 // feedback 1330 g = pTopGroup->MakeGroup(B_TRANSLATE("Feedback")); 1331 label = g->MakeNullParameter( 1332 P_FEEDBACK_LABEL, 1333 B_MEDIA_NO_TYPE, 1334 B_TRANSLATE("Feedback"), 1335 B_GENERIC); 1336 1337 value = g->MakeContinuousParameter( 1338 P_FEEDBACK, 1339 B_MEDIA_NO_TYPE, 1340 "", 1341 B_GAIN, "", 0.0, 1.0, 0.01); 1342 label->AddOutput(value); 1343 value->AddInput(label); 1344 1345 // * Install parameter web 1346 SetParameterWeb(pWeb); 1347 } 1348 1349 // construct delay line if necessary, reset filter state 1350 void FlangerNode::initFilter() { 1351 PRINT(("FlangerNode::initFilter()\n")); 1352 ASSERT(m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format); 1353 1354 if(!m_pDelayBuffer) { 1355 m_pDelayBuffer = new AudioBuffer( 1356 m_format.u.raw_audio, 1357 frames_for_duration( 1358 m_format.u.raw_audio, 1359 (bigtime_t)s_fMaxDelay*1000LL)); 1360 m_pDelayBuffer->zero(); 1361 } 1362 1363 m_framesSent = 0; 1364 m_delayWriteFrame = 0; 1365 m_fTheta = 0.0; 1366 m_fThetaInc = calc_sweep_delta(m_format.u.raw_audio, m_fSweepRate); 1367 m_fSweepBase = calc_sweep_base(m_format.u.raw_audio, m_fDelay, m_fDepth); 1368 m_fSweepFactor = calc_sweep_factor(m_format.u.raw_audio, m_fDepth); 1369 1370 // 1371 // PRINT(( 1372 // "\tFrames %ld\n" 1373 // "\tDelay %.2f\n" 1374 // "\tDepth %.2f\n" 1375 // "\tSweepBase %.2f\n" 1376 // "\tSweepFactor %.2f\n", 1377 // m_pDelayBuffer->frames(), 1378 // m_fDelay, m_fDepth, m_fSweepBase, m_fSweepFactor)); 1379 } 1380 1381 void FlangerNode::startFilter() { 1382 PRINT(("FlangerNode::startFilter()\n")); 1383 } 1384 void FlangerNode::stopFilter() { 1385 PRINT(("FlangerNode::stopFilter()\n")); 1386 } 1387 1388 // figure processing latency by doing 'dry runs' of filterBuffer() 1389 bigtime_t FlangerNode::calcProcessingLatency() { 1390 PRINT(("FlangerNode::calcProcessingLatency()\n")); 1391 1392 if(m_output.destination == media_destination::null) { 1393 PRINT(("\tNot connected.\n")); 1394 return 0LL; 1395 } 1396 1397 // allocate a temporary buffer group 1398 BBufferGroup* pTestGroup = new BBufferGroup( 1399 m_output.format.u.raw_audio.buffer_size, 1); 1400 1401 // fetch a buffer 1402 BBuffer* pBuffer = pTestGroup->RequestBuffer( 1403 m_output.format.u.raw_audio.buffer_size); 1404 ASSERT(pBuffer); 1405 1406 pBuffer->Header()->type = B_MEDIA_RAW_AUDIO; 1407 pBuffer->Header()->size_used = m_output.format.u.raw_audio.buffer_size; 1408 1409 // run the test 1410 bigtime_t preTest = system_time(); 1411 filterBuffer(pBuffer); 1412 bigtime_t elapsed = system_time()-preTest; 1413 1414 // clean up 1415 pBuffer->Recycle(); 1416 delete pTestGroup; 1417 1418 // reset filter state 1419 initFilter(); 1420 1421 return elapsed; 1422 } 1423 1424 // filter buffer data in place 1425 // 1426 // +++++ add 2-channel support 15sep991 1427 // 1428 1429 const size_t MAX_CHANNELS = 2; 1430 1431 struct _frame { 1432 float channel[MAX_CHANNELS]; 1433 }; 1434 1435 void FlangerNode::filterBuffer( 1436 BBuffer* pBuffer) { 1437 1438 if(!m_pDelayBuffer) 1439 return; 1440 1441 // for each input frame: 1442 // - fetch 1443 // - write delay line(writeFrame) 1444 // - read delay line(writeFrame-readOffset) [interpolate] 1445 // - mix (replace) 1446 // - advance writeFrame 1447 // - update readOffset 1448 1449 AudioBuffer input(m_format.u.raw_audio, pBuffer); 1450 1451 ASSERT( 1452 m_format.u.raw_audio.channel_count == 1 || 1453 m_format.u.raw_audio.channel_count == 2); 1454 uint32 channels = m_format.u.raw_audio.channel_count; 1455 bool stereo = m_format.u.raw_audio.channel_count == 2; 1456 1457 uint32 samples = input.frames() * channels; 1458 for(uint32 inPos = 0; inPos < samples; ++inPos) { 1459 1460 // read from input buffer 1461 _frame inFrame = {}; 1462 inFrame.channel[0] = ((float*)input.data())[inPos]; 1463 if(stereo) 1464 inFrame.channel[1] = ((float*)input.data())[inPos + 1]; 1465 1466 // interpolate from delay buffer 1467 float readOffset = m_fSweepBase + (m_fSweepFactor * sin(m_fTheta)); 1468 float fReadFrame = (float)m_delayWriteFrame - readOffset; 1469 if(fReadFrame < 0.0) 1470 fReadFrame += m_pDelayBuffer->frames(); 1471 1472 // float delayed; 1473 1474 1475 // read low-index (possibly only) frame 1476 _frame delayedFrame = {}; 1477 1478 int32 readFrameLo = (int32)floor(fReadFrame); 1479 uint32 pos = readFrameLo * channels; 1480 delayedFrame.channel[0] = ((float*)m_pDelayBuffer->data())[pos]; 1481 if(stereo) 1482 delayedFrame.channel[1] = ((float*)m_pDelayBuffer->data())[pos+1]; 1483 1484 if(readFrameLo != (int32)fReadFrame) { 1485 1486 // interpolate (A) 1487 uint32 readFrameHi = (int32)ceil(fReadFrame); 1488 delayedFrame.channel[0] *= ((float)readFrameHi - fReadFrame); 1489 if(stereo) 1490 delayedFrame.channel[1] *= ((float)readFrameHi - fReadFrame); 1491 1492 // read high-index frame 1493 int32 hiWrap = (readFrameHi == m_pDelayBuffer->frames()) ? 0 : readFrameHi; 1494 ASSERT(hiWrap >= 0); 1495 pos = (uint32)hiWrap * channels; 1496 _frame hiFrame; 1497 hiFrame.channel[0] = ((float*)m_pDelayBuffer->data())[pos]; 1498 if(stereo) 1499 hiFrame.channel[1] = ((float*)m_pDelayBuffer->data())[pos+1]; 1500 1501 // interpolate (B) 1502 delayedFrame.channel[0] += 1503 hiFrame.channel[0] * (fReadFrame - (float)readFrameLo); 1504 if(stereo) 1505 delayedFrame.channel[1] += 1506 hiFrame.channel[1] * (fReadFrame - (float)readFrameLo); 1507 } 1508 1509 // mix back to output buffer 1510 ((float*)input.data())[inPos] = 1511 (inFrame.channel[0] * (1.0-m_fMixRatio)) + 1512 (delayedFrame.channel[0] * m_fMixRatio); 1513 if(stereo) 1514 ((float*)input.data())[inPos+1] = 1515 (inFrame.channel[1] * (1.0-m_fMixRatio)) + 1516 (delayedFrame.channel[1] * m_fMixRatio); 1517 1518 // write to delay buffer 1519 uint32 delayWritePos = m_delayWriteFrame * channels; 1520 ((float*)m_pDelayBuffer->data())[delayWritePos] = 1521 inFrame.channel[0] + 1522 (delayedFrame.channel[0] * m_fFeedback); 1523 if(stereo) 1524 ((float*)m_pDelayBuffer->data())[delayWritePos+1] = 1525 inFrame.channel[1] + 1526 (delayedFrame.channel[1] * m_fFeedback); 1527 1528 // advance write position 1529 if(++m_delayWriteFrame >= m_pDelayBuffer->frames()) 1530 m_delayWriteFrame = 0; 1531 1532 // advance read offset ('LFO') 1533 m_fTheta += m_fThetaInc; 1534 if(m_fTheta > 2 * M_PI) 1535 m_fTheta -= 2 * M_PI; 1536 1537 // if(m_fDelayReadDelta < 0.0) { 1538 // if(m_fDelayReadOffset < m_fDelay) 1539 // m_fDelayReadDelta = -m_fDelayReadDelta; 1540 // } else { 1541 // if(m_fDelayReadOffset > m_fDepth) 1542 // m_fDelayReadDelta = -m_fDelayReadDelta; 1543 // } 1544 // m_fDelayReadOffset += m_fDelayReadDelta; 1545 } 1546 } 1547 1548 1549 /*! Figure the rate at which the (radial) read offset changes, 1550 based on the given sweep rate (in Hz) 1551 */ 1552 float 1553 calc_sweep_delta(const media_raw_audio_format& format, float fRate) 1554 { 1555 return 2 * M_PI * fRate / format.frame_rate; 1556 } 1557 1558 /*! Figure the base delay (in frames) based on the given 1559 sweep delay/depth (in msec) 1560 */ 1561 float 1562 calc_sweep_base(const media_raw_audio_format& format, float delay, float depth) 1563 { 1564 return (format.frame_rate * (delay + depth)) / 1000.0; 1565 } 1566 1567 1568 float 1569 calc_sweep_factor(const media_raw_audio_format& format, float depth) 1570 { 1571 return (format.frame_rate * depth) / 1000.0; 1572 } 1573 1574 1575 // END -- FlangerNode.cpp -- 1576