Skip to content

Commit

Permalink
ready clean (#531)
Browse files Browse the repository at this point in the history
  • Loading branch information
ppg0921 authored Feb 20, 2024
1 parent 3f4298e commit eeba269
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 237 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,6 @@ export NODE_OPTIONS="--max-old-space-size=8192"
pnpm install
node initDB.js jsons/exportDataEmpty.json
```
### Waveform generation
We use [audiowaveform](https://github.com/bbc/audiowaveform/tree/master?fbclid=IwAR19jDCDp5DCzdLK7Z8EgE7W5NSjm8B-wdFABsrT62D2b80bVtCuydkMgnM) to generate waveform. Refer to [Installation](https://github.com/bbc/audiowaveform/blob/master/README.md#installation) section to install the package first.

259 changes: 22 additions & 237 deletions utils/generateWaveform.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,15 @@
const fs = require("fs");
const process = require("process");
const path = require("path");
const axios = require("axios");

const child_process = require("child_process");

const sharp = require("sharp");
const jm = require("join-images");
const readline = require("readline");
const { getAudioDurationInSeconds } = require("get-audio-duration");

// the annotation part can be used when the file is a module

// import { createWriteStream, existsSync, unlink } from "fs";
// import { platform } from "process";
// import { join, dirname, basename } from "path";
// import path from 'path';
// import axios from "axios";
// import { fileURLToPath } from 'url';
// import { execSync } from 'child_process';
// const url = require("url");

// const __filename = url.fileURLToPath(import.meta.url);
// const __dirname = path.dirname(__filename);
// const __dirname = path.dirname(__filename);

// procedure: check existance->download->construct command->generate waveform

// const img_res = '4000x1000';
let img_milisec_per_pin = 4;
let img_res_width = 40000;
let img_res_height = 400;
let img_res = String(img_res_width) + "x" + String(img_res_height);
let music_duration = 420000;
// console.log(`img_res = ${img_res}`);
const hex_colo = "3D82B1";
const sname = "../files/music/waveform.png";
const sname_tmp = "../files/music/waveform_tmp.png";
const sname = "../files/data/wavform.json";
const musicName = "../files/music/2023.mp3"; // you can alter the music name here
let ifp = path.join(String(__dirname), sname); // name (path included) to the waveform image
let ifp_temp = path.join(String(__dirname), sname_tmp); // name (path included) to the waveform image
let sfp = path.join(String(__dirname), musicName); // name (path included) to the music
const leftName = "../files/music/leftWaveform.png";
const rightName = "../files/music/rightWaveform.png";
const lcp = path.join(String(__dirname), leftName);
const rcp = path.join(String(__dirname), rightName);
let releaseUrl = ""; // url for downloading ffmpeg
let ffbin = ""; // the path name(file name included) of the ffmpeg binary
let cmd = []; // the command that will later be used to generate waveform

const readLength = async () => {
await getAudioDurationInSeconds(sfp).then((duration) => {
music_duration = duration * 1000;
console.log(`duration = ${music_duration} miliseconds`);
});
};
let pixelsPerSecond = 1000;


const rl = readline.createInterface({
Expand All @@ -65,13 +20,17 @@ const rl = readline.createInterface({
const readIntegerInput = (prompt) => {
return new Promise((resolve, reject) => {
rl.question(prompt, (input) => {

const parsedInput = parseInt(input, 10);
if (!isNaN(parsedInput)) {
resolve(parsedInput);
} else {
console.log("Invalid input, try again...");
resolve(readIntegerInput(prompt));
// reject(new Error('Invalid input. Please enter a valid integer.'));
if (input == "") {
resolve(1000);
} else {
console.log("Invalid input, try again...");
resolve(readIntegerInput(prompt));
}
}
});
});
Expand All @@ -80,12 +39,10 @@ const readIntegerInput = (prompt) => {
const readInput = async () => {

try {
img_milisec_per_pin = await readIntegerInput("Enter time duration(miliseconds) in one pixel: ");
img_res_width = Math.round(music_duration / img_milisec_per_pin);
// img_res_width = await readIntegerInput('Enter the width of the waveform: ');
// img_res_height = await readIntegerInput('Enter the height of the waveform: ');
img_res = img_res_width + "x" + img_res_height;
console.log(`Waveform resolution: ${img_res}`);
pixelsPerSecond = await readIntegerInput("Enter pixels-per-second(1000): ");
if (!(typeof pixelsPerSecond === 'number' && isFinite(pixelsPerSecond))) {
pixelsPerSecond = 1000;
}
} catch (error) {
console.log("something wrong in the input...");
console.error(error.message);
Expand All @@ -94,16 +51,13 @@ const readInput = async () => {
}
};

const generateWav = () => {

const callFFMPEG = () => {
cmd = ["sudo", "ffmpeg"];
cmd = [...cmd, "-i", String(sfp),
"-hide_banner", "-loglevel", "error", "-filter_complex",
`showwavespic=s=${img_res}:split_channels=1:colors=${hex_colo}`,
"-frames:v", "1", "-y", String(ifp_temp)];
cmd = ["sudo", "audiowaveform"];
cmd = [...cmd, "-i", String(sfp), "-o", String(ifp), "--pixels-per-second", String(pixelsPerSecond)];

let cmdString = cmd.join(" ");
console.log(`cmd = ${cmd.join(" ")}`);
console.log(`cmd = ${cmdString}`);
let startGenerationTime = Date.now();
console.log("start generating");
try {
Expand All @@ -116,178 +70,9 @@ const callFFMPEG = () => {
console.log(`Time taken for generating waveform: ${elapsedTime} ms`);
};

// download from url and save as dest, then generate waveform
const dl_url = async (url, dest, ffmpegExistance) => {
await readInput(img_res_width, img_res_height);
if (!ffmpegExistance) {
let start_time = Date.now();
console.log("downloading ffmpeg...");
await axios({
method: "get",
url: url,
responseType: "stream",
// httpsAgent: {
// rejectUnauthorized: false, // Disable SSL verification
// },
}).then((response) => {
const writer = fs.createWriteStream(dest);
response.data.pipe(writer);
// console.log("here1");
return new Promise((resolve, reject) => {
let error = null;
writer.on("error", err => {
error = err;
writer.close();
reject(err);
});
// or writer.on('finish')?
writer.on("close", () => {
if (!error) {
resolve(true);
}
//no need to call the reject here, as it will have been called in the
//'error' stream;
});

});

}).then(() => {
const downloadTime = (Date.now() - start_time) / 1000; // Convert milliseconds to seconds
console.log(`Download time ${downloadTime.toFixed(2)}s`);
callFFMPEG();
}).catch(function(error) {
console.error("Error downloading file:", error.message);
});
} else {
callFFMPEG();
}
};

// unzip file (not used in this file but it exists in the original blender addon)
const unzip = (zipPath, extractDirPath) => {
const zip = new AdmZip(zipPath);

try {
zip.extractAllTo(extractDirPath, /*overwrite*/ true);
console.log(`Successfully extracted: ${zipPath} to ${extractDirPath}`);
} catch (error) {
console.error("Error extracting zip file:", error.message);
}
};

// check if ffmpeg exists
const ffmpegExist = async () => {
// decide releaseUrl based on operating system
if (process.platform.startsWith("win")) {
releaseUrl = "https://github.com/Pullusb/static_bin/raw/main/ffmpeg/windows/ffmpeg.exe";
} else if (process.platform.startsWith("linux") || process.platform === "freebsd") {
// console.log("linux");
releaseUrl = "https://github.com/Pullusb/static_bin/raw/main/ffmpeg/linux/ffmpeg";
} else { // Mac
releaseUrl = "https://github.com/Pullusb/static_bin/raw/main/ffmpeg/mac/ffmpeg";
}

// Check if ffmpeg is already in the current path
ffbin = path.join(String(__dirname), String(path.basename(releaseUrl)));
// console.log(ffbin);
// console.log(fs.existsSync(ffbin));
return fs.existsSync(ffbin);
};

const cutImages = async () => {
await sharp(String(ifp_temp))
.extract({ left: 0, top: 0, width: img_res_width, height: img_res_height / 4 })
.toFile(String(lcp))
.then(() => {
// console.log("here2.5")
})
.catch((err) => {
// console.log("here2.5");
if (err) {
console.log("Failed extracting upper half of the waveform...");
console.log(err);
}
});
// console.log("here3");
await sharp(String(ifp_temp))
.extract({ left: 0, top: 3 * img_res_height / 4, width: img_res_width, height: img_res_height / 4 })
.toFile(String(rcp))
.then(() => {
// console.log("here3.5")
})
.catch((err) => {
// console.log("here3.5");
if (err) {
console.log("Failed extracting lower half of the waveform...");
console.log(err);
}
});
// console.log("here4");
};

const AsyncJoinImages = async () => {
console.log("joining waveforms...");
await jm.joinImages([String(lcp), String(rcp)], { direction: "vertical" })
.then((img) => {
// Save image as file
img.toFile(String(ifp))
.then(() => { console.log("image saved"); })
.catch((err) => {
console.log("failed to save image as file");
console.log(err);
});

}).catch((err) => {
console.log("Failed joining waveforms...");
console.log(err);
});
};

const AsyncImageManipulation = async () => {
await cutImages();
await AsyncJoinImages();
};

const errReport = (err) => {
if (err) throw err;
};

const rmRedundantWaveform = () => {
let rmCmd = ["rm", String(lcp), String(rcp), String(ifp_temp)];
rmCmdString = rmCmd.join(" ");
// console.log(`rmCmdString = ${rmCmdString}`);
console.log("removing redundant waveforms...");
try {
fs.unlink(String(lcp), errReport);
fs.unlink(String(rcp), errReport);
fs.unlink(String(ifp_temp), errReport);
} catch (error) {
console.error("Error deleting existing file:", error.message);
}
};

const mainGenerate = async (releaseUrl, ffbin) => {
let ffmpegExistance = await ffmpegExist(); // ffbin, releaseUrl is set in ffmpegExist
await readLength();
await dl_url(releaseUrl, String(ffbin), ffmpegExistance);
await AsyncImageManipulation();
rmRedundantWaveform();
};


mainGenerate(releaseUrl, ffbin);

// download if ffmpeg not in path

// delete ffmpeg if exists
// if (ffmpegExistance) {
// try {
// fs.unlink(ffbin, (err) => {
// if (err) throw err;
// console.log(`successfully deleted ${ffbin}`);
// });
const mainGenerate = async () => {
await readInput();
generateWav();
}

// } catch (error) {
// console.error('Error deleting existing file:', error.message);
// }
// }
mainGenerate();

0 comments on commit eeba269

Please sign in to comment.