LoginSignup
21
12

More than 3 years have passed since last update.

フロントエンドで音声データを扱うときの基礎知識

Posted at

最近音声データを触ることが多かったので、基本的な用語の説明だけしていく。

sampleRate

単位 : Hz
1秒間のデータを分割する数。
6000Hzだと、0.01秒ごとに分割。
この分割した1つ1つのデータをサンプリングと呼ぶ。
CDは44.1KHz。ChromeのStreamingも44.1KHz。

bitDepth

単位 : bit
デフォルト : 16bit
サンプリングに対して与える情報量。
何bit与えるかを示す。

channels

音をどの程度分けるか。多いほど立体感のある音が作れる。
1 なら モノラル, 2 なら ステレオ。
基本的には、音声なら 1, 音楽なら 2 以上。

sampleRateを修正するJavaScript

48KHzを16KHz(LINEAR16)で保存するには、 downSampleBuffer(buff, 48000, 16000) で呼び出す。

export const downsampleBuffer = (buffer, sampleRate, outSampleRate) => {
  if (outSampleRate > sampleRate) {
    console.error('downsampling rate show be smaller than original sample rate');
  }

  const sampleRateRatio = sampleRate / outSampleRate;
  const newLength = Math.round(buffer.length / sampleRateRatio);
  const result = new Int16Array(newLength);
  let offsetResult = 0;
  let offsetBuffer = 0;
  // bpsを縮める処理 (n byte分のデータを合算して、n byteで割る)
  while (offsetResult < result.length) {
    const nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);

    // 次のoffsetまでの合計を保存
    let accum = 0;
    let count = 0;
    for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i += 1) {
      accum += buffer[i];
      count += 1;
    }

    // 16進数で保存 (LINEAR16でストリーミングするため)
    result[offsetResult] = Math.min(1, accum / count) * 0x7FFF;
    offsetResult += 1;
    offsetBuffer = nextOffsetBuffer;
  }
  return result.buffer;
};

PCM (pulse code modulation)

圧縮していない生データのこと。
拡張子 raw はPCMである。
rawデータに sampleRate, bitDepth, channelsなどのメタ情報をヘッダに加えたものがwavファイル。 と思っていたが、wavファイルはrawデータ以外も使えるらしい。

WAVファイルのヘッダ情報

image.png

http://soundfile.sapp.org/doc/WaveFormat/

Int16Array (16bitのデータをwavに変換するJavaScript)

export const createWavHeader = (view, config) => {
  const {
    sampleRate, bitDepth, channel, dataLength,
  } = config;
  writeUTFBytes(view, 0, 'RIFF');

  // file length
  view.setUint32(4, dataLength + 32, true);
  // set file size at the end
  writeUTFBytes(view, 8, 'WAVE');
  // FMT sub-chunk
  writeUTFBytes(view, 12, 'fmt ');
  // chunk size
  view.setUint32(16, 16, true);
  // format code
  view.setUint16(20, 1, true);
  // channels : (current use monoral : 1)
  view.setUint16(22, channel, true);
  // sampling rate
  view.setUint32(24, sampleRate, true);
  // data rate
  view.setUint32(28, sampleRate * 2 * channel, true);
  // data block size
  view.setUint16(32, channel * 2, true);
  // bits per sample (bitDepth)
  view.setUint16(34, bitDepth, true);
  // data sub-chunk
  writeUTFBytes(view, 36, 'data');
  // DUMMY data chunk length (set real value on export)
  view.setUint32(40, dataLength, true);
};

export const exportWAV = (
  channel, buffers /* Int16Array */, wavConf = { sampleRate: AUDIO_SAMPLE_RATE, bitDepth: AUDIO_BIT_DEPTH }
) => {
  const dataLength = buffers.length * channel * 2;
  const buffer = new ArrayBuffer(44 + dataLength);
  const view = new DataView(buffer);

  // copy WAV header data into the array buffer
  createWavHeader(view, { ...wavConf, channel, dataLength });

  // write audio data
  writeFromInputBuffers(view, channel, 44, buffers);
  return new Blob([view], { type: 'audio/wav' });
};

音声認識系でよく利用される形で録音

$ brew install sox

音声認識サービスの自動認証は、wavファイルのヘッダ形式を見て、データが違うとエラーになる。

  • LINEAR16(16bit)
  • 16kHz
  • モノラル
rec --rate 16k --bits 16 --channels 1 test.wav

30秒だけ録音

rec --rate 16k --bits 16 --channels 3 test.wav trim 0 30

ハイレゾ( High-Resolution Audio)

ビットレートがめちゃくちゃ高いぜということ。

凄さ sampleRate(KHz) bitDepth(bit) channels bitRate(kbps)
通常 44.1 16 1 705.6
通常 44.1 16 1 705.6
ハイレゾ1 96 24 2 4608
ハイレゾ2 192 24 2 9216
21
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
12