这是一个问题:
我所做的是:
var samples = []; // each element of this array stores a chunk of 512 samples
var audioCtx = new AudioContext();
var source = audioCtx.createBufferSource();
source.buffer = audioCtx.createBuffer(1, 512, 88200);
// bufferSize is 512 because it is the size of chunks
var scriptNode = audioCtx.createScriptProcessor(512, 1, 1);
scriptNode.onaudioprocess = function(audioProcessingEvent) {
// play a chunk if there is at least one.
if (samples.length > 0) {
audioProcessingEvent.outputBuffer.copyToChannel(samples.shift(), 0, 0);
}
};
source.connect(scriptNode);
scriptNode.connect(audioCtx.destination);
source.start();
peerConnection.addEventListener("datachannel", function(e) {
e.channel.onmessage = function(m) {
var values = new Float32Array(m.data);
samples.push(values);
};
);
有几个问题:
audioProcessingEvent.outputBuffer.sampleRate
一直都是48000
。显然,它是不依赖于的比特率的source
,我找不到将其设置为的方法88200
,44100
也找不到任何其他值。声音呈现的延迟会不断增长。ScriptProcessorNode
不推荐使用。预先感谢您的任何建议!
您需要一个AudioBuffer。
您可以直接从TypedArray将原始PCM数据复制到其通道中。
您可以指定其sampleRate
,AudioContext将负责重新采样以匹配声卡的设置。
但是请注意,每个块2048个字节意味着@ 88Khz,每个块将仅表示5毫秒的音频数据:我们传递了一个Float32Array,因此byteSize为4,并且2048/4/88200 =±0.0058s。
您可能会希望增加它,并实施一些缓冲策略。
这是一个小演示,作为概念证明,将大块的数据存储到缓冲区Float32Array中。
const min_sample_duration = 2; // sec
const sample_rate = 88200; // Hz
// how much data is needed to play for at least min_sample_duration
const min_sample_size = min_sample_duration * sample_rate;
const fetching_interval = 100; // ms
// you'll probably want this much bigger
let chunk_size = 2048; // bytes
const log = document.getElementById( 'log' );
const btn = document.getElementById( 'btn' );
btn.onclick = e => {
let stopped = false;
let is_reading = false;
const ctx = new AudioContext();
// to control output volume
const gain = ctx.createGain();
gain.gain.value = 0.01;
gain.connect( ctx.destination );
// this will get updated at every new fetch
let fetched_data = new Float32Array( 0 );
// keep it accessible so we can stop() it
let active_node;
// let's begin
periodicFetch();
// UI update
btn.textContent = "stop";
btn.onclick = e => {
stopped = true;
if( active_node ) { active_node.stop(0); }
};
oninput = handleUIEvents;
// our fake fetcher, calls itself every 50ms
function periodicFetch() {
// data from server (here just some noise)
const noise = Float32Array.from( { length: chunk_size / 4 }, _ => (Math.random() * 1) - 0.5 );
// we concatenate the data just fetched with what we have already buffered
fetched_data = concatFloat32Arrays( fetched_data, noise );
// for demo only
log.textContent = "buffering: " + fetched_data.length + '/ ' + min_sample_size;
if( !stopped ) {
// do it again
setTimeout( periodicFetch , fetching_interval );
}
// if we are not actively reading and have fetched enough
if( !is_reading && fetched_data.length > min_sample_size ) {
readingLoop(); // start reading
}
}
function readingLoop() {
if( stopped || fetched_data.length < min_sample_size ) {
is_reading = false;
return;
}
// let the world know we are actively reading
is_reading = true;
// create a new AudioBuffer
const aud_buf = ctx.createBuffer( 1, fetched_data.length, sample_rate );
// copy our fetched data to its first channel
aud_buf.copyToChannel( fetched_data, 0 );
// clear the buffered data
fetched_data = new Float32Array( 0 );
// the actual player
active_node = ctx.createBufferSource();
active_node.buffer = aud_buf;
active_node.onended = readingLoop; // in case we buffered enough while playing
active_node.connect( gain );
active_node.start( 0 );
}
function handleUIEvents( evt ) {
const type = evt.target.name;
const value = evt.target.value;
switch( type ) {
case "chunk-size":
chunk_size = +value;
break;
case "volume":
gain.gain.value = +value;
break;
}
}
};
// helpers
function concatFloat32Arrays( arr1, arr2 ) {
if( !arr1 || !arr1.length ) {
return arr2 && arr2.slice();
}
if( !arr2 || !arr2.length ) {
return arr1 && arr1.slice();
}
const out = new Float32Array( arr1.length + arr2.length );
out.set( arr1 );
out.set( arr2, arr1.length );
return out;
}
label { display: block }
<button id="btn">start</button>
<pre id="log"></pre>
<div>
<label>Output volume:<input type="range" name="volume" min="0" max="0.5" step="0.01" value="0.01"></label>
</div>
<div>
Size of each chunk fetched:
<label><input type="radio" name="chunk-size" value="2048" checked>2048 bytes (OP's current)</label>
<label><input type="radio" name="chunk-size" value="35280">35280 bytes (barely enough for 0.1s interval)</label>
<label><input type="radio" name="chunk-size" value="44100">44100 bytes (enough for 0.1s interval)</label>
</div>
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句