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