Skip to content

Commit

Permalink
refactor: general code review
Browse files Browse the repository at this point in the history
  • Loading branch information
joanroig committed May 3, 2023
1 parent 51bd35a commit 82dc9b6
Showing 1 changed file with 34 additions and 67 deletions.
101 changes: 34 additions & 67 deletions src/services/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,92 +10,70 @@ export class MidiChordDetector {
threshold: number;

constructor() {
// Read config file
// read config file
const configPath = path.join(process.cwd(), "config.json");
const config = JSON.parse(fs.readFileSync(configPath).toString());
// Prepare in and out paths taking into account if they are relative or absolute
if (!path.isAbsolute(config.inFolderPath)) {
this.inFolderPath = path.join(process.cwd(), `${config.inFolderPath}`);
} else {
this.inFolderPath = config.inFolderPath;
}
if (!path.isAbsolute(config.outFolderPath)) {
this.outFolderPath = path.join(process.cwd(), `${config.outFolderPath}`);
} else {
this.outFolderPath = config.outFolderPath;
}
// Set threshold
this.inFolderPath = path.resolve(process.cwd(), config.inFolderPath);
this.outFolderPath = path.resolve(process.cwd(), config.outFolderPath);
this.threshold = config.threshold;
}

public start(): void {
const directories = fs
.readdirSync(this.inFolderPath, { withFileTypes: true })
.filter((dirent): boolean => dirent.isDirectory())
.map((dirent): string => dirent.name);
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);

for (const directory of directories) {
directories.forEach((directory) => {
const fileNames = fs
.readdirSync(path.join(this.inFolderPath, directory))
.filter(
(name): boolean => name.endsWith(".mid") || name.endsWith(".midi")
);
.filter((name) => name.endsWith(".mid") || name.endsWith(".midi"));

for (const fileName of fileNames) {
fileNames.forEach((fileName) => {
const filePath = path.join(this.inFolderPath, directory, fileName);
const midiData = fs.readFileSync(filePath);
const midiParsed = midi.parseMidi(midiData);
console.info(`Detecting chords: "${filePath}"`);
this.detectChords(`${directory}/${fileName}`, midiParsed);
}
}
});
});
console.info(`Finished!`);
}

private detectChords(outFilePath: string, midiParsed: midi.MidiData): void {
// Use the note events of the first track that has any note
let noteEvents;
for (const track of midiParsed.tracks) {
for (const event of track) {
if (event.type === "noteOn") {
noteEvents = track;
}
}
}
// use the note events of the first track that has any note
const noteEvents = midiParsed.tracks.find((track) =>
track.some((event) => event.type === "noteOn")
);
if (!noteEvents) {
console.error("None of the tracks of has notes!");
console.error("None of the tracks has notes!");
return;
}

const chordSet: ChordSet = { CHORD: [] };
const notesOn: number[] = []; // track all noteOn events
const chords: number[][] = []; // store all chords found
const notesOn: number[] = [];
const chords: number[][] = [];

for (const event of noteEvents) {
noteEvents.forEach((event) => {
if (event.type === "noteOn") {
notesOn.push(event.noteNumber);
} else if (event.type === "noteOff") {
const deltaTime = event.deltaTime;
if (deltaTime > this.threshold) {
// if deltaTime is greater than the threshold, it's the end of the current chord
chords.push([...new Set(notesOn)]); // add the chord to the chords array, using a Set to remove duplicates
notesOn.length = 0; // clear the notesOn array
}
} else if (event.type === "noteOff" && event.deltaTime > this.threshold) {
// if deltaTime is greater than the threshold, it's the end of the current chord
chords.push([...new Set(notesOn)]);
notesOn.length = 0;
}
}
});

// handle the last chord if notesOn is not empty
if (notesOn.length) {
chords.push([...new Set(notesOn)]);
}

for (const chord of chords) {
chords.forEach((chord) => {
const chordXml: Chord = { NOTE: [] };
for (const note of chord) {
chordXml.NOTE.push({ MIDI: note });
}
chord.forEach((note) => chordXml.NOTE.push({ MIDI: note }));
chordSet.CHORD.push(chordXml);
}
});

const xml = this.getChordSetXml(chordSet);
this.saveChordsXml(xml, outFilePath);
Expand All @@ -106,13 +84,13 @@ export class MidiChordDetector {

xml.push(`<?xml version="1.0" encoding="UTF-8"?>\n\n`);
xml.push(`<CHORDSET version="2" uuid="${this.generateUuid()}">\n`);
for (const chord of chordSet.CHORD) {
chordSet.CHORD.forEach((chord) => {
xml.push(` <CHORD>\n`);
for (const note of chord.NOTE) {
chord.NOTE.forEach((note) => {
xml.push(` <NOTE MIDI="${note.MIDI}"/>\n`);
}
});
xml.push(` </CHORD>\n`);
}
});
xml.push(`</CHORDSET>\n`);

const res = xml.join("");
Expand All @@ -132,27 +110,16 @@ export class MidiChordDetector {
if (!fs.existsSync(directoryPath)) {
fs.mkdirSync(directoryPath, { recursive: true });
}

// write the file
console.info(`Saving scaler set: "${outFilePath}"`);
fs.writeFileSync(outFilePath, xml);
}

private generateUuid(): string {
let uuid = "";
const chars = "abcdef0123456789";
const segments = [8, 4, 4, 4, 12];

segments.forEach((segment, index) => {
for (let i = 0; i < segment; i++) {
const char = chars[Math.floor(Math.random() * chars.length)];
uuid += char;
}
if (index !== segments.length - 1) {
uuid += "-";
}
});

return uuid;
const randomChar = () => Math.floor(Math.random() * 16).toString(16);
return segments
.map((segment) => Array.from({ length: segment }, randomChar).join(""))
.join("-");
}
}

0 comments on commit 82dc9b6

Please sign in to comment.