1

場景:使用Javascript - 尋求在準確的位置時聲音一定位置音軌

  1. 音頻開始從0:00播放。在0:05處,軌跡跳轉到0:30
  2. 軌道立即開始在0:30玩耍,正好0:35,軌道向後跳轉到0:05和播放音頻文件

摘要的其餘部分: 播放:0:00至0.05跳過:0:05至0:30播放:0:30至0:35跳過:0:35至0:05播放:0.05到END

真正的問題出現在需要立即無縫跳過時。例如,setTimeoutvery inaccurate並且意味着在這種情況下不能使用漂移。

我試圖使用Web Audio API來實現這一點,但我仍然無法立即轉換。我目前正在使用AudioBufferSourceNode.start(when, offset, duration);安排加載歌曲的片段,最初以上述示例的(0 [when], 0 [offset], 0:05 [duration])傳遞。在此之後,我仍然使用setTimeout調用相同的功能,運行時間短至0:30並安排了類似(0 [when] + 0:05 [previous duration], 0:30 [offset], 0:05 [duration])的計劃。這

var AudioContext = window.AudioContext || window.webkitAudioContext, 
 
    audioCtx  = new AudioContext(); 
 
    
 
var skipQueue = [0, 5, 30, 35, 5]; 
 
    
 
// getSong(); // load in the preview song (https://audiojungle.net/item/inspiring/9325839?s_rank=1) 
 

 
playSound(0, 0); 
 

 
function getSong() { 
 
    console.log("Loading song..."); 
 

 
    request = new XMLHttpRequest(); 
 
    // request.open('GET', "https://preview.s3.envato.com/files/230851789/preview.mp3?response-content-disposition=attachment%3Bfilename%3D20382637_uplifting-cinematic-epic_by_jwaldenmusic_preview.mp3&Expires=1501966849&Signature=HUMfPw3b4ap13cyrc0ZrNNumb0s4AXr7eKHezyIR-rU845u65oQpxjmZDl8AUZU7cR1KuQGV4TLkQ~egPt5hCiw7SUBRApXw3nnrRdtf~M6PXbNqVYhrhfNq4Y~MgvZdd1NEetv2rCjhirLw4OIhkiC2xH2jvbN6mggnhNnw8ZemBzDH3stCVDTEPGuRgUJrwLwsgBHmy5D2Ef0It~oN8LGG~O~LFB5MGHHmRSjejhjnrfSngWNF9SPI3qn7hOE6WDvcEbNe2vBm5TvEx2OTSlYQc1472rrkGDcxzOHGu9jLEizL-sSiV61uVAp5wqKxd2xNBcsUn3EXXnjMIAmUIQ__&Key-Pair-Id=APKAIEEC7ZU2JC6FKENA", true); // free preview from envato 
 
    request.responseType = "arraybuffer"; 
 

 
    request.onload = function() { 
 
     var audioData = request.response; 
 

 
     audioCtx.decodeAudioData(audioData, function(buffer) { 
 
      audioBuffer = buffer; 
 

 
      console.log("Ready to play!"); 
 
      
 
      playSong() 
 
     }, function(e) { 
 
      "Error with decoding audio data" + e.err 
 
     }); 
 
    } 
 

 
    request.send(); 
 
} 
 

 

 
function playSound(previousPlay, nextPlay) { 
 
    // source  = audioCtx.createBufferSource(); 
 
    // source.buffer = audioBuffer; 
 
    // source.connect(audioCtx.destination); 
 
    
 
    skipQueue.shift(); 
 

 
    var duration = Math.abs(skipQueue[0] - previousPlay); 
 

 
    // source.start(nextPlay, previousPlay, duration); 
 
    
 
    console.log("Running: source.start(" + nextPlay + ", " + previousPlay + ", " + duration + ")"); 
 
    console.log("Current Time: " + previousPlay); 
 
    console.log("Next Play in: " + duration + " (Skipping from " + skipQueue[0] + " to " + skipQueue[1] + ")"); 
 

 
    if (skipQueue.length > 1) { 
 
     setTimeout(function() { 
 
      playSound(skipQueue[0], nextPlay + duration); 
 
     }, 1000 * duration - 50); // take 50ms off for drift that'll be corrected in scheduling 
 
    } 
 
}
<strong>Expected:</strong><br /> 
 
Play: 0:00 to 0.05, Skip: 0:05 to 0:30, Play: 0:30 to 0:35, Skip: 0:35 to 0:05, Play: 0.05 to END

的例子我能不能得到這個簡化的例子下降100%的工作,但你可以看到我的嘗試是在控制檯中的內容。由於StackOverflow不支持AJAX,我還評論了代碼。

我打算使用任何其他的API或方法,你有想法!

+0

工作小提琴?也許?只想玩 –

回答

2

當您實際調用AudioBufferSourceNode上的start()時,您不會顯示,因此我無法顯式調試。但總而言之,您需要計劃停止並在需要發生時啓動AHEAD,而不是嘗試在setTimeout回調中「交換」播放的活動聲音(或者在聲音停止播放時嘗試讓它們對齊)。

例如,你會做這樣的事情:

var bs1 = audioCtx.createBufferSourceNode(); 
var bs2 = audioCtx.createBufferSourceNode(); 
var bs3 = audioCtx.createBufferSourceNode(); 
var now = audioCtx.currentTime + 0.020; // add 20ms for scheduling slop 

bs1.buffer = audioBuffer; 
bs1.connect(audioCtx.destination); 
bs2.buffer = audioBuffer; 
bs2.connect(audioCtx.destination); 
bs3.buffer = audioBuffer; 
bs3.connect(audioCtx.destination); 
bs1.start(now, 0, 5); // time, offset, duration 
bs2.start(now+5, 30, 5); 
bs3.start(now+10, 5); // no duration; play to end 

如果要取消這個遊戲,你必須斷開buffersource節點。

+0

感謝您的答案!是的,像這樣的東西會工作我想,但我需要先測試它。什麼是20ms?在無數次排隊跳過的情況下,進行此操作的最佳方式是什麼? –

+0

20ms(或更多)是因爲您需要在WebAudio時間軸中安排事情,才能實際發生。 cf兩個時鐘的故事:https://www.html5rocks.com/en/tutorials/audio/scheduling/。 – cwilso

1

其中的主要問題是時間的準確性,從0:05到0:30以及從0:35到0:05的無縫轉換。

我想你應該創建兩個不同的音頻源。從0:00開始,在0:30尋找另一個,但在開始使用事件播放時立即暫停。

然後使用ontimeupdate事件跟蹤時間並檢查它是否爲5,然後暫停當前並開始第二個應該被緩衝的時間,當它到達0:35時暫停並啓動第一個並讓它播放直到音頻結束。

+0

感謝您的建議!我已經試過這個,不認爲它會滿足精確度的要求,並立即通過賽道尋找。 https://stackoverflow.com/a/12325960/2957677 –

1

播放:0:00到0.05,跳過:0:05到0:30,播放:0:30到0:35,跳過:0:35 到0:05,播放:0.05到結束

您可以使用Media Fragments URIpause事件來按順序呈現精確的媒體播放。如果必要的話通過HTMLMediaElementAudioContext.createMediaElementSource()

const audio = document.querySelector("audio"); 
 

 
const mediaURL = "https://ia600305.us.archive.org/30/items/return_201605/return.mp3"; 
 

 
const timeSlices = ["#t=0,5", "#t=30,35", "#t=5"]; 
 
let index = 0; 
 
audio.onpause = e => { 
 
    if (index < timeSlices.length) { 
 
    audio.src = `${mediaURL}${timeSlices[index++]}` 
 
    } else { 
 
    console.log("Media fragments playlist completed"); 
 
    } 
 
} 
 
audio.ontimeupdate = e => console.log(Math.ceil(audio.currentTime)); 
 
audio.oncanplay = e => audio.play(); 
 
audio.src = `${mediaURL}${timeSlices[index++]}`;
<audio controls></audio>

+0

感謝您的回答!我不知道你可以這樣做。但是,在這個例子中,轉換不是一個「立即無縫跳過」。我將在整首歌中毫無節奏地過渡,所以重要的一點是轉換髮生在一個非常精確的點上,但是使用'ontimeupdate'不能提供足夠的精度(https://stackoverflow.com/a/12325960/2957677) –

+0

@TobyMellor'timeupdate'事件僅用於顯示媒體播放的精確'.currentTime'。 'pause'事件處理程序是下一個媒體分區被設置爲'.src''

+0

@TobyMellor另請參見[HTML5音頻流:精確測量延遲?](https://stackoverflow.com/questions/38768375/html5-audio-streaming-precisely-measure-latency) – guest271314