OscillatorNode

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

OscillatorNode 接口表示一个周期性波形,例如正弦波。它是一个 AudioScheduledSourceNode 音频处理模块,用于创建给定波形的指定频率——实际上是一个恒定的音调。

EventTarget AudioNode AudioScheduledSourceNode OscillatorNode
输入数量 0
输出数量 1
通道计数模式 max
声道数 2 (在默认计数模式下未使用)
通道解释 扬声器

构造函数

OscillatorNode()

创建一个新的 OscillatorNode 对象实例,可以选择提供一个对象来指定节点的 属性 的默认值。或者,您可以使用 BaseAudioContext.createOscillator() 工厂方法;请参阅 创建 AudioNode

实例属性

还继承了其父类 AudioScheduledSourceNode 的属性。

OscillatorNode.frequency

一个 a-rate AudioParam,表示振动的频率(以赫兹为单位)(尽管返回的 AudioParam 是只读的,但它表示的值不是)。默认值为 440 Hz(标准的中央 A 音)。

OscillatorNode.detune

一个 a-rate AudioParam,表示振动的失谐(以音分为单位)(尽管返回的 AudioParam 是只读的,但它表示的值不是)。默认值为 0。

OscillatorNode.type

一个字符串,指定要播放的波形形状;它可以是许多标准值之一,或者 custom,用于使用 PeriodicWave 来描述自定义波形。不同的波形会产生不同的音调。标准值是 "sine""square""sawtooth""triangle""custom"。默认值为 "sine"

实例方法

还继承了其父类 AudioScheduledSourceNode 的方法。

OscillatorNode.setPeriodicWave()

设置一个 PeriodicWave,该波形描述了用于替代标准波形之一的周期性波形;调用此方法会将 type 设置为 custom

AudioScheduledSourceNode.start()

指定开始播放音调的确切时间。

AudioScheduledSourceNode.stop()

指定停止播放音调的时间。

事件

还继承了其父类 AudioScheduledSourceNode 的事件。

示例

使用 OscillatorNode

以下示例展示了如何使用 AudioContext 创建一个振荡器节点并开始播放其音调。有关实际应用示例,请查看我们的 Violent Theremin 演示相关代码请参见 app.js)。

js
// create web audio api context
const audioCtx = new AudioContext();

// create Oscillator node
const oscillator = audioCtx.createOscillator();

oscillator.type = "square";
oscillator.frequency.setValueAtTime(440, audioCtx.currentTime); // value in hertz
oscillator.connect(audioCtx.destination);
oscillator.start();

不同的振荡器节点类型

四个内置的振荡器 类型sinesquaretrianglesawtooth。它们是振荡器生成的波形的形状。有趣的事实:这些是大多数合成器的默认设置,因为它们是易于电子生成的波形。此示例可视化了不同类型在不同频率下的波形。

html
<div class="controls">
  <label for="type-select">
    Oscillator type
    <select id="type-select">
      <option>sine</option>
      <option>square</option>
      <option>triangle</option>
      <option>sawtooth</option>
    </select>
  </label>

  <label for="freq-range">
    Frequency
    <input
      type="range"
      min="100"
      max="800"
      step="10"
      value="250"
      id="freq-range" />
  </label>
  <button data-playing="init" id="play-button">Play</button>
</div>

<canvas id="wave-graph"></canvas>

代码分为两部分:第一部分,我们设置了声音相关的部分。

js
const typeSelect = document.getElementById("type-select");
const frequencyControl = document.getElementById("freq-range");
const playButton = document.getElementById("play-button");

const audioCtx = new AudioContext();
const osc = new OscillatorNode(audioCtx, {
  type: typeSelect.value,
  frequency: frequencyControl.valueAsNumber,
});
// Rather than creating a new oscillator for every start and stop
// which you would do in an audio application, we are just going
// to mute/un-mute for demo purposes - this means we need a gain node
const gain = new GainNode(audioCtx);
const analyser = new AnalyserNode(audioCtx, {
  fftSize: 1024,
  smoothingTimeConstant: 0.8,
});
osc.connect(gain).connect(analyser).connect(audioCtx.destination);

typeSelect.addEventListener("change", () => {
  osc.type = typeSelect.value;
});

frequencyControl.addEventListener("input", () => {
  osc.frequency.value = frequencyControl.valueAsNumber;
});

playButton.addEventListener("click", () => {
  if (audioCtx.state === "suspended") {
    audioCtx.resume();
  }

  if (playButton.dataset.playing === "init") {
    osc.start(audioCtx.currentTime);
    playButton.dataset.playing = "true";
    playButton.innerText = "Pause";
  } else if (playButton.dataset.playing === "false") {
    gain.gain.linearRampToValueAtTime(1, audioCtx.currentTime + 0.2);
    playButton.dataset.playing = "true";
    playButton.innerText = "Pause";
  } else if (playButton.dataset.playing === "true") {
    gain.gain.linearRampToValueAtTime(0.0001, audioCtx.currentTime + 0.2);
    playButton.dataset.playing = "false";
    playButton.innerText = "Play";
  }
});

至于第二部分,我们使用上面创建的 AnalyserNode 将波形绘制在画布上。

js
const dpr = window.devicePixelRatio;
const w = 500 * dpr;
const h = 300 * dpr;
const canvasEl = document.getElementById("wave-graph");
canvasEl.width = w;
canvasEl.height = h;
const canvasCtx = canvasEl.getContext("2d");

const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);

// draw an oscilloscope of the current oscillator
function draw() {
  analyser.getByteTimeDomainData(dataArray);

  canvasCtx.fillStyle = "white";
  canvasCtx.fillRect(0, 0, w, h);

  canvasCtx.lineWidth = 4.0;
  canvasCtx.strokeStyle = "black";
  canvasCtx.beginPath();

  const sliceWidth = (w * 1.0) / bufferLength;
  let x = 0;

  for (let i = 0; i < bufferLength; i++) {
    const v = dataArray[i] / 128.0;
    const y = (v * h) / 2;
    if (i === 0) {
      canvasCtx.moveTo(x, y);
    } else {
      canvasCtx.lineTo(x, y);
    }
    x += sliceWidth;
  }

  canvasCtx.lineTo(w, h / 2);
  canvasCtx.stroke();

  requestAnimationFrame(draw);
}

draw();

警告:此示例会发出噪音!

规范

规范
Web Audio API
# OscillatorNode

浏览器兼容性

另见