I'm using WaveSurfer.js for a project to show the waveform of an audio clip:
I wanted the user to be able to select a portion of the audio and get that as a separate audio clip.
Based on this StackOverflow answer by the author of the library, here is a more general version of the code:
function createBuffer(originalBuffer, duration) {
var sampleRate = originalBuffer.sampleRate
var frameCount = duration * sampleRate
var channels = originalBuffer.numberOfChannels
return new AudioContext().createBuffer(channels, frameCount, sampleRate)
}
function copyBuffer(fromBuffer, fromStart, fromEnd, toBuffer, toStart) {
var sampleRate = fromBuffer.sampleRate
var frameCount = (fromEnd - fromStart) * sampleRate
for (var i = 0; i < fromBuffer.numberOfChannels; i++) {
var fromChanData = fromBuffer.getChannelData(i)
var toChanData = toBuffer.getChannelData(i)
for (var j = 0, f = Math.round(fromStart*sampleRate), t = Math.round(toStart*sampleRate); j < frameCount; j++, f++, t++) {
toChanData[t] = fromChanData[f]
}
}
}
Here is how to use these functions:
// our WaveSurfer instance
var wavesurfer = WaveSurfer.create({ container: '#waveform' })
wavesurfer.load('track.mp3')
// the clip we want to get
var start = 10 // get it with waversufer.getCurrentTime()
var end = 20
var duration = end - start
// create a new buffer to hold the new clip
var buffer = createBuffer(wavesurfer.backend.buffer, duration)
// copy
copyBuffer(wavesurfer.backend.buffer, start, end, buffer, 0)
// load the new buffer
wavesurfer.empty()
wavesurfer.loadDecodedBuffer(buffer)
The reason I structured the code like this is so that I can create a buffer of the clip repeated several times:
var buffer = createBuffer(wavesurfer.backend.buffer, duration*3)
copyBuffer(wavesurfer.backend.buffer, start, end, buffer, 0)
copyBuffer(wavesurfer.backend.buffer, start, end, buffer, duration)
copyBuffer(wavesurfer.backend.buffer, start, end, buffer, duration*2)