Published
Edited
Nov 8, 2020
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
parseTab(tab)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
parseTab(tab).map(slice =>
slice.map(({ string, fret }) =>
placementToNote(STANDARD_TUNING, string, fret)
)
)
Insert cell
Insert cell
function parseTab(tab) {
const lines = tab.trim().split('\n');

let head = 0;
let slice = null;
const reel = [];

function startSlice() {
slice = Array(6).fill();
}

function flushSlice() {
reel.push({ slice, index: head - 1 });
slice = null;
}

function addToSlice(curr) {
curr.forEach((fret, i) => {
if (!slice[i]) {
slice[i] = fret;
} else {
slice[i] += fret;
}
});
}

function readHead() {
const result = lines.map(line => line[head]);
head += 1;
return result;
}

while (lines.some(line => head < line.length)) {
const curr = readHead().map(d => (isInteger(d) ? d : null));
const hasSomeFretInfo = curr.some(Boolean);

if (hasSomeFretInfo) {
if (!slice) {
startSlice();
}

addToSlice(curr);
} else if (slice) {
flushSlice();
}
}

return reel.map(({ index, slice }) =>
slice
.map((fret, string) => ({ fret, string: string + 1, index }))
.filter(d => d.fret != null)
.map(({ fret, string, index }) => ({
fret: Number.parseInt(fret),
string,
index
}))
);
}
Insert cell
function isInteger(str) {
const n = Number.parseInt(str);
return Number.isFinite(n) && !Number.isNaN(n);
}
Insert cell
function placementToNote(tuning, string, fret) {
return transposeBySemitones(tuning[string - 1], fret);
}
Insert cell
function transposeBySemitones(note, semitones) {
return tonal.Note.fromMidi(tonal.Note.midi(note) + semitones);
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more