gpt4 book ai didi

javascript - 从 url 中提取音频片段并使用纯 Web Audio API 播放

转载 作者:行者123 更新时间:2023-11-30 14:22:29 25 4
gpt4 key购买 nike

在以下网址上:

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

我想在以下范围上使用纯Web Audio API播放一个音频:

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

我将该文件下载到我的 table ​​面(我使用的是 Windows 10),然后使用 VLC 打开它并获得以下文件信息:

enter image description here

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

在这里你有关于这个概念的信息:

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

从那里我得到了以下摘录:

enter image description here

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

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

通过从服务器下载支持请求 header 的片段:Range

然后我尝试了下面的代码:

...
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);
...

我的问题是:

  1. 我需要为变量找到正确的值:range_from,以便它从秒开始播放:306.6

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

这里是我目前的代码:

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>

这里有对应的CodePen.io:

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

能否请您为 range_from 提供正确的值并将其用于 CodePen.io 上的 fork 代码?

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

[编辑 1]

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

在我做的一个快速实验中,使用 { Windows 10, Android, iPhone } x { Native browser, Chrome, Firefox } 的组合,上面右边的代码只适用于:{ (Windows 10,Chrome),(Android,Chrome),(Android, native 浏览器)

遗憾的是它不起作用:

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

有没有办法可以向浏览器开发者提出请求,让他们关注这个问题?

Google ChromeWindows 10Android 上表现非常出色。

如果其他浏览器也这样做会很有趣。

谢谢!

最佳答案

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

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

因此,您的 fetch() 现在应该可以工作了(我也更改了范围长度):

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();
});
}

编辑我添加了基本的帧头搜索,我的天哪,连老狐狸都吃掉了。对于稳定的解决方案,您必须解析文件头以获取元数据,并将其与帧头数据进行比较。并在找不到 header 时执行某些操作并且... ...

关于javascript - 从 url 中提取音频片段并使用纯 Web Audio API 播放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52513425/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com