Pcm

ブラウザでpcmオーディオを再生する方法



How Play Pcm Audio Browser



元のブログへようこそ: https://lightfish.cn/2019-01-01-browser-pcm-add-wav-header

序文

最近、私はオーディオとビデオのプログラミングの知識を整理しています。半年以上の間、特定のソースのpcmファイルをバックグラウンドで再生する必要があったことを覚えています。当時、加工方法は少しの技術で記録していました。



背景:ビジネスニーズ、Webバックグラウンドでpcmファイルを再生、ファイルは大きくありません(約300KB、既知のpcmパラメーターサンプリングレート16000、サンプリング桁数16、チャネル番号1

遊び方

pcmは比較的原始的なオーディオ形式であるため、ブラウザはpcmオーディオを直接再生できません。



PCM(Puls Code Modulation)フルネームパルスコード変調録音。PCM録音は、エンコードや圧縮処理を行わずに、サウンドのアナログ信号を0、1の識別されたデジタル信号として表現するため、PCMは非圧縮オーディオの元の形式と見なすことができます。 PCM形式のファイルにはヘッダー情報が含まれておらず、プレーヤーはサンプリングレート、チャンネル数、サンプル数、オーディオデータのサイズなどを知ることができず、再生を行うことができません。

ブラウザにpcmを認識させる方法

ブラウザは別のオーディオ形式を再生できます。WAV形式はWAVEと呼ばれます。上記のように、WAV形式のファイルを生成するには、PCMファイルの前にWAVファイルヘッダーを追加するだけで済みます。

したがって、私の解決策は、wavヘッダーをpcmに追加し、続いてブラウザーjavascriptの練習コードを追加することです。



javascriptがファイルストリームを処理する方法

Jsはファイルストリームとネットワークデータを扱っています。これは通常、ArrayBufferタイプで使用されます。 ArrayBufferタイプのAPI呼び出しメソッドの場合、事前に詳細を知る必要があります。

  • 最初のステップは、ネットワークpcmファイルのArrayBufferを非同期で取得することです。
const getWebFileArrayBuffer = async (url) => { return await fetch(url).then(response => response.arrayBuffer()) }
  • 2番目のステップは、取得したpixBufferファイルストリームArrayBufferにwavヘッダーを追加することです。
const getWebPcm2WavArrayBuffer = async (url) => { const bytes = await getWebFileArrayBuffer(url) return addWavHeader(bytes, 16000, 16, 1) // Here is the current business needs, specific parameters, sample rate 16000, sample bit number 16, channel number 1 } const addWavHeader = function (samples, sampleRateTmp, sampleBits, channelCount) { let dataLength = samples.byteLength let buffer = new ArrayBuffer(44 + dataLength) let view = new DataView(buffer) function writeString (view, offset, string) { for (let i = 0 i < string.length i++) { view.setUint8(offset + i, string.charCodeAt(i)) } } let offset = 0 /* resource exchange file identifier */ writeString(view, offset, 'RIFF') offset += 4 /* The total number of bytes from the next address to the end of the file, ie file size -8 */ view.setUint32(offset, /* 32 */ 36 + dataLength, true) offset += 4 /* WAV file mark */ writeString(view, offset, 'WAVE') offset += 4 /* Waveform format flag */ writeString(view, offset, 'fmt ') offset += 4 /* Filter bytes, typically 0x10 = 16 */ view.setUint32(offset, 16, true) offset += 4 /* Format category (sampled data in PCM format) */ view.setUint16(offset, 1, true) offset += 2 /* Number of channels */ view.setUint16(offset, channelCount, true) offset += 2 /* Sampling rate, number of samples per second, indicating the playback speed of each channel */ view.setUint32(offset, sampleRateTmp, true) offset += 4 /* Waveform data transfer rate (average number of bytes per second) Number of channels × data bits per second × data bits per sample / 8 * / view.setUint32(offset, sampleRateTmp * channelCount * (sampleBits / 8), true) offset += 4 /* Fast data adjustment number Number of bytes occupied at the time of sampling Number of channels × number of data bits per sample / 8 * / view.setUint16(offset, channelCount * (sampleBits / 8), true) offset += 2 /* Number of data per sample */ view.setUint16(offset, sampleBits, true) offset += 2 /* data identifier */ writeString(view, offset, 'data') offset += 4 /* Total number of sampled data, ie the total size of the data -44 */ view.setUint32(offset, dataLength, true) offset += 4 function floatTo32BitPCM (output, offset, input) { input = new Int32Array(input) for (let i = 0 i < input.length i++, offset += 4) { output.setInt32(offset, input[i], true) } } function floatTo16BitPCM (output, offset, input) { input = new Int16Array(input) for (let i = 0 i < input.length i++, offset += 2) { output.setInt16(offset, input[i], true) } } function floatTo8BitPCM (output, offset, input) { input = new Int8Array(input) for (let i = 0 i < input.length i++, offset++) { output.setInt8(offset, input[i], true) } } if (sampleBits == 16) { floatTo16BitPCM(view, 44, samples) } else if (sampleBits == 8) { floatTo8BitPCM(view, 44, samples) } else { floatTo32BitPCM(view, 44, samples) } return view.buffer }
  • 3番目のステップは、ブラウザでpcmによって転送されたwavのファイルストリームを再生することです。 ArrayBuffer
  • 最初にbase64形式に変換されました
const getWebPcm2WavBase64 = async (url) => { let bytes = await getWebPcm2WavArrayBuffer(url) return `data:audio/wavbase64,${btoa(new Uint8Array(bytes).reduce((data, byte) => { return data + String.fromCharCode(byte) }, ''))}` }
  • base64文字列をコンポーネントに入力します。ここにreact/ant design例として、メソッドをパッケージ化します
const playWebPcm = async (url) => { try { let pcmBase64 = await fileServer.getWebPcm2WavBase64(url) Modal.info({ title: 'Play audio', content: ( <audio controls src={pcmBase64} type='audio/wav' autoPlay /> ), onOk () {}, okText: 'shut down', }) } catch (err) { console.error(err) message.error('Failed to preload audio file') } }

参照

オーディオフォーマットの紹介とWAV、JavaバージョンへのPCM変換