问题
I have created a multitrack web player using wavesurfer.js which can adjust the levels and panning of the different tracks.
What I want to do is export the mixed tracks with new levels and panning as a single .wav file.
I've done a bit of research into this and alot of people are pointing to https://github.com/mattdiamond/Recorderjs but development stopped on this over 4 years ago and from what I've found it seems to have a load of issues.
Just initializing it like so var rec = new Recorder(spectrum);
I get an error saying Cannot read property 'createScriptProcessor' of undefined at new Recorder
And a quick search shows that is deprecated, see https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/createScriptProcessor.
Although I have a multitrack player if I can figure out how to export a single track with the levels and panning I could go from there. Is there any other way of exporting web audio with just the web audio API or can anybody point me to any other js libraries like this that might work?
回答1:
Assuming you have PCM audio, you can add a RIFF/WAV header to it, create a Blob from that, and then set blob as an Object URL on the a.href
attribute. StackOverflow blocks the download here, but you run it locally to test. Hope this helps! 🇮🇪
// fetch stereo PCM Float 32 little-endian file
const url = 'https://batman.dev/static/61881209/triangle-stereo-float.pcm'
const ctx = new AudioContext()
const elStatus = document.querySelector('#status')
const elButton = document.querySelector('#download')
init().catch(showError)
async function init() {
// get raw/PCM buffer (you will presumably already have your own)
const buffer = await (await fetch(url)).arrayBuffer()
// get WAV file bytes and audio params of your audio source
const wavBytes = getWavBytes(buffer, {
isFloat: true, // floating point or 16-bit integer (WebAudio API decodes to Float32Array)
numChannels: 2,
sampleRate: 44100,
})
// add the button
elButton.href = URL.createObjectURL(
new Blob([wavBytes], { type: 'audio/wav' })
)
elButton.setAttribute('download', 'my-audio.wav') // name file
status('')
elButton.hidden = false
}
function status(msg) {
elStatus.innerText = msg
}
function showError(e) {
console.error(e)
status(`ERROR: ${e}`)
}
// Returns Uint8Array of WAV bytes
function getWavBytes(buffer, options) {
const type = options.isFloat ? Float32Array : Uint16Array
const numFrames = buffer.byteLength / type.BYTES_PER_ELEMENT
const headerBytes = getWavHeader(Object.assign({}, options, { numFrames }))
const wavBytes = new Uint8Array(headerBytes.length + buffer.byteLength);
// prepend header, then add pcmBytes
wavBytes.set(headerBytes, 0)
wavBytes.set(new Uint8Array(buffer), headerBytes.length)
return wavBytes
}
// adapted from https://gist.github.com/also/900023
// returns Uint8Array of WAV header bytes
function getWavHeader(options) {
const numFrames = options.numFrames
const numChannels = options.numChannels || 2
const sampleRate = options.sampleRate || 44100
const bytesPerSample = options.isFloat? 4 : 2
const format = options.isFloat? 3 : 1
const blockAlign = numChannels * bytesPerSample
const byteRate = sampleRate * blockAlign
const dataSize = numFrames * blockAlign
const buffer = new ArrayBuffer(44)
const dv = new DataView(buffer)
let p = 0
function writeString(s) {
for (let i = 0; i < s.length; i++) {
dv.setUint8(p + i, s.charCodeAt(i))
}
p += s.length
}
function writeUint32(d) {
dv.setUint32(p, d, true)
p += 4
}
function writeUint16(d) {
dv.setUint16(p, d, true)
p += 2
}
writeString('RIFF') // ChunkID
writeUint32(dataSize + 36) // ChunkSize
writeString('WAVE') // Format
writeString('fmt ') // Subchunk1ID
writeUint32(16) // Subchunk1Size
writeUint16(format) // AudioFormat
writeUint16(numChannels) // NumChannels
writeUint32(sampleRate) // SampleRate
writeUint32(byteRate) // ByteRate
writeUint16(blockAlign) // BlockAlign
writeUint16(bytesPerSample * 8) // BitsPerSample
writeString('data') // Subchunk2ID
writeUint32(dataSize) // Subchunk2Size
return new Uint8Array(buffer)
}
body {
padding: 2rem;
font-family: sans-serif;
text-align: center;
}
#download {
padding: 1em 2em;
color: #fff;
background: #4c8bf5;
text-decoration: none;
}
<div id="status">Loading...</div>
<a hidden id="download">⬇ Download</a>
来源:https://stackoverflow.com/questions/62157122/downloading-audio-from-web-that-has-been-modified-with-wavesurfer-js