从URL中提取音频片段并使用纯Web Audio API播放 [英] Extracting fragment of audio from a url and play it with pure Web Audio API

查看:160
本文介绍了从URL中提取音频片段并使用纯Web Audio API播放的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下网址上:



 频道数量:2 
采样率:44100 Hz每个样本b $ b位:32(float32)

这里有关于此的概念的信息:





我想播放上面评论的范围(也在这里粘贴):

 范围从:秒:306.6 
范围:第二:311.8
总计:5.2秒

从服务器下载该片段,该片段支持请求标头:范围



然后我尝试了以下代码:

  ... 
let num_channels = 2;
let sample_rate = 44100;
让range_from = 0; //目标:306.6秒
让range_length =(sample_rate / num_channels)* 5.2; //目标:5.2秒
让range_to = range_from +(range_length - 1); //range_to包含(确认)
request.setRequestHeader(Range,bytes =+ range_from + - + range_to);
...

我的问题是:


  1. 我需要为变量找到正确的值: range_from 所以它开始播放从第二个: 306.6


  2. 我想知道上面指定的值是否为: range_length 是否正确,因为可能有用于标题等的字节,我的意思是: headers + 数据


这里有你到目前为止的代码:



  window.AudioContext = window.AudioContext || window.webkitAudioContext; // iPhone(可能是其他人)必需的。可以改变近期.const URL ='https://www.tophtml.com/snl/15.mp3';const context = new AudioContext(); window.addEventListener('load',function(){const button_option_1 = document.querySelector('。button_option_1'); const button_option_1_play = document.querySelector('。button_option_1_play'); button_option_1_play.disabled = true; button_option_1.addEventListener('click',async function(){let time_start,duration; let buffer; log('...',false); button_option_1_play.disabled = true; button_option_1_play.onclick =()=> playBuffer(buffer); // --- time_start = new Date()。getTime(); let arrayBuffer =等待获取(URL); //下载完整持续时间= sprintf('%。2fs',(新日期()。getTime() -  time_start)/ 1000); log(sprintf('P2。延迟:+%s下载等待...',持续时间)); // --- time_start = new Date()。getTime();让audioBuffer = await decodeAudioData(context,arrayBuffer); //解码完整的持续时间= sprintf('%。2fs ',(ne w Date()。getTime() -  time_start)/ 1000); log(sprintf('P3。延迟:+%s用于解码。',持续时间)); // --- button_option_1_play.disabled = false; buffer = audioBuffer; button_option_1_play.click(); });}); function playBuffer(buffer,from,duration){const source = context.createBufferSource(); //source的类型:AudioBufferSourceNodesource.buffer = buffer; source.connect(context.destination); source.start(context.currentTime,from,duration);} function log(text,append = true){let log = document.querySelector('。log'); if(!append)log.innerHTML =''; let entry = document.createElement('div'); entry.innerHTML = text; log.appendChild(entry);} function decodeAudioData(context,arrayBuffer){return new Promise(async(resolve,reject)=> {if(false){} else if(context.decodeAudioData.length == 1){/ / console.log('decodeAudioData / Way 1');让audioBuffer = await context.decodeAudioData(arrayBuffer); resolve(audioBuffer);} else if(context.decodeAudioData.length == 2){// iPhone必备(Safari) ,Chrome)和Mac(Safari)。可以改变不久的将来.// console.log('decodeAudioData / Way 2'); context.decodeAudioData(arrayBuffer,function onSuccess(audioBuffer){resolve(audioBuffer);});}函数提取(url){return new Promise((resolve,reject)=> {var request = new XMLHttpRequest(); request.open('GET',url,true); request.responseType ='arraybuffer ';让num_channels = 2;让sample_rate = 44100;让range_from = 0; //目标:306.6秒让range_length =(sample_rate / num_channels)* 5.2; //目标:5.2秒让我们跑ge_to = range_from +(range_length  -  1); //range_to包含(确认)request.setRequestHeader(Range,bytes =+ range_from + - + range_to); request.onload = function(){let arrayBuffer = request.response; let byteArray = new Uint8Array(arrayBuffer); // console.log(Array.from(byteArray)); //只记录信息解析(arrayBuffer); } request.send(); }};}  

  .log {display:inline-block ; font-family:Courier New,Courier,monospace; font-size:13px; margin-top:10px;填充:4px; background-color:#d4e4ff;} .divider {border-top:1px solid #ccc;保证金:10px 0;}  

 < script src = https://cdnjs.cloudflare.com/ajax/libs/sprintf/1.1.1/sprintf.min.js\"></script><button class =button_option_1>选项1< / button>< ; button class =button_option_1_play>播放< / button>< br />< div class =log> [empty]< / div>  



这里有相应的 CodePen.io



https://codepen.io/anon/pen/ RYXKmP



可以请您提供正确的价值: range_from 并在分叉上使用它代码 CodePen.io



相关问题: https://engineering.stackexchange.com/questions/23929





这是一个更简单的 Cod ePen.io https://codepen.io/anon/pen/YJKVde ,专注于检查浏览器在给定随机位置的情况下移动到下一个有效帧的能力。



在快速实验中我做了,使用 {Windows 10,Android,iPhone} x {原生浏览器,Chrome,Firefox} 的组合,上面的代码仅适用于: { (Windows 10,Chrome),(Android,Chrome),(Android,原生浏览器)}



遗憾的是它没有工作:

  {(iPhone,Safari),(iPhone,Chrome),(Windows 10,Firefox),(Android, Firefox)} 

有没有办法我们可以向浏览器开发者提交请求以注意这一点在 Windows 10 Android



有趣的是其他浏览器都会这样做同样。



谢谢!

解决方案

帧长(秒)=帧样本/采样率为38.28帧/秒。



Fram长度(字节)= 144 *比特率/采样率



所以,你的fetch()现在应该可以工作(我也改变了范围长度):

  function fetch(url) {
返回新的承诺((resolve,reject)=> {
var request = new XMLHttpRequest();
request.open('GET',url,true);
request.responseType ='arraybuffer';
让num_channels = 2;
让bitrate = 192000;
let sample_rate = 44100;
let byte_per_sec = 144 *(bitrate / sample_rate)* 38.28;
let range_from = Math.floor(byte_per_sec * 306.6);
let range_length = Math.floor(byte_per_sec * 5.2);
let range_to = range_from +(range_length - 1);
request.setRequestHeader(Range,bytes =+ range_from + - + range_to);
request.onload = function(){
let arrayBuffer = request.response;
let byteArray = new Uint8Array(arrayBuffer);
// ******************
for(let i = 0; i< byteArray.length; i + = 1){
if((byteArray [i] === 0b11111111)&&(byteArray [i + 1]& 0b11110000)=== 0b11110000){
log('我们有一个赢家!帧头在:'+ i,true);
console.log((parseInt(byteArray [i],10))。toString(2)); // frame header 4 bytes
console.log((parseInt(byteArray [i + 1],10))。toString(2));
console.log((parseInt(byteArray [i + 2],10))。toString(2));
console.log((parseInt(byteArray [i + 3],10))。toString(2));
resolve(arrayBuffer.slice(i));
休息;
}
}
// ******************
}
request.send();
});
}

编辑
我添加了基本框架标题搜索,my'o'my,甚至老狐狸都吃。
对于稳定的解决方案,您必须解析文件头以获取元数据,以将其与帧头数据进行比较。当找不到标题时做一些事情......


On the following url:

https://www.tophtml.com/snl/15.mp3

there is one audio I want to play using pure Web Audio API on the following range:

range from: second: 306.6
  range to: second: 311.8
     total: 5.2 seconds

I downloaded that file to my desktop (I'm using Windows 10), then opened it with VLC and got the following file info:

number of channels: 2
       sample rate: 44100 Hz
   bits per sample: 32 (float32)

Here you have info about concepts on this:

https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Basic_concepts_behind_Web_Audio_API#Audio_buffers_frames_samples_and_channels

from where I got the following excerpt:

I want to play the range commented above (also pasting it here):

range from: second: 306.6
  range to: second: 311.8
     total: 5.2 seconds

by downloading just that fragment from the server, which supports the request header: Range.

Then I tried the following code:

...
let num_channels    = 2;
let sample_rate     = 44100;
let range_from      = 0;                                    // Goal: 306.6 seconds
let range_length    = (sample_rate / num_channels) * 5.2;   // Goal:   5.2 seconds
let range_to        = range_from + (range_length - 1);      // "range_to" is inclusive (confirmed)
request.setRequestHeader("Range", "bytes=" + range_from + "-" + range_to);
...

My questions are:

  1. I need to find the right value for variable: range_from so it starts playing from second: 306.6.

  2. I want to know if the value specified above for: range_length is correct or not since probably there are bytes used for headers, etc., I mean: headers + data.

Here you have the code I have so far:

window.AudioContext = window.AudioContext || window.webkitAudioContext; // necessary for iPhone (maybe others). Could change a near future.

const URL = 'https://www.tophtml.com/snl/15.mp3';
const context = new AudioContext();

window.addEventListener('load', function() {

	const button_option_1			= document.querySelector('.button_option_1');
	const button_option_1_play		= document.querySelector('.button_option_1_play');
	button_option_1_play.disabled	= true;

	button_option_1.addEventListener('click', async function() {
		let time_start, duration;
		let buffer;
		log('...', false);
		button_option_1_play.disabled = true;
		button_option_1_play.onclick = () => playBuffer(buffer);
		//---
		time_start = new Date().getTime();
		let arrayBuffer = await fetch(URL);
		// download complete
		duration = sprintf('%.2fs', (new Date().getTime()-time_start)/1000);
		log(sprintf('P2. Delay: +%s for download. Wait...', duration));
		//---
		time_start = new Date().getTime();		
		let audioBuffer = await decodeAudioData(context, arrayBuffer);
		// decoding complete
		duration = sprintf('%.2fs', (new Date().getTime()-time_start)/1000);
		log(sprintf('P3. Delay: +%s for decoding.', duration));
		//---
		button_option_1_play.disabled = false;
		buffer = audioBuffer;
		button_option_1_play.click();
	});

});
function playBuffer(buffer, from, duration) {
	const source = context.createBufferSource(); // type of "source": "AudioBufferSourceNode"
	source.buffer = buffer;
	source.connect(context.destination);
	source.start(context.currentTime, from, duration);
}
function log(text, append = true) {
	let log = document.querySelector('.log');
	if (!append)
		log.innerHTML = '';
	let entry = document.createElement('div');
	entry.innerHTML = text;
	log.appendChild(entry);
}
function decodeAudioData(context, arrayBuffer) {
	return new Promise(async (resolve, reject) => {
		if (false) {}
		else if (context.decodeAudioData.length == 1) {
			// console.log('decodeAudioData / Way 1');
			let audioBuffer = await context.decodeAudioData(arrayBuffer);
			resolve(audioBuffer);
		}
		else if (context.decodeAudioData.length == 2) {
			// necessary for iPhone (Safari, Chrome) and Mac (Safari). Could change a near future.
			// console.log('decodeAudioData / Way 2');
			context.decodeAudioData(arrayBuffer, function onSuccess(audioBuffer) {
				resolve(audioBuffer);
			});
		}
	});
}
function fetch(url) {
	return new Promise((resolve, reject) => {
		var request = new XMLHttpRequest();
		request.open('GET', url, true);
		request.responseType = 'arraybuffer';
		let num_channels	= 2;
		let sample_rate		= 44100;
		let range_from		= 0;									// Goal: 306.6 seconds
		let range_length	= (sample_rate / num_channels) * 5.2;	// Goal:   5.2 seconds
		let range_to		= range_from + (range_length - 1);		// "range_to" is inclusive (confirmed)
		request.setRequestHeader("Range", "bytes=" + range_from + "-" + range_to);
		request.onload = function() {
			let arrayBuffer = request.response;
			let byteArray = new Uint8Array(arrayBuffer);
			// console.log(Array.from(byteArray)); // just logging info
			resolve(arrayBuffer);
		}
		request.send();
	});
}

.log {
	display: inline-block;
	font-family: "Courier New", Courier, monospace;
	font-size: 13px;
	margin-top: 10px;
	padding: 4px;	
	background-color: #d4e4ff;
}
.divider {
	border-top: 1px solid #ccc;
	margin: 10px 0;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/sprintf/1.1.1/sprintf.min.js"></script>

<button class="button_option_1">Option 1</button>
<button class="button_option_1_play">Play</button><br />
<div class="log">[empty]</div>

Here you have the corresponding CodePen.io:

https://codepen.io/anon/pen/RYXKmP

Could you please, provide the right value for: range_from and use it on a forked code on CodePen.io?

Related question: https://engineering.stackexchange.com/questions/23929

[EDIT 1]

Here is a simpler CodePen.io: https://codepen.io/anon/pen/YJKVde, which is focused on check the ability of the browser to move, given a random position, to the next valid frame.

On a quick experiment I did, using combinations of { Windows 10, Android, iPhone } x { Native browser, Chrome, Firefox }, the right above code only works on: { (Windows 10, Chrome), (Android, Chrome), (Android, Native browser) }.

It's a pity it doesn't work on:

{ (iPhone, Safari), (iPhone, Chrome), (Windows 10, Firefox), (Android, Firefox) }

Is there a way we can submit a request to the browser developers to pay attention to this?

Google Chrome is doing really well on Windows 10 and Android.

It would be interesting that the rest of the browsers do the same.

Thanks!

解决方案

Frame length (sec) = frame samples / sample rate which makes 38.28 frames/sec.

Fram length (byte) = 144*bitrate/sample rate

So, your fetch() should work now (I changed range length too):

function fetch(url) {
  return new Promise((resolve, reject) => {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    let num_channels    = 2;
    let bitrate         = 192000;
    let sample_rate     = 44100;
    let byte_per_sec    = 144 * (bitrate/sample_rate) * 38.28;
    let range_from      = Math.floor(byte_per_sec * 306.6);
    let range_length    = Math.floor(byte_per_sec * 5.2);
    let range_to        = range_from + (range_length - 1);
    request.setRequestHeader("Range", "bytes=" + range_from + "-" + range_to);
    request.onload = function() {
        let arrayBuffer = request.response;
        let byteArray = new Uint8Array(arrayBuffer);
        //******************
            for ( let i = 0; i < byteArray.length; i += 1 ) {
                if (( byteArray[i] === 0b11111111 ) && ( byteArray[ i + 1 ] & 0b11110000 ) === 0b11110000 ){
                    log('we have a winner! Frame header at:'+i, true);
                    console.log((parseInt(byteArray[i], 10)).toString(2)); //frame header 4 bytes
                    console.log((parseInt(byteArray[i+1], 10)).toString(2));
                    console.log((parseInt(byteArray[i+2], 10)).toString(2));
                    console.log((parseInt(byteArray[i+3], 10)).toString(2));
                    resolve(arrayBuffer.slice(i));
                    break;
                }
            }
        //******************
    }
    request.send();
  });
}

EDIT I added basic frame header search and, my'o'my, even old fox eats that. For a stabile solution you'll have to parse file header to get metadata, to compare that against frame header data. And do something when header is not found and... ...

这篇关于从URL中提取音频片段并使用纯Web Audio API播放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆