Fix audio

This commit is contained in:
Skyler Lehmkuhl 2025-01-15 21:15:05 -05:00
parent 7b28257d3f
commit 49a7e3f75f
1 changed files with 116 additions and 12 deletions

View File

@ -585,6 +585,8 @@ let actions = {
}, },
execute: (action) => { execute: (action) => {
let shape = pointerList[action.shape]; let shape = pointerList[action.shape];
console.log(action.shape)
console.log(pointerList)
shape.fillStyle = action.newColor; shape.fillStyle = action.newColor;
}, },
rollback: (action) => { rollback: (action) => {
@ -685,6 +687,8 @@ let actions = {
player: player, player: player,
start: action.frameNum, start: action.frameNum,
img: img, img: img,
src: action.audiosrc,
uuid: action.uuid
}; };
pointerList[action.uuid] = soundObj; pointerList[action.uuid] = soundObj;
newAudioLayer.sounds[action.uuid] = soundObj; newAudioLayer.sounds[action.uuid] = soundObj;
@ -2864,14 +2868,49 @@ class AudioLayer {
const audioLayer = new AudioLayer(json.idx, json.name); const audioLayer = new AudioLayer(json.idx, json.name);
// TODO: load audiolayer from json // TODO: load audiolayer from json
audioLayer.sounds = {}; audioLayer.sounds = {};
for (let id in json.sounds) {
const jsonSound = json.sounds[id]
const img = new Image();
img.className = "audioWaveform";
const player = new Tone.Player().toDestination();
player.load(jsonSound.src)
.then(() => {
generateWaveform(img, player.buffer, 50, 25, config.framerate);
})
.catch(error => {
// Handle any errors that occur during the load or waveform generation
console.error(error);
});
let soundObj = {
player: player,
start: jsonSound.start,
img: img,
src: jsonSound.src,
uuid: jsonSound.uuid
};
pointerList[jsonSound.uuid] = soundObj;
audioLayer.sounds[jsonSound.uuid] = soundObj;
// TODO: change start time
audioLayer.track.add(0, jsonSound.uuid);
}
audioLayer.audible = json.audible; audioLayer.audible = json.audible;
return audioLayer; return audioLayer;
} }
toJSON(randomizeUuid = false) { toJSON(randomizeUuid = false) {
console.log(this.sounds)
const json = {}; const json = {};
json.type = "AudioLayer"; json.type = "AudioLayer";
// TODO: build json from audiolayer // TODO: build json from audiolayer
json.sounds = {}; json.sounds = {};
for (let id in this.sounds) {
const sound = this.sounds[id]
json.sounds[id] = {
start: sound.start,
src: sound.src,
uuid: sound.uuid
}
}
json.audible = this.audible; json.audible = this.audible;
if (randomizeUuid) { if (randomizeUuid) {
json.idx = uuidv4(); json.idx = uuidv4();
@ -4545,6 +4584,60 @@ async function _open(path, returnJson = false) {
undoStack.push(action); undoStack.push(action);
} }
} else { } else {
if (file.version < "1.7.6") {
function restoreLineColors(obj) {
// Step 1: Create colorMapping dictionary
const colorMapping = (obj.actions || []).reduce((map, action) => {
if (action.name === "addShape" && action.action.curves.length > 0) {
map[action.action.uuid] = action.action.curves[0].color;
}
return map;
}, {});
// Step 2: Recursive pass to add colors from colorMapping back to curves
function recurse(item) {
if (item?.curves && item.idx && colorMapping[item.idx]) {
item.curves.forEach(curve => {
if (Array.isArray(curve)) curve.push(colorMapping[item.idx]);
});
}
Object.values(item).forEach(value => {
if (typeof value === 'object' && value !== null) recurse(value);
});
}
recurse(obj);
}
restoreLineColors(file)
function restoreAudio(obj) {
const audioSrcMapping = (obj.actions || []).reduce((map, action) => {
if (action.name === "addAudio") {
map[action.action.layeruuid] = action.action;
}
return map;
}, {});
function recurse(item) {
if (item.type=="AudioLayer" && audioSrcMapping[item.idx]) {
const action = audioSrcMapping[item.idx]
item.sounds[action.uuid] = {
start: action.frameNum,
src: action.audiosrc,
uuid: action.uuid
}
}
Object.values(item).forEach(value => {
if (typeof value === 'object' && value !== null) recurse(value);
});
}
recurse(obj);
}
restoreAudio(file)
}
// disabled for now // disabled for now
// for (let action of file.actions) { // for (let action of file.actions) {
// undoStack.push(action) // undoStack.push(action)
@ -4867,6 +4960,8 @@ async function setupVideoExport(ext, path, canvas, exportContext) {
let muxer; let muxer;
let videoEncoder; let videoEncoder;
let videoConfig; let videoConfig;
let audioEncoder;
let audioConfig;
const frameTimeMicroseconds = parseInt(1_000_000 / config.framerate) const frameTimeMicroseconds = parseInt(1_000_000 / config.framerate)
const oldContext = context; const oldContext = context;
context = exportContext; context = exportContext;
@ -4896,6 +4991,14 @@ async function setupVideoExport(ext, path, canvas, exportContext) {
height: config.fileHeight, height: config.fileHeight,
bitrate: bitrate, bitrate: bitrate,
}; };
// Todo: add configuration for mono/stereo
audioConfig = {
codec: 'mp4a.40.2', // AAC codec
sampleRate: 44100,
numberOfChannels: 2, // Mono
bitrate: 64000,
};
} else if (ext === "webm") { } else if (ext === "webm") {
target = new WebMMuxer.ArrayBufferTarget(); target = new WebMMuxer.ArrayBufferTarget();
muxer = new WebMMuxer.Muxer({ muxer = new WebMMuxer.Muxer({
@ -4916,6 +5019,13 @@ async function setupVideoExport(ext, path, canvas, exportContext) {
bitrate: bitrate, bitrate: bitrate,
bitrateMode: "constant", bitrateMode: "constant",
}; };
audioConfig = {
codec: 'opus', // Use Opus codec for WebM
sampleRate: 48000,
numberOfChannels: 2,
bitrate: 64000,
}
} }
// Initialize the video encoder // Initialize the video encoder
@ -4926,18 +5036,12 @@ async function setupVideoExport(ext, path, canvas, exportContext) {
videoEncoder.configure(videoConfig); videoEncoder.configure(videoConfig);
async function finishEncoding() { // audioEncoder = new AudioEncoder({
const progressText = document.getElementById('progressText'); // output: (chunk, meta) => muxer.addAudioChunk(chunk, meta),
progressText.innerText = 'Finalizing...'; // error: (e) => console.error(e),
const progressBar = document.getElementById('progressBar'); // });
progressBar.value = 100;
await videoEncoder.flush(); // audioEncoder.configure(audioConfig)
muxer.finalize();
await writeFile(path, new Uint8Array(target.buffer));
const modal = document.getElementById('progressModal');
modal.style.display = 'none';
document.querySelector("body").style.cursor = "default";
}
const processFrame = async (currentFrame) => { const processFrame = async (currentFrame) => {
if (currentFrame < root.maxFrame) { if (currentFrame < root.maxFrame) {