Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(846)

Side by Side Diff: third_party/WebKit/Source/modules/webaudio/AudioNode.cpp

Issue 2839063003: Implement tail processing for AudioNodes (Closed)
Patch Set: Make declaration order consistent Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2010, Google Inc. All rights reserved. 2 * Copyright (C) 2010, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 DCHECK_EQ(new_channel_count_mode_, channel_count_mode_); 83 DCHECK_EQ(new_channel_count_mode_, channel_count_mode_);
84 DCHECK_EQ(new_channel_interpretation_, channel_interpretation_); 84 DCHECK_EQ(new_channel_interpretation_, channel_interpretation_);
85 85
86 is_initialized_ = true; 86 is_initialized_ = true;
87 } 87 }
88 88
89 void AudioHandler::Uninitialize() { 89 void AudioHandler::Uninitialize() {
90 is_initialized_ = false; 90 is_initialized_ = false;
91 } 91 }
92 92
93 void AudioHandler::ClearInternalStateWhenDisabled() {}
94
95 void AudioHandler::Dispose() { 93 void AudioHandler::Dispose() {
96 DCHECK(IsMainThread()); 94 DCHECK(IsMainThread());
97 DCHECK(Context()->IsGraphOwner()); 95 DCHECK(Context()->IsGraphOwner());
98 96
99 Context()->GetDeferredTaskHandler().RemoveChangedChannelCountMode(this); 97 Context()->GetDeferredTaskHandler().RemoveChangedChannelCountMode(this);
100 Context()->GetDeferredTaskHandler().RemoveChangedChannelInterpretation(this); 98 Context()->GetDeferredTaskHandler().RemoveChangedChannelInterpretation(this);
101 Context()->GetDeferredTaskHandler().RemoveAutomaticPullNode(this); 99 Context()->GetDeferredTaskHandler().RemoveAutomaticPullNode(this);
102 for (auto& output : outputs_) 100 for (auto& output : outputs_)
103 output->Dispose(); 101 output->Dispose();
104 node_ = nullptr; 102 node_ = nullptr;
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 // have the results cached in their bus; 316 // have the results cached in their bus;
319 double current_time = Context()->currentTime(); 317 double current_time = Context()->currentTime();
320 if (last_processing_time_ != current_time) { 318 if (last_processing_time_ != current_time) {
321 // important to first update this time because of feedback loops in the 319 // important to first update this time because of feedback loops in the
322 // rendering graph. 320 // rendering graph.
323 last_processing_time_ = current_time; 321 last_processing_time_ = current_time;
324 322
325 PullInputs(frames_to_process); 323 PullInputs(frames_to_process);
326 324
327 bool silent_inputs = InputsAreSilent(); 325 bool silent_inputs = InputsAreSilent();
328 if (!silent_inputs) {
329 last_non_silent_time_ =
330 (Context()->CurrentSampleFrame() + frames_to_process) /
331 static_cast<double>(Context()->sampleRate());
332 }
333
334 if (silent_inputs && PropagatesSilence()) { 326 if (silent_inputs && PropagatesSilence()) {
335 SilenceOutputs(); 327 SilenceOutputs();
336 // AudioParams still need to be processed so that the value can be updated 328 // AudioParams still need to be processed so that the value can be updated
337 // if there are automations or so that the upstream nodes get pulled if 329 // if there are automations or so that the upstream nodes get pulled if
338 // any are connected to the AudioParam. 330 // any are connected to the AudioParam.
339 ProcessOnlyAudioParams(frames_to_process); 331 ProcessOnlyAudioParams(frames_to_process);
340 } else { 332 } else {
341 // Unsilence the outputs first because the processing of the node may 333 // Unsilence the outputs first because the processing of the node may
342 // cause the outputs to go silent and we want to propagate that hint to 334 // cause the outputs to go silent and we want to propagate that hint to
343 // the downstream nodes. (For example, a Gain node with a gain of 0 will 335 // the downstream nodes. (For example, a Gain node with a gain of 0 will
344 // want to silence its output.) 336 // want to silence its output.)
345 UnsilenceOutputs(); 337 UnsilenceOutputs();
346 Process(frames_to_process); 338 Process(frames_to_process);
347 } 339 }
340
341 if (!silent_inputs) {
342 // Update last_non_silent_time AFTER processing this block.
343 // Doing it before causes PropagateSilence to be one render
344 // quantum longer than necessary.
345 last_non_silent_time_ =
346 (Context()->CurrentSampleFrame() + frames_to_process) /
347 static_cast<double>(Context()->sampleRate());
348 }
348 } 349 }
349 } 350 }
350 351
351 void AudioHandler::CheckNumberOfChannelsForInput(AudioNodeInput* input) { 352 void AudioHandler::CheckNumberOfChannelsForInput(AudioNodeInput* input) {
352 DCHECK(Context()->IsAudioThread()); 353 DCHECK(Context()->IsAudioThread());
353 DCHECK(Context()->IsGraphOwner()); 354 DCHECK(Context()->IsGraphOwner());
354 355
355 DCHECK(inputs_.Contains(input)); 356 DCHECK(inputs_.Contains(input));
356 if (!inputs_.Contains(input)) 357 if (!inputs_.Contains(input))
357 return; 358 return;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 DCHECK(IsMainThread()); 396 DCHECK(IsMainThread());
396 BaseAudioContext::AutoLocker locker(Context()); 397 BaseAudioContext::AutoLocker locker(Context());
397 398
398 is_disabled_ = false; 399 is_disabled_ = false;
399 for (auto& output : outputs_) 400 for (auto& output : outputs_)
400 output->Enable(); 401 output->Enable();
401 } 402 }
402 } 403 }
403 404
404 void AudioHandler::DisableOutputsIfNecessary() { 405 void AudioHandler::DisableOutputsIfNecessary() {
406 // This function calls other functions that require graph ownership,
407 // so assert that this needs graph ownership too.
408 DCHECK(Context()->IsGraphOwner());
409
405 // Disable outputs if appropriate. We do this if the number of connections is 410 // Disable outputs if appropriate. We do this if the number of connections is
406 // 0 or 1. The case of 0 is from deref() where there are no connections left. 411 // 0 or 1. The case of 0 is from deref() where there are no connections left.
407 // The case of 1 is from AudioNodeInput::disable() where we want to disable 412 // The case of 1 is from AudioNodeInput::disable() where we want to disable
408 // outputs when there's only one connection left because we're ready to go 413 // outputs when there's only one connection left because we're ready to go
409 // away, but can't quite yet. 414 // away, but can't quite yet.
410 if (connection_ref_count_ <= 1 && !is_disabled_) { 415 if (connection_ref_count_ <= 1 && !is_disabled_) {
411 // Still may have JavaScript references, but no more "active" connection 416 // Still may have JavaScript references, but no more "active" connection
412 // references, so put all of our outputs in a "dormant" disabled state. 417 // references, so put all of our outputs in a "dormant" disabled state.
413 // Garbage collection may take a very long time after this time, so the 418 // Garbage collection may take a very long time after this time, so the
414 // "dormant" disabled nodes should not bog down the rendering... 419 // "dormant" disabled nodes should not bog down the rendering...
415 420
416 // As far as JavaScript is concerned, our outputs must still appear to be 421 // As far as JavaScript is concerned, our outputs must still appear to be
417 // connected. But internally our outputs should be disabled from the inputs 422 // connected. But internally our outputs should be disabled from the inputs
418 // they're connected to. disable() can recursively deref connections (and 423 // they're connected to. disable() can recursively deref connections (and
419 // call disable()) down a whole chain of connected nodes. 424 // call disable()) down a whole chain of connected nodes.
420 425
421 // TODO(rtoy,hongchan): we need special cases the convolver, delay, biquad, 426 // If a node requires tail processing, we defer the disabling of
422 // and IIR since they have a significant tail-time and shouldn't be 427 // the outputs so that the tail for the node can be output.
423 // disconnected simply because they no longer have any input connections. 428 // Otherwise, we can disable the outputs right away.
424 // This needs to be handled more generally where AudioNodes have a tailTime 429 if (RequiresTailProcessing()) {
425 // attribute. Then the AudioNode only needs to remain "active" for tailTime 430 if (Context()->ContextState() !=
426 // seconds after there are no longer any active connections. 431 BaseAudioContext::AudioContextState::kClosed) {
427 // 432 Context()->GetDeferredTaskHandler().AddTailProcessingNode(this);
428 // The analyser node also requires special handling because we 433 }
429 // need the internal state to be updated for the time and FFT data 434 } else {
430 // even if it has no connections. 435 ReallyDisableOutputs();
431 if (GetNodeType() != kNodeTypeConvolver &&
432 GetNodeType() != kNodeTypeDelay &&
433 GetNodeType() != kNodeTypeBiquadFilter &&
434 GetNodeType() != kNodeTypeIIRFilter &&
435 GetNodeType() != kNodeTypeAnalyser) {
436 is_disabled_ = true;
437 ClearInternalStateWhenDisabled();
438 for (auto& output : outputs_)
439 output->Disable();
440 } 436 }
441 } 437 }
442 } 438 }
443 439
440 void AudioHandler::ReallyDisableOutputs() {
441 is_disabled_ = true;
442 for (auto& output : outputs_)
443 output->Disable();
444 }
445
444 void AudioHandler::MakeConnection() { 446 void AudioHandler::MakeConnection() {
445 AtomicIncrement(&connection_ref_count_); 447 AtomicIncrement(&connection_ref_count_);
446 448
449 Context()->GetDeferredTaskHandler().RemoveTailProcessingNode(this);
450
447 #if DEBUG_AUDIONODE_REFERENCES 451 #if DEBUG_AUDIONODE_REFERENCES
448 fprintf(stderr, "[%16p]: %16p: %2d: AudioHandler::ref %3d [%3d]\n", 452 fprintf(stderr, "[%16p]: %16p: %2d: AudioHandler::ref %3d [%3d] @%.15g\n",
449 Context(), this, GetNodeType(), connection_ref_count_, 453 Context(), this, GetNodeType(), connection_ref_count_,
450 node_count_[GetNodeType()]); 454 node_count_[GetNodeType()], Context()->currentTime());
451 #endif 455 #endif
452 // See the disabling code in disableOutputsIfNecessary(). This handles 456 // See the disabling code in disableOutputsIfNecessary(). This handles
453 // the case where a node is being re-connected after being used at least 457 // the case where a node is being re-connected after being used at least
454 // once and disconnected. In this case, we need to re-enable. 458 // once and disconnected. In this case, we need to re-enable.
455 EnableOutputsIfNecessary(); 459 EnableOutputsIfNecessary();
456 } 460 }
457 461
458 void AudioHandler::BreakConnection() { 462 void AudioHandler::BreakConnection() {
459 // The actual work for deref happens completely within the audio context's 463 // The actual work for deref happens completely within the audio context's
460 // graph lock. In the case of the audio thread, we must use a tryLock to 464 // graph lock. In the case of the audio thread, we must use a tryLock to
(...skipping 15 matching lines...) Expand all
476 // later. 480 // later.
477 DCHECK(Context()->IsAudioThread()); 481 DCHECK(Context()->IsAudioThread());
478 Context()->GetDeferredTaskHandler().AddDeferredBreakConnection(*this); 482 Context()->GetDeferredTaskHandler().AddDeferredBreakConnection(*this);
479 } 483 }
480 } 484 }
481 485
482 void AudioHandler::BreakConnectionWithLock() { 486 void AudioHandler::BreakConnectionWithLock() {
483 AtomicDecrement(&connection_ref_count_); 487 AtomicDecrement(&connection_ref_count_);
484 488
485 #if DEBUG_AUDIONODE_REFERENCES 489 #if DEBUG_AUDIONODE_REFERENCES
486 fprintf(stderr, "[%16p]: %16p: %2d: AudioHandler::deref %3d [%3d]\n", 490 fprintf(stderr, "[%16p]: %16p: %2d: AudioHandler::deref %3d [%3d] @%.15g\n",
487 Context(), this, GetNodeType(), connection_ref_count_, 491 Context(), this, GetNodeType(), connection_ref_count_,
488 node_count_[GetNodeType()]); 492 node_count_[GetNodeType()], Context()->currentTime());
489 #endif 493 #endif
490 494
491 if (!connection_ref_count_) 495 if (!connection_ref_count_)
492 DisableOutputsIfNecessary(); 496 DisableOutputsIfNecessary();
493 } 497 }
494 498
495 #if DEBUG_AUDIONODE_REFERENCES 499 #if DEBUG_AUDIONODE_REFERENCES
496 500
497 bool AudioHandler::is_node_count_initialized_ = false; 501 bool AudioHandler::is_node_count_initialized_ = false;
498 int AudioHandler::node_count_[kNodeTypeEnd]; 502 int AudioHandler::node_count_[kNodeTypeEnd];
499 503
500 void AudioHandler::PrintNodeCounts() { 504 void AudioHandler::PrintNodeCounts() {
501 fprintf(stderr, "\n\n"); 505 fprintf(stderr, "\n\n");
502 fprintf(stderr, "===========================\n"); 506 fprintf(stderr, "===========================\n");
503 fprintf(stderr, "AudioNode: reference counts\n"); 507 fprintf(stderr, "AudioNode: reference counts\n");
504 fprintf(stderr, "===========================\n"); 508 fprintf(stderr, "===========================\n");
505 509
506 for (unsigned i = 0; i < kNodeTypeEnd; ++i) 510 for (unsigned i = 0; i < kNodeTypeEnd; ++i)
507 fprintf(stderr, "%2d: %d\n", i, node_count_[i]); 511 fprintf(stderr, "%2d: %d\n", i, node_count_[i]);
508 512
509 fprintf(stderr, "===========================\n\n\n"); 513 fprintf(stderr, "===========================\n\n\n");
510 } 514 }
511 515
512 #endif // DEBUG_AUDIONODE_REFERENCES 516 #endif // DEBUG_AUDIONODE_REFERENCES
513 517
518 #if DEBUG_AUDIONODE_REFERENCES > 1
519 void AudioHandler::TailProcessingDebug(const char* note) {
520 fprintf(stderr, "[%16p]: %16p: %2d: %s %d @%.15g", Context(), this,
521 GetNodeType(), note, connection_ref_count_, Context()->currentTime());
522
523 // If we're on the audio thread, we can print out the tail and
524 // latency times (because these methods can only be called from the
525 // audio thread.)
526 if (Context()->IsAudioThread()) {
527 fprintf(stderr, ", tail=%.15g + %.15g, last=%.15g\n", TailTime(),
528 LatencyTime(), last_non_silent_time_);
529 }
530
531 fprintf(stderr, "\n");
532 }
533
534 void AudioHandler::AddTailProcessingDebug() {
535 TailProcessingDebug("addTail");
536 }
537
538 void AudioHandler::RemoveTailProcessingDebug() {
539 TailProcessingDebug("remTail");
540 }
541 #endif // DEBUG_AUDIONODE_REFERENCES > 1
542
514 void AudioHandler::UpdateChannelCountMode() { 543 void AudioHandler::UpdateChannelCountMode() {
515 channel_count_mode_ = new_channel_count_mode_; 544 channel_count_mode_ = new_channel_count_mode_;
516 UpdateChannelsForInputs(); 545 UpdateChannelsForInputs();
517 } 546 }
518 547
519 void AudioHandler::UpdateChannelInterpretation() { 548 void AudioHandler::UpdateChannelInterpretation() {
520 channel_interpretation_ = new_channel_interpretation_; 549 channel_interpretation_ = new_channel_interpretation_;
521 } 550 }
522 551
523 unsigned AudioHandler::NumberOfOutputChannels() const { 552 unsigned AudioHandler::NumberOfOutputChannels() const {
(...skipping 437 matching lines...) Expand 10 before | Expand all | Expand 10 after
961 } 990 }
962 991
963 void AudioNode::DidAddOutput(unsigned number_of_outputs) { 992 void AudioNode::DidAddOutput(unsigned number_of_outputs) {
964 connected_nodes_.push_back(nullptr); 993 connected_nodes_.push_back(nullptr);
965 DCHECK_EQ(number_of_outputs, connected_nodes_.size()); 994 DCHECK_EQ(number_of_outputs, connected_nodes_.size());
966 connected_params_.push_back(nullptr); 995 connected_params_.push_back(nullptr);
967 DCHECK_EQ(number_of_outputs, connected_params_.size()); 996 DCHECK_EQ(number_of_outputs, connected_params_.size());
968 } 997 }
969 998
970 } // namespace blink 999 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698