网络音频 API - 多个同步曲目 - 新曲目开始时停止上一曲目
Web Audio API - multiple synchronized tracks - stopping previous track when new track starts
我正在尝试模仿 'Mozilla Web Audio API for games' 网络文档中的网络音频 API 多轨演示。
我唯一需要注意的是,我希望在单击新曲目后停止之前的曲目(而不是在彼此之上分层播放)。
例如,单击鼓,鼓开始演奏,然后单击吉他,鼓停止,吉他从鼓停止的地方开始演奏。
有什么想法吗?是否有更好的 tools/libraries 来处理网络音频?
http://jsfiddle.net/c87z11jj/1/
<ul>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-leadguitar.mp3">Lead Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-drums.mp3">Drums</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-bassguitar.mp3">Bass Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-horns.mp3">Horns</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-clav.mp3">Clavi</a></li>
</ul>
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var offset = 0;
var context = new AudioContext();
function playTrack(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
var audiobuffer;
// Decode asynchronously
request.onload = function() {
if (request.status == 200) {
context.decodeAudioData(request.response, function(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
console.log('context.currentTime '+context.currentTime);
if (offset == 0) {
source.start();
offset = context.currentTime;
} else {
source.start(0,context.currentTime - offset);
}
}, function(e) {
console.log('Error decoding audio data:' + e);
});
} else {
console.log('Audio didn\'t load successfully; error code:' + request.statusText);
}
}
request.send();
}
var tracks = document.getElementsByClassName('track');
for (var i = 0, len = tracks.length; i < len; i++) {
tracks[i].addEventListener('click', function(e){
console.log(this.href);
playTrack(this.href);
e.preventDefault();
});
}
只需将 BufferSources 存储在外部作用域的某处,然后调用它们的 stop()
方法。
我冒昧地重写了一点你的加载逻辑,你不应该在每次开始新曲目时都创建一个新请求,在这种情况下你失去了 AudioBuffers 相对于 Audio 元素的主要优势:它们是真正快速实例化。
var active_source = null;
function stopActiveSource() {
if (active_source) {
active_source.onended = null; // manual stop, no event
active_source.stop(0);
}
}
// instead of requesting a new ArrayBuffer every time
// store them in a dictionnary
var buffers = {};
var context = new(window.AudioContext || window.webkitAudioContext)();
function playTrack(url) {
// get fom our dictionnary
var buffer = buffers[url];
// stop the active one if any
stopActiveSource();
// create a new BufferSource
var source = context.createBufferSource();
// it is now the active one
active_source = source;
source.onended = function() {
active_source = null;
};
source.buffer = buffer;
source.connect(context.destination);
source.start(0);
}
// start by getting all AudioBuffers
var tracks = document.getElementsByClassName('track');
for (var i = 0, len = tracks.length; i < len; i++) {
tracks[i].addEventListener('click', function(e) {
playTrack(this.href);
e.preventDefault();
});
getBuffer(tracks[i].href);
}
function getBuffer(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(evt) {
context.decodeAudioData(request.response, store);
};
request.send();
function store(buffer) {
buffers[url] = buffer;
}
}
<base href="https://dl.dropboxusercontent.com/s/">
<ul>
<li><a class="track" href="kbgd2jm7ezk3u3x/hihat.mp3">HiHat</a></li>
<li><a class="track" href="h2j6vm17r07jf03/snare.mp3">Snare</a></li>
<li><a class="track" href="1cdwpm3gca9mlo0/kick.mp3">Kick</a></li>
<li><a class="track" href="h8pvqqol3ovyle8/tom.mp3">Tom</a></li>
</ul>
在@Kaiido 的帮助下解决了这个问题
同时同步并在上一曲目停止的地方开始新曲目的示例:
<ul>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-leadguitar.mp3">Lead Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-drums.mp3">Drums</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-bassguitar.mp3">Bass Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-horns.mp3">Horns</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-clav.mp3">Clavi</a></li>
</ul>
let active_source = null;
let buffers = {};
const context = new(window.AudioContext || window.webkitAudioContext)();
let offset = 0;
const tempo = 3.074074076;
const tracks = document.getElementsByClassName('track');
function playTrack(url) {
let buffer = buffers[url];
let source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.loop = true;
if (offset == 0) {
source.start();
offset = context.currentTime;
active_source = source;
} else {
let relativeTime = context.currentTime - offset;
let beats = relativeTime / tempo;
let remainder = beats - Math.floor(beats);
let delay = tempo - (remainder*tempo);
let when = context.currentTime+delay;
stopActiveSource(when);
source.start(context.currentTime+delay,relativeTime+delay);
active_source = source;
source.onended = function() {
active_source = null;
};
}
}
for (var i = 0, len = tracks.length; i < len; i++) {
tracks[i].addEventListener('click', function(e) {
playTrack(this.href);
e.preventDefault();
});
getBuffer(tracks[i].href);
}
function getBuffer(url) {
const request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(evt) {
context.decodeAudioData(request.response, store);
};
request.send();
function store(buffer) {
buffers[url] = buffer;
}
}
function stopActiveSource(when) {
if (active_source) {
active_source.onended = null;
active_source.stop(when);
}
}
我正在尝试模仿 'Mozilla Web Audio API for games' 网络文档中的网络音频 API 多轨演示。
我唯一需要注意的是,我希望在单击新曲目后停止之前的曲目(而不是在彼此之上分层播放)。
例如,单击鼓,鼓开始演奏,然后单击吉他,鼓停止,吉他从鼓停止的地方开始演奏。
有什么想法吗?是否有更好的 tools/libraries 来处理网络音频?
http://jsfiddle.net/c87z11jj/1/
<ul>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-leadguitar.mp3">Lead Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-drums.mp3">Drums</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-bassguitar.mp3">Bass Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-horns.mp3">Horns</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-clav.mp3">Clavi</a></li>
</ul>
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var offset = 0;
var context = new AudioContext();
function playTrack(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
var audiobuffer;
// Decode asynchronously
request.onload = function() {
if (request.status == 200) {
context.decodeAudioData(request.response, function(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
console.log('context.currentTime '+context.currentTime);
if (offset == 0) {
source.start();
offset = context.currentTime;
} else {
source.start(0,context.currentTime - offset);
}
}, function(e) {
console.log('Error decoding audio data:' + e);
});
} else {
console.log('Audio didn\'t load successfully; error code:' + request.statusText);
}
}
request.send();
}
var tracks = document.getElementsByClassName('track');
for (var i = 0, len = tracks.length; i < len; i++) {
tracks[i].addEventListener('click', function(e){
console.log(this.href);
playTrack(this.href);
e.preventDefault();
});
}
只需将 BufferSources 存储在外部作用域的某处,然后调用它们的 stop()
方法。
我冒昧地重写了一点你的加载逻辑,你不应该在每次开始新曲目时都创建一个新请求,在这种情况下你失去了 AudioBuffers 相对于 Audio 元素的主要优势:它们是真正快速实例化。
var active_source = null;
function stopActiveSource() {
if (active_source) {
active_source.onended = null; // manual stop, no event
active_source.stop(0);
}
}
// instead of requesting a new ArrayBuffer every time
// store them in a dictionnary
var buffers = {};
var context = new(window.AudioContext || window.webkitAudioContext)();
function playTrack(url) {
// get fom our dictionnary
var buffer = buffers[url];
// stop the active one if any
stopActiveSource();
// create a new BufferSource
var source = context.createBufferSource();
// it is now the active one
active_source = source;
source.onended = function() {
active_source = null;
};
source.buffer = buffer;
source.connect(context.destination);
source.start(0);
}
// start by getting all AudioBuffers
var tracks = document.getElementsByClassName('track');
for (var i = 0, len = tracks.length; i < len; i++) {
tracks[i].addEventListener('click', function(e) {
playTrack(this.href);
e.preventDefault();
});
getBuffer(tracks[i].href);
}
function getBuffer(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(evt) {
context.decodeAudioData(request.response, store);
};
request.send();
function store(buffer) {
buffers[url] = buffer;
}
}
<base href="https://dl.dropboxusercontent.com/s/">
<ul>
<li><a class="track" href="kbgd2jm7ezk3u3x/hihat.mp3">HiHat</a></li>
<li><a class="track" href="h2j6vm17r07jf03/snare.mp3">Snare</a></li>
<li><a class="track" href="1cdwpm3gca9mlo0/kick.mp3">Kick</a></li>
<li><a class="track" href="h8pvqqol3ovyle8/tom.mp3">Tom</a></li>
</ul>
在@Kaiido 的帮助下解决了这个问题
同时同步并在上一曲目停止的地方开始新曲目的示例:
<ul>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-leadguitar.mp3">Lead Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-drums.mp3">Drums</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-bassguitar.mp3">Bass Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-horns.mp3">Horns</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-clav.mp3">Clavi</a></li>
</ul>
let active_source = null;
let buffers = {};
const context = new(window.AudioContext || window.webkitAudioContext)();
let offset = 0;
const tempo = 3.074074076;
const tracks = document.getElementsByClassName('track');
function playTrack(url) {
let buffer = buffers[url];
let source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.loop = true;
if (offset == 0) {
source.start();
offset = context.currentTime;
active_source = source;
} else {
let relativeTime = context.currentTime - offset;
let beats = relativeTime / tempo;
let remainder = beats - Math.floor(beats);
let delay = tempo - (remainder*tempo);
let when = context.currentTime+delay;
stopActiveSource(when);
source.start(context.currentTime+delay,relativeTime+delay);
active_source = source;
source.onended = function() {
active_source = null;
};
}
}
for (var i = 0, len = tracks.length; i < len; i++) {
tracks[i].addEventListener('click', function(e) {
playTrack(this.href);
e.preventDefault();
});
getBuffer(tracks[i].href);
}
function getBuffer(url) {
const request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(evt) {
context.decodeAudioData(request.response, store);
};
request.send();
function store(buffer) {
buffers[url] = buffer;
}
}
function stopActiveSource(when) {
if (active_source) {
active_source.onended = null;
active_source.stop(when);
}
}