Skip to content

Instantly share code, notes, and snippets.

@mauritslamers
Created December 5, 2019 21:02
Show Gist options
  • Save mauritslamers/6015a256a2aa51689e3b8f9afd914a4d to your computer and use it in GitHub Desktop.
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
Copy link

masongcm commented Mar 8, 2021

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