Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/ai-music-system / lib/training.js |
| Date | Not specified in source |
| Repository | Janady13/ai-music-system |
| Revision | 9a816470ef81edba7a5d8f3aae62c05eead638a7 |
| Branch | main |
| Path or archive member | lib/training.js |
| Original SHA-256 | 849a8b89c7578b0508cd61cc722236819af56106b746fd3eae3eda1e08703dee |
| Git blob SHA | 346de6d79338eb5c81c3998cad63aad5278d06e1 |
| Reading-copy SHA-256 | 849a8b89c7578b0508cd61cc722236819af56106b746fd3eae3eda1e08703dee |
Every recorded occurrence
These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/ai-music-system / lib/training.js | main | 9a816470ef81edba7a5d8f3aae62c05eead638a7 | Open original |
| Janady13/ai-music-system / lib/training.js | codex/create-ai-for-music-generation | 017052c0e72c6175378aebb7a7f9dd96e82c3ce2 | Open original |
Recovered source transcript
Download complete source text ↓300 lines · 9.0 KB
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
const { write } = require("./state");
const ROOT = path.join(__dirname, "..");
const DATA_DIR = path.join(ROOT, "data");
const CKPT_DIR = path.join(ROOT, "checkpoints");
const META_FILE = path.join(CKPT_DIR, "meta.json");
const MODEL_FILE = path.join(CKPT_DIR, "model.json");
const CORPUS_FILE = path.join(DATA_DIR, "music_corpus.json");
const DEFAULT_META = {
step: 0,
loss: 1.0,
lastCheckpoint: null,
datasetDigest: null,
model: {
profiles: {},
datasetSize: 0,
updatedAt: null,
datasetDigest: null
}
};
function ensureDirs() {
if (!fs.existsSync(CKPT_DIR)) fs.mkdirSync(CKPT_DIR, { recursive: true });
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });
}
function readJSON(file, fallback) {
try { return JSON.parse(fs.readFileSync(file, "utf8")); }
catch { return JSON.parse(JSON.stringify(fallback)); }
}
function normaliseMeta(meta) {
const merged = { ...DEFAULT_META, ...meta };
merged.model = { ...DEFAULT_META.model, ...(meta?.model || {}) };
if (!merged.model.profiles) merged.model.profiles = {};
return merged;
}
function loadMeta() {
ensureDirs();
return normaliseMeta(readJSON(META_FILE, DEFAULT_META));
}
function saveMeta(meta) {
ensureDirs();
const serialised = normaliseMeta(meta);
fs.writeFileSync(META_FILE, JSON.stringify(serialised, null, 2));
write({
train_step: serialised.step,
train_loss: serialised.loss,
dataset_digest: serialised.datasetDigest,
last_checkpoint: serialised.lastCheckpoint,
model_profiles: Object.keys(serialised.model.profiles || {}).length
});
}
function loadModel() {
ensureDirs();
const model = readJSON(MODEL_FILE, { profiles: {}, datasetSize: 0, updatedAt: null, datasetDigest: null });
if (!model.profiles) model.profiles = {};
return model;
}
function saveModel(model) {
ensureDirs();
fs.writeFileSync(MODEL_FILE, JSON.stringify(model, null, 2));
}
function loadCorpus() {
ensureDirs();
try {
return JSON.parse(fs.readFileSync(CORPUS_FILE, "utf8"));
} catch {
return [];
}
}
function hashCorpus(corpus) {
const json = JSON.stringify(corpus);
return crypto.createHash("sha1").update(json).digest("hex");
}
function createEmptyProfile(genre, mood) {
return {
genre,
mood,
count: 0,
tempo: { min: Infinity, max: 0, mean: 0 },
timeSignatures: {},
keys: {},
chordProgressions: {},
bassStyles: {},
drumPatterns: {},
melodicMotifs: {},
textures: {},
instruments: {},
sections: {},
arrangementBlueprints: {}
};
}
function ensureProfile(profiles, genre, mood) {
const key = `${genre}::${mood}`;
if (!profiles[key]) {
profiles[key] = createEmptyProfile(genre, mood);
} else {
const profile = profiles[key];
profile.genre = profile.genre || genre;
profile.mood = profile.mood || mood;
const tempo = profile.tempo || {};
profile.tempo = {
min: Number.isFinite(tempo.min) ? tempo.min : Infinity,
max: Number.isFinite(tempo.max) ? tempo.max : 0,
mean: Number.isFinite(tempo.mean) ? tempo.mean : 0
};
profile.timeSignatures = profile.timeSignatures || {};
profile.keys = profile.keys || {};
profile.chordProgressions = profile.chordProgressions || {};
profile.bassStyles = profile.bassStyles || {};
profile.drumPatterns = profile.drumPatterns || {};
profile.melodicMotifs = profile.melodicMotifs || {};
profile.textures = profile.textures || {};
profile.instruments = profile.instruments || {};
profile.sections = profile.sections || {};
profile.arrangementBlueprints = profile.arrangementBlueprints || {};
}
return profiles[key];
}
function incrementMap(map, value, weight = 1) {
if (!value && value !== 0) return;
const key = Array.isArray(value) ? value.join(" | ") : String(value);
if (!key.trim()) return;
map[key] = (map[key] || 0) + weight;
}
function mergeMany(map, values, weight = 1) {
if (!Array.isArray(values)) return;
for (const value of values) {
incrementMap(map, value, weight);
}
}
function updateTempo(profile, tempo) {
if (!tempo) return;
const min = Number.isFinite(tempo.min) ? tempo.min : (Number.isFinite(tempo.mean) ? tempo.mean : tempo);
const max = Number.isFinite(tempo.max) ? tempo.max : (Number.isFinite(tempo.mean) ? tempo.mean : tempo);
const mean = Number.isFinite(tempo.mean)
? tempo.mean
: Number.isFinite(min) && Number.isFinite(max)
? (min + max) / 2
: Number.isFinite(min)
? min
: Number.isFinite(max)
? max
: null;
if (!Number.isFinite(mean)) return;
profile.tempo.min = Number.isFinite(profile.tempo.min) ? Math.min(profile.tempo.min, min) : min;
profile.tempo.max = Number.isFinite(profile.tempo.max) ? Math.max(profile.tempo.max, max) : max;
profile.tempo.mean += (mean - profile.tempo.mean) / profile.count;
}
function updateSections(profile, arrangement) {
if (!Array.isArray(arrangement)) return;
const blueprint = [];
for (const part of arrangement) {
if (!part) continue;
const name = String(part.section || part.name || part.part || "section").toLowerCase();
const length = Number(part.length || part.bars || part.measures || 8);
if (!name.trim()) continue;
blueprint.push(name);
const info = profile.sections[name] || { count: 0, avgLength: 0 };
info.count += 1;
info.avgLength += (length - info.avgLength) / info.count;
profile.sections[name] = info;
}
if (blueprint.length) {
incrementMap(profile.arrangementBlueprints, blueprint.join(" > "));
}
}
function integrateSample(model, sample) {
const moods = Array.isArray(sample?.moods) && sample.moods.length ? sample.moods : ["default"];
for (const mood of moods) {
const profile = ensureProfile(model.profiles, sample.genre || "unknown", mood);
profile.count += 1;
updateTempo(profile, sample.tempo);
mergeMany(profile.timeSignatures, sample.timeSignatures);
mergeMany(profile.keys, sample.keys);
if (Array.isArray(sample.chordProgressions)) {
for (const progression of sample.chordProgressions) {
incrementMap(profile.chordProgressions, progression);
}
}
mergeMany(profile.bassStyles, sample.bassStyles);
mergeMany(profile.drumPatterns, sample.drumPatterns);
mergeMany(profile.melodicMotifs, sample.melodicMotifs);
mergeMany(profile.textures, sample.textures);
mergeMany(profile.instruments, sample.instruments);
updateSections(profile, sample.arrangement);
}
}
function countMapTotals(mapCollection, field) {
let total = 0;
for (const key of Object.keys(mapCollection)) {
const profile = mapCollection[key];
const map = profile[field] || {};
for (const value of Object.values(map)) total += Number(value) || 0;
}
return total;
}
function serialiseProfiles(profiles) {
const result = {};
for (const [key, profile] of Object.entries(profiles)) {
result[key] = {
...profile,
tempo: {
min: Number.isFinite(profile.tempo.min) ? Number(profile.tempo.min.toFixed(2)) : null,
max: Number.isFinite(profile.tempo.max) ? Number(profile.tempo.max.toFixed(2)) : null,
mean: Number.isFinite(profile.tempo.mean) ? Number(profile.tempo.mean.toFixed(2)) : null
}
};
}
return result;
}
function trainSteps(steps = 100) {
ensureDirs();
const corpus = loadCorpus();
const meta = loadMeta();
const model = loadModel();
if (!Array.isArray(corpus) || corpus.length === 0) {
meta.loss = 1.0;
saveMeta(meta);
return meta;
}
const digest = hashCorpus(corpus);
model.datasetDigest = digest;
model.datasetSize = corpus.length;
for (let i = 0; i < steps; i++) {
const sample = corpus[(meta.step + i) % corpus.length];
integrateSample(model, sample);
meta.step += 1;
const coverage = Object.keys(model.profiles).length || 1;
const harmonicVariety = countMapTotals(model.profiles, "chordProgressions") + 1;
const textureDepth = countMapTotals(model.profiles, "textures") + 1;
const datasetFactor = Math.log1p(model.datasetSize);
const learningGain = Math.log1p(meta.step);
const syntheticLoss = 1 / (datasetFactor + coverage / 2) + 1 / (learningGain + harmonicVariety / 50 + textureDepth / 75);
meta.loss = Number(Math.max(0.0025, syntheticLoss * 0.35).toFixed(6));
if (meta.step % 200 === 0) {
const ck = path.join(CKPT_DIR, `ckpt_step_${meta.step}.json`);
fs.writeFileSync(ck, JSON.stringify({
step: meta.step,
loss: meta.loss,
time: Date.now(),
datasetDigest: digest
}, null, 2));
meta.lastCheckpoint = ck;
}
}
model.updatedAt = new Date().toISOString();
meta.datasetDigest = digest;
meta.model = {
profiles: serialiseProfiles(model.profiles),
datasetSize: model.datasetSize,
updatedAt: model.updatedAt,
datasetDigest: model.datasetDigest
};
saveModel(meta.model);
saveMeta(meta);
return meta;
}
module.exports = {
trainSteps,
loadMeta,
loadModel,
saveModel,
loadCorpus,
DATA_DIR,
CKPT_DIR,
META_FILE,
MODEL_FILE,
CORPUS_FILE
};