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.

Revisions

  1. mauritslamers created this gist Dec 5, 2019.
    104 changes: 104 additions & 0 deletions audacity_rescue.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,104 @@
    // 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');