Introduce AudioBuffers for user access in ScriptProcessorNode

This CL adds new AudioBuffers for the access from the user code.

Bug: 1177465
Test: The local ASAN build doesn't reproduce on given POCs.
Change-Id: Id9a3505ddb9ab61b4442385d0b830ef56f65f797
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2718543
Auto-Submit: Hongchan Choi <hongchan@chromium.org>
Reviewed-by: Raymond Toy <rtoy@chromium.org>
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#857817}
diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_node.cc b/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
index f0caef87..5dd0c89 100644
--- a/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
@@ -44,6 +44,28 @@
 
 namespace blink {
 
+namespace {
+
+bool IsAudioBufferDetached(AudioBuffer* buffer) {
+  bool is_buffer_detached = false;
+  for (unsigned channel = 0; channel < buffer->numberOfChannels(); ++channel) {
+    if (buffer->getChannelData(channel)->buffer()->IsDetached()) {
+      is_buffer_detached = true;
+      break;
+    }
+  }
+
+  return is_buffer_detached;
+}
+
+bool BufferTopologyMatches(AudioBuffer* buffer_1, AudioBuffer* buffer_2) {
+  return (buffer_1->numberOfChannels() == buffer_2->numberOfChannels()) &&
+         (buffer_1->length() == buffer_2->length()) &&
+         (buffer_1->sampleRate() == buffer_2->sampleRate());
+}
+
+}  // namespace
+
 ScriptProcessorHandler::ScriptProcessorHandler(
     AudioNode& node,
     float sample_rate,
@@ -346,6 +368,12 @@
     input_buffers_.push_back(input_buffer);
     output_buffers_.push_back(output_buffer);
   }
+
+  external_input_buffer_ = AudioBuffer::Create(
+      number_of_input_channels, buffer_size, sample_rate);
+  external_output_buffer_ = AudioBuffer::Create(
+      number_of_output_channels, buffer_size, sample_rate);
+
   SetHandler(ScriptProcessorHandler::Create(
       *this, sample_rate, buffer_size, number_of_input_channels,
       number_of_output_channels, input_buffers_, output_buffers_));
@@ -483,11 +511,62 @@
 
 void ScriptProcessorNode::DispatchEvent(double playback_time,
                                         uint32_t double_buffer_index) {
-  AudioBuffer* input_buffer = input_buffers_.at(double_buffer_index).Get();
-  AudioBuffer* output_buffer = output_buffers_.at(double_buffer_index).Get();
-  DCHECK(output_buffer);
+  DCHECK(IsMainThread());
+
+  AudioBuffer* backing_input_buffer =
+      input_buffers_.at(double_buffer_index).Get();
+
+  // The backing buffer can be nullptr, when the number of input channels is 0.
+  if (backing_input_buffer) {
+    // Also the author code might have transferred |external_input_buffer| to
+    // other threads or replaced it with a different AudioBuffer object. Then
+    // re-create a new buffer instance.
+    if (IsAudioBufferDetached(external_input_buffer_) ||
+        !BufferTopologyMatches(backing_input_buffer,
+                               external_input_buffer_)) {
+      external_input_buffer_ = AudioBuffer::Create(
+          backing_input_buffer->numberOfChannels(),
+          backing_input_buffer->length(),
+          backing_input_buffer->sampleRate());
+    }
+
+    for (unsigned channel = 0;
+         channel < backing_input_buffer->numberOfChannels(); ++channel) {
+      const float* source = static_cast<float*>(
+          backing_input_buffer->getChannelData(channel)->buffer()->Data());
+      float* destination = static_cast<float*>(
+          external_input_buffer_->getChannelData(channel)->buffer()->Data());
+      memcpy(destination, source,
+             backing_input_buffer->length() * sizeof(float));
+    }
+  }
+
   AudioNode::DispatchEvent(*AudioProcessingEvent::Create(
-      input_buffer, output_buffer, playback_time));
+      external_input_buffer_, external_output_buffer_, playback_time));
+
+  AudioBuffer* backing_output_buffer =
+      output_buffers_.at(double_buffer_index).Get();
+
+  if (backing_output_buffer) {
+    if (IsAudioBufferDetached(external_output_buffer_) ||
+        !BufferTopologyMatches(backing_output_buffer,
+                               external_output_buffer_)) {
+      external_output_buffer_ = AudioBuffer::Create(
+          backing_output_buffer->numberOfChannels(),
+          backing_output_buffer->length(),
+          backing_output_buffer->sampleRate());
+    }
+
+    for (unsigned channel = 0;
+         channel < backing_output_buffer->numberOfChannels(); ++channel) {
+      const float* source = static_cast<float*>(
+          external_output_buffer_->getChannelData(channel)->buffer()->Data());
+      float* destination = static_cast<float*>(
+          backing_output_buffer->getChannelData(channel)->buffer()->Data());
+      memcpy(destination, source,
+             backing_output_buffer->length() * sizeof(float));
+    }
+  }
 }
 
 bool ScriptProcessorNode::HasPendingActivity() const {
@@ -506,6 +585,8 @@
 void ScriptProcessorNode::Trace(Visitor* visitor) const {
   visitor->Trace(input_buffers_);
   visitor->Trace(output_buffers_);
+  visitor->Trace(external_input_buffer_);
+  visitor->Trace(external_output_buffer_);
   AudioNode::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_node.h b/third_party/blink/renderer/modules/webaudio/script_processor_node.h
index b166a04..9ae1789 100644
--- a/third_party/blink/renderer/modules/webaudio/script_processor_node.h
+++ b/third_party/blink/renderer/modules/webaudio/script_processor_node.h
@@ -168,6 +168,8 @@
  private:
   HeapVector<Member<AudioBuffer>> input_buffers_;
   HeapVector<Member<AudioBuffer>> output_buffers_;
+  Member<AudioBuffer> external_input_buffer_;
+  Member<AudioBuffer> external_output_buffer_;
 };
 
 }  // namespace blink