mirror of
https://github.com/CKegel/Web-SSTV.git
synced 2025-06-07 12:05:49 +00:00
Update encode.js to add saving functionality
This commit is contained in:
parent
3dd92a6dbc
commit
5876a3ec32
127
encode.js
127
encode.js
@ -604,8 +604,83 @@ modeSelect.addEventListener("change", (e) => {
|
|||||||
drawPreview();
|
drawPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
startButton.onclick = () => {
|
function bufferToWave(abuffer, len) {
|
||||||
|
var numOfChan = abuffer.numberOfChannels,
|
||||||
|
length = len * numOfChan * 2 + 44,
|
||||||
|
buffer = new ArrayBuffer(length),
|
||||||
|
view = new DataView(buffer),
|
||||||
|
channels = [],
|
||||||
|
i,
|
||||||
|
sample,
|
||||||
|
offset = 0,
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
// write WAVE header
|
||||||
|
setUint32(0x46464952); // "RIFF"
|
||||||
|
setUint32(length - 8); // file length - 8
|
||||||
|
setUint32(0x45564157); // "WAVE"
|
||||||
|
|
||||||
|
setUint32(0x20746d66); // "fmt " chunk
|
||||||
|
setUint32(16); // length = 16
|
||||||
|
setUint16(1); // PCM (uncompressed)
|
||||||
|
setUint16(numOfChan);
|
||||||
|
setUint32(abuffer.sampleRate);
|
||||||
|
setUint32(abuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
|
||||||
|
setUint16(numOfChan * 2); // block-align
|
||||||
|
setUint16(16); // 16-bit (hardcoded in this demo)
|
||||||
|
|
||||||
|
setUint32(0x61746164); // "data" - chunk
|
||||||
|
setUint32(length - pos - 4); // chunk length
|
||||||
|
|
||||||
|
// write interleaved data
|
||||||
|
for (i = 0; i < abuffer.numberOfChannels; i++)
|
||||||
|
channels.push(abuffer.getChannelData(i));
|
||||||
|
|
||||||
|
while (pos < length) {
|
||||||
|
for (i = 0; i < numOfChan; i++) {
|
||||||
|
// interleave channels
|
||||||
|
sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
|
||||||
|
sample = (0.5 + sample * 0.5) * 0xffff; // scale to 16-bit unsigned
|
||||||
|
sample = Math.floor(sample);
|
||||||
|
view.setUint16(pos, sample, true); // write 16-bit sample
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
offset++; // next source sample
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
function setUint16(data) {
|
||||||
|
view.setUint16(pos, data, true);
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUint32(data) {
|
||||||
|
view.setUint32(pos, data, true);
|
||||||
|
pos += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveWavFile(buffer, filename) {
|
||||||
|
var blob = new Blob([buffer], { type: 'audio/wav' });
|
||||||
|
var url = window.URL.createObjectURL(blob);
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.style.display = 'none';
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
let saveButton = document.createElement("button");
|
||||||
|
saveButton.id = "saveButton";
|
||||||
|
saveButton.className = "contrast";
|
||||||
|
saveButton.textContent = "Save as WAV";
|
||||||
|
saveButton.disabled = true;
|
||||||
|
document.querySelector("main.container").appendChild(saveButton);
|
||||||
|
|
||||||
|
startButton.onclick = () => {
|
||||||
if (modeSelect.value == "none") {
|
if (modeSelect.value == "none") {
|
||||||
warningText.textContent = "You must select a mode";
|
warningText.textContent = "You must select a mode";
|
||||||
startButton.disabled = true;
|
startButton.disabled = true;
|
||||||
@ -625,11 +700,61 @@ startButton.onclick = () => {
|
|||||||
audioCtx.resume();
|
audioCtx.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sstvFormat.getDuration = function() {
|
||||||
|
return (this.numScanLines * (this.scanLineLength + this.blankingInterval) + this.syncPulseLength) + 1;
|
||||||
|
};
|
||||||
|
|
||||||
let oscillator = audioCtx.createOscillator();
|
let oscillator = audioCtx.createOscillator();
|
||||||
oscillator.type = "sine";
|
oscillator.type = "sine";
|
||||||
|
|
||||||
|
let dest = audioCtx.createMediaStreamDestination();
|
||||||
|
oscillator.connect(dest);
|
||||||
oscillator.connect(audioCtx.destination);
|
oscillator.connect(audioCtx.destination);
|
||||||
|
|
||||||
sstvFormat.prepareImage(canvasData.data);
|
sstvFormat.prepareImage(canvasData.data);
|
||||||
sstvFormat.encodeSSTV(oscillator, audioCtx.currentTime + 1);
|
sstvFormat.encodeSSTV(oscillator, audioCtx.currentTime + 1);
|
||||||
|
|
||||||
|
let recorder = new MediaRecorder(dest.stream);
|
||||||
|
let chunks = [];
|
||||||
|
|
||||||
|
recorder.ondataavailable = (e) => {
|
||||||
|
chunks.push(e.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
recorder.onstop = async () => {
|
||||||
|
let blob = new Blob(chunks, { type: 'audio/ogg; codecs=opus' });
|
||||||
|
let arrayBuffer = await blob.arrayBuffer();
|
||||||
|
let audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
|
||||||
|
let wavBuffer = bufferToWave(audioBuffer, audioBuffer.length);
|
||||||
|
let wavBlob = new Blob([wavBuffer], { type: 'audio/wav' });
|
||||||
|
let url = URL.createObjectURL(wavBlob);
|
||||||
|
let imgName = imgPicker.value.split("\\")[2].split(".")[0];
|
||||||
|
saveButton.dataset.url = url;
|
||||||
|
saveButton.dataset.filename = 'sstv_' + imgName + '.wav';
|
||||||
|
saveButton.disabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
recorder.start();
|
||||||
|
oscillator.stop(audioCtx.currentTime + 1 + sstvFormat.getDuration());
|
||||||
|
|
||||||
|
oscillator.onended = () => {
|
||||||
|
recorder.stop();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
saveButton.onclick = () => {
|
||||||
|
let url = saveButton.dataset.url;
|
||||||
|
let filename = saveButton.dataset.filename;
|
||||||
|
let a = document.createElement('a');
|
||||||
|
a.style.display = 'none';
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
if (saveButton.dataset.url) {
|
||||||
|
window.URL.revokeObjectURL(saveButton.dataset.url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user