Web Audio APIでOfflineAudioContextを作るときの長さの指定、どうしよう

OfflineAudioContextのコンストラク

Web Audio APIのOfflineAudioContextを作るとき、
コンストラクタで長さを指定しますよね。length。

  • これが十分な長さより短いと作成した音声が短くなってしまい、
  • 長すぎると最後の方に音が出力されないゾーンが出来てしまう。
const offlineCtx = new OfflineAudioContext(numberOfChannels, length, sampleRate);


けれど、いつもいつも事前に適切な長さが分かることばかりではない。
特に、Web Audio APIで音声を加工するのであれば。
例えば、playbackRateを低くしたりすると、音声が長くなってしまいますよね。


(加工後の音声の秒数でも分かれば計算で出せるけど、
音声を加工するにはOfflineAudioContextを作る必要があって、
音声を加工する前にlengthの指定が必要なんですね)

どうしよう

これで良いのか分からないけど、
自分の対応はこうしてみた。

  • でっかく作って (短いと加工した音声が入らなくなるのだから当然だ)
  • 余分な部分を切り詰めよう

あ、これ、今更だけど、オフラインに音声ファイルを出力するケースを想定した考えかもしれない。

長いOfflineAudioContextを作る

これは簡単ですね。
lengthの数値を大きくすれば良い。

const offlineCtx = new OfflineAudioContext(numberOfChannels, length, sampleRate);

切り詰める

音声が入っている場所までの長さを調べる。

ああ、そうだ。このコードは最終的にWAVファイルの出力を想定しているので、
データのサイズが偶数である必要があって、長さを計測する際、少し調整が入っているよ。

function correctFrameCount(audioBuffer: AudioBuffer): number {
  let max = 0;
  for (let i = 0; i < audioBuffer.numberOfChannels; i++) {
    const buffer: Float32Array = audioBuffer.getChannelData(i);
    const count = correctBufferLength(buffer);
    if (max < count) {
      max = count;
    }
  }
  return max;
}
function correctBufferLength(buffer: Float32Array): number {
  let pos = 0;
  for (let i = buffer.length - 1; i >= 0; i--) {
    if (buffer[i] !== 0x00) {
      pos = i;
      break;
    }
  }
  if (pos % 2 != 0) {
    pos += 1;
  }
  return pos;
}

そして、測った長さにAudioBufferを切り詰める。

function buildCorrectAudioBuffer(audioBuffer: AudioBuffer): any {
  const frameCount = correctFrameCount(audioBuffer);
  const nAudioBuffer = new AudioBuffer({
      numberOfChannels: audioBuffer.numberOfChannels,
      length: frameCount,
      sampleRate: audioBuffer.sampleRate,
  });

  for (let i = 0; i < audioBuffer.numberOfChannels; i++) {
    const buffer = audioBuffer.getChannelData(i);
    const trimmed = buffer.slice(0, frameCount);
    nAudioBuffer.copyToChannel(trimmed, i, 0);
  }
  return nAudioBuffer;
}
const newAudioBuf = buildCorrectAudioBuffer(audioBuf);

おわり

おわり。