Commit 70d15d5d authored by Hongchan Choi's avatar Hongchan Choi Committed by Commit Bot

Initialize sampleRate in AudioWorkletGlobalScope upon construction

This CL changes how |sampleRate| in AudioWorkletGlobalScope is updated.

Previously, the sample rate was updated when the first processor was
created, thus accessing the sample rate in the class definition phase
gives 0 value. With this CL, the sample rate is initialized as soon
as the global scope is created, before the script is loaded/parsed.

Bug: 815313
Test: http/tests/webaudio/audio-worklet/global-sample-rate.html
Change-Id: I994ee70c10ed93618b28c7d29df42b5d458c74f8
Reviewed-on: https://chromium-review.googlesource.com/935747
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
Reviewed-by: default avatarRaymond Toy <rtoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#539254}
parent cba0bcce
<!DOCTYPE html>
<html>
<head>
<title>
Test sampleRate in AudioWorkletGlobalScope
</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../../webaudio-resources/audit.js"></script>
<script src="audio-worklet-common.js"></script>
</head>
<body>
<script id="layout-test-code">
// TODO(hongchan): remove this assertion when AudioWorklet shipped.
assertAudioWorklet();
let audit = Audit.createTaskRunner();
let sampleRate = 48000;
let renderLength = 512;
let context = new OfflineAudioContext(1, renderLength, sampleRate);
// Without rendering the context, attempt to access |sampleRate| in the
// global scope as soon as it is created.
audit.define(
'Query |sampleRate| upon AudioWorkletGlobalScope construction',
(task, should) => {
let onePoleFilterNode =
new AudioWorkletNode(context, 'one-pole-filter');
let frequencyParam = onePoleFilterNode.parameters.get('frequency');
should(frequencyParam.maxValue,
'frequencyParam.maxValue')
.beEqualTo(0.5 * context.sampleRate);
task.done();
});
context.audioWorklet.addModule('one-pole-processor.js').then(() => {
audit.run();
});
</script>
</body>
</html>
/**
* @class OnePoleFilter
* @extends AudioWorkletProcessor
*
* A simple One-pole filter.
*/
class OnePoleFilter extends AudioWorkletProcessor {
// This gets evaluated as soon as the global scope is created.
static get parameterDescriptors() {
return [{
name: 'frequency',
defaultValue: 250,
minValue: 0,
maxValue: 0.5 * sampleRate
}];
}
constructor() {
super();
this.updateCoefficientsWithFrequency_(250);
}
updateCoefficientsWithFrequency_(frequency) {
this.b1_ = Math.exp(-2 * Math.PI * frequency / sampleRate);
this.a0_ = 1.0 - this.b1_;
this.z1_ = 0;
}
process(inputs, outputs, parameters) {
let input = inputs[0];
let output = outputs[0];
let frequency = parameters.frequency;
for (let channel = 0; channel < output.length; ++channel) {
let inputChannel = input[channel];
let outputChannel = output[channel];
for (let i = 0; i < outputChannel.length; ++i) {
this.updateCoefficientsWithFrequency_(frequency[i]);
this.z1_ = inputChannel[i] * this.a0_ + this.z1_ * this.b1_;
outputChannel[i] = this.z1_;
}
}
return true;
}
}
registerProcessor('one-pole-filter', OnePoleFilter);
......@@ -48,6 +48,11 @@ WebThread* AudioWorklet::GetBackingThread() {
return GetMessagingProxy()->GetWorkletBackingThread();
}
BaseAudioContext* AudioWorklet::GetBaseAudioContext() const {
DCHECK(IsMainThread());
return context_.Get();
}
const Vector<CrossThreadAudioParamInfo>
AudioWorklet::GetParamInfoListForProcessor(
const String& name) {
......
......@@ -42,6 +42,8 @@ class MODULES_EXPORT AudioWorklet final : public Worklet {
WebThread* GetBackingThread();
BaseAudioContext* GetBaseAudioContext() const;
const Vector<CrossThreadAudioParamInfo> GetParamInfoListForProcessor(
const String& name);
......
......@@ -125,15 +125,10 @@ void AudioWorkletGlobalScope::registerProcessor(
AudioWorkletProcessor* AudioWorkletGlobalScope::CreateProcessor(
const String& name,
float sample_rate,
MessagePortChannel message_port_channel,
scoped_refptr<SerializedScriptValue> node_options) {
DCHECK(IsContextThread());
// TODO(hongchan): do this only once when the association between
// BaseAudioContext and AudioWorkletGlobalScope is established.
sample_rate_ = sample_rate;
// The registered definition is already checked by AudioWorkletNode
// construction process, so the |definition| here must be valid.
AudioWorkletProcessorDefinition* definition = FindDefinition(name);
......@@ -381,6 +376,10 @@ ProcessorCreationParams* AudioWorkletGlobalScope::GetProcessorCreationParams() {
return processor_creation_params_.get();
}
void AudioWorkletGlobalScope::SetSampleRate(float sample_rate) {
sample_rate_ = sample_rate;
}
void AudioWorkletGlobalScope::Trace(blink::Visitor* visitor) {
visitor->Trace(processor_definition_map_);
visitor->Trace(processor_instances_);
......
......@@ -72,7 +72,6 @@ class MODULES_EXPORT AudioWorkletGlobalScope final
// for some reason.
AudioWorkletProcessor* CreateProcessor(
const String& name,
float sample_rate,
MessagePortChannel,
scoped_refptr<SerializedScriptValue> node_options);
......@@ -96,6 +95,8 @@ class MODULES_EXPORT AudioWorkletGlobalScope final
// is no on-going processor construction, this MUST return nullptr.
ProcessorCreationParams* GetProcessorCreationParams();
void SetSampleRate(float sample_rate);
// IDL
double currentTime() const { return current_time_; }
float sampleRate() const { return sample_rate_; }
......
......@@ -48,9 +48,6 @@ namespace {
static const size_t kRenderQuantumFrames = 128;
// This is a typical sample rate.
static const float kTestingSampleRate = 44100;
} // namespace
class AudioWorkletGlobalScopeTest : public PageTestBase {
......@@ -187,7 +184,6 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
AudioWorkletProcessor* processor =
global_scope->CreateProcessor("testProcessor",
kTestingSampleRate,
dummy_port_channel,
SerializedScriptValue::NullValue());
EXPECT_TRUE(processor);
......@@ -288,7 +284,6 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
MessagePortChannel dummy_port_channel = channel->port2()->Disentangle();
AudioWorkletProcessor* processor =
global_scope->CreateProcessor("testProcessor",
kTestingSampleRate,
dummy_port_channel,
SerializedScriptValue::NullValue());
EXPECT_TRUE(processor);
......
......@@ -35,7 +35,6 @@ void AudioWorkletMessagingProxy::CreateProcessor(
CrossThreadUnretained(GetWorkerThread()),
CrossThreadUnretained(handler),
handler->Name(),
handler->Context()->sampleRate(),
std::move(message_port_channel),
std::move(node_options)));
}
......@@ -44,15 +43,13 @@ void AudioWorkletMessagingProxy::CreateProcessorOnRenderingThread(
WorkerThread* worker_thread,
AudioWorkletHandler* handler,
const String& name,
float sample_rate,
MessagePortChannel message_port_channel,
scoped_refptr<SerializedScriptValue> node_options) {
DCHECK(worker_thread->IsCurrentThread());
AudioWorkletGlobalScope* global_scope =
ToAudioWorkletGlobalScope(worker_thread->GlobalScope());
AudioWorkletProcessor* processor =
global_scope->CreateProcessor(name, sample_rate, message_port_channel,
node_options);
global_scope->CreateProcessor(name, message_port_channel, node_options);
handler->SetProcessorOnRenderThread(processor);
}
......@@ -92,7 +89,8 @@ AudioWorkletMessagingProxy::CreateObjectProxy(
ParentFrameTaskRunners* parent_frame_task_runners) {
return std::make_unique<AudioWorkletObjectProxy>(
static_cast<AudioWorkletMessagingProxy*>(messaging_proxy),
parent_frame_task_runners);
parent_frame_task_runners,
worklet_->GetBaseAudioContext()->sampleRate());
}
std::unique_ptr<WorkerThread> AudioWorkletMessagingProxy::CreateWorkerThread() {
......
......@@ -38,7 +38,6 @@ class AudioWorkletMessagingProxy final : public ThreadedWorkletMessagingProxy {
WorkerThread*,
AudioWorkletHandler*,
const String& name,
float sample_rate,
MessagePortChannel,
scoped_refptr<SerializedScriptValue> node_options);
......
......@@ -15,14 +15,17 @@ namespace blink {
AudioWorkletObjectProxy::AudioWorkletObjectProxy(
AudioWorkletMessagingProxy* messaging_proxy_weak_ptr,
ParentFrameTaskRunners* parent_frame_task_runners)
ParentFrameTaskRunners* parent_frame_task_runners,
float context_sample_rate)
: ThreadedWorkletObjectProxy(
static_cast<ThreadedWorkletMessagingProxy*>(messaging_proxy_weak_ptr),
parent_frame_task_runners) {}
parent_frame_task_runners),
context_sample_rate_(context_sample_rate) {}
void AudioWorkletObjectProxy::DidCreateWorkerGlobalScope(
WorkerOrWorkletGlobalScope* global_scope) {
global_scope_ = ToAudioWorkletGlobalScope(global_scope);
global_scope_->SetSampleRate(context_sample_rate_);
}
void AudioWorkletObjectProxy::DidEvaluateModuleScript(bool success) {
......
......@@ -16,7 +16,8 @@ class AudioWorkletObjectProxy final
: public ThreadedWorkletObjectProxy {
public:
AudioWorkletObjectProxy(AudioWorkletMessagingProxy*,
ParentFrameTaskRunners*);
ParentFrameTaskRunners*,
float context_sample_rate);
// Implements WorkerReportingProxy.
void DidCreateWorkerGlobalScope(WorkerOrWorkletGlobalScope*) override;
......@@ -28,6 +29,8 @@ class AudioWorkletObjectProxy final
GetAudioWorkletMessagingProxyWeakPtr();
CrossThreadPersistent<AudioWorkletGlobalScope> global_scope_;
float context_sample_rate_;
};
} // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment