Skip to content

Instantly share code, notes, and snippets.

@mauritslamers
Created December 5, 2019 21:02
Show Gist options
  • Select an option

  • Save mauritslamers/6015a256a2aa51689e3b8f9afd914a4d to your computer and use it in GitHub Desktop.

Select an option

Save mauritslamers/6015a256a2aa51689e3b8f9afd914a4d to your computer and use it in GitHub Desktop.
audacity rescue script nodejs with stereo support
// recover from audacity crash
// usage:
// node audacity_rescue.js [sourcedir] [result_file.wav] [mono|stereo]
// the mono or stereo option is optional, the default is mono.
//
// Description:
// This script will sort all files on modification date.
// If you create a backup, make sure you use `cp -a` to preserve the modification date
// or create a zip.
// The resulting file will pretend to be a wave file (because of the .wav extension), but
// is in fact a Audacity simple block file. You will have to open the resulting file with
// audacity and then export it as a normal file.
const fs = require('fs');
const pathLib = require('path');
const pj = pathLib.join;
const srcdirname = process.argv[2];
const result_file = process.argv[3];
const type = process.argv[4];
if (!(srcdirname && result_file)) {
console.log("Need both a source dir and a target file");
process.exit(1);
}
if (!type) type = 'mono';
if (! (type === 'mono' || type === 'stereo')) {
console.log('invalid audio format');
}
console.log('source dir name: ', srcdirname);
console.log('result_file', result_file);
// we need all files in the subdirectories of dirname
// sorted by date
const allFiles = [];
function readFiles (dir) {
const names = fs.readdirSync(dir);
names.forEach(n => {
const fullPath = pj(dir, n);
const stats = fs.statSync(fullPath);
if (stats.isDirectory()) {
readFiles(fullPath);
}
else {
if (pathLib.extname(n) === ".au") {
allFiles.push({ name: fullPath, time: stats.mtime.getTime()})
}
}
});
}
console.log('searching and sorting files by date');
readFiles(srcdirname);
allFiles.sort( (a, b) => {
return a.time - b.time;
});
if (type === 'stereo') {
console.log('writing result files');
const f = pathLib.parse(result_file);
const left_fn = pathLib.join(f.root, f.dir, f.base + '_L' + f.ext);
const right_fn = pathLib.join(f.root, f.dir, f.base + '_R' + f.ext);
const fhL = fs.openSync(left_fn, 'w');
const fhR = fs.openSync(right_fn, 'w');
let targetPosL = 0;
let targetPosR = 0;
const numFiles = allFiles.length;
allFiles.forEach( (f, i) => {
console.log(`reading ${f.name} (${i} of ${numFiles})`);
const buf = fs.readFileSync(f.name);
const offset = (i === 0 || i === 1) ? 0: buf.readUInt16LE(4);
if (i % 2 === 0) { // assume left
targetPosL += fs.writeSync(fhL, buf, offset, buf.length-offset, targetPosL);
}
else {
// assume right
targetPosR += fs.writeSync(fhR, buf, offset, buf.length-offset, targetPosR);
}
})
fs.closeSync(fhL);
fs.closeSync(fhR);
}
else {
// now merge all files in the result
console.log('writing result file');
const fh = fs.openSync(result_file, 'w');
let targetPos = 0;
const numFiles = allFiles.length;
allFiles.forEach( (f, i) => {
console.log(`reading ${f.name} (${i} of ${numFiles})`);
const buf = fs.readFileSync(f.name);
const offset = i === 0? 0: buf.readUInt16LE(4);
targetPos += fs.writeSync(fh, buf, offset, buf.length-offset, targetPos);
});
fs.closeSync(fh);
}
console.log('Done');
@masongcm

masongcm commented Mar 8, 2021

Copy link
Copy Markdown

Hi! Thanks so much for putting this together. Do you know if this would work with multiple tracks? I'm trying on a crashed project that has multiple tracks, and they do get appended but they are in a strange order.

Also, do I have to run this on all files in the _data folder, on only a certain subset?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment