@summer_icicle/time-traveler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +320 -0
  3. package/dist/audio/AudioPlayer.d.ts +4 -0
  4. package/dist/audio/AudioPlayer.js +28 -0
  5. package/dist/audio/AudioPlayer.js.map +1 -0
  6. package/dist/audio/Envelope.d.ts +9 -0
  7. package/dist/audio/Envelope.js +29 -0
  8. package/dist/audio/Envelope.js.map +1 -0
  9. package/dist/audio/LowPassFilter.d.ts +5 -0
  10. package/dist/audio/LowPassFilter.js +14 -0
  11. package/dist/audio/LowPassFilter.js.map +1 -0
  12. package/dist/audio/Oscillator.d.ts +8 -0
  13. package/dist/audio/Oscillator.js +42 -0
  14. package/dist/audio/Oscillator.js.map +1 -0
  15. package/dist/audio/StereoBuffer.d.ts +4 -0
  16. package/dist/audio/StereoBuffer.js +2 -0
  17. package/dist/audio/StereoBuffer.js.map +1 -0
  18. package/dist/audio/Synthesizer.d.ts +21 -0
  19. package/dist/audio/Synthesizer.js +186 -0
  20. package/dist/audio/Synthesizer.js.map +1 -0
  21. package/dist/audio/WavEncoder.d.ts +8 -0
  22. package/dist/audio/WavEncoder.js +56 -0
  23. package/dist/audio/WavEncoder.js.map +1 -0
  24. package/dist/cli/commands/analyze.d.ts +2 -0
  25. package/dist/cli/commands/analyze.js +90 -0
  26. package/dist/cli/commands/analyze.js.map +1 -0
  27. package/dist/cli/commands/export.d.ts +2 -0
  28. package/dist/cli/commands/export.js +86 -0
  29. package/dist/cli/commands/export.js.map +1 -0
  30. package/dist/cli/commands/play.d.ts +2 -0
  31. package/dist/cli/commands/play.js +69 -0
  32. package/dist/cli/commands/play.js.map +1 -0
  33. package/dist/git/GitCommit.d.ts +10 -0
  34. package/dist/git/GitCommit.js +2 -0
  35. package/dist/git/GitCommit.js.map +1 -0
  36. package/dist/git/GitHistoryAnalyzer.d.ts +7 -0
  37. package/dist/git/GitHistoryAnalyzer.js +97 -0
  38. package/dist/git/GitHistoryAnalyzer.js.map +1 -0
  39. package/dist/git/GitHistoryParser.d.ts +7 -0
  40. package/dist/git/GitHistoryParser.js +97 -0
  41. package/dist/git/GitHistoryParser.js.map +1 -0
  42. package/dist/git/GitHistoryStats.d.ts +15 -0
  43. package/dist/git/GitHistoryStats.js +2 -0
  44. package/dist/git/GitHistoryStats.js.map +1 -0
  45. package/dist/git/GitRepository.d.ts +10 -0
  46. package/dist/git/GitRepository.js +115 -0
  47. package/dist/git/GitRepository.js.map +1 -0
  48. package/dist/index.d.ts +2 -0
  49. package/dist/index.js +15 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/music/MusicComposer.d.ts +13 -0
  52. package/dist/music/MusicComposer.js +257 -0
  53. package/dist/music/MusicComposer.js.map +1 -0
  54. package/dist/music/MusicEvent.d.ts +11 -0
  55. package/dist/music/MusicEvent.js +2 -0
  56. package/dist/music/MusicEvent.js.map +1 -0
  57. package/dist/music/MusicMapper.d.ts +9 -0
  58. package/dist/music/MusicMapper.js +67 -0
  59. package/dist/music/MusicMapper.js.map +1 -0
  60. package/dist/music/MusicProfile.d.ts +11 -0
  61. package/dist/music/MusicProfile.js +2 -0
  62. package/dist/music/MusicProfile.js.map +1 -0
  63. package/dist/music/midi.d.ts +2 -0
  64. package/dist/music/midi.js +26 -0
  65. package/dist/music/midi.js.map +1 -0
  66. package/dist/music/normalize.d.ts +1 -0
  67. package/dist/music/normalize.js +8 -0
  68. package/dist/music/normalize.js.map +1 -0
  69. package/dist/music/notes.d.ts +1 -0
  70. package/dist/music/notes.js +24 -0
  71. package/dist/music/notes.js.map +1 -0
  72. package/dist/music/scales.d.ts +2 -0
  73. package/dist/music/scales.js +7 -0
  74. package/dist/music/scales.js.map +1 -0
  75. package/package.json +59 -0
@@ -0,0 +1,186 @@
1
+ import { Oscillator } from "./Oscillator.js";
2
+ import { Envelope } from "./Envelope.js";
3
+ import { LowPassFilter } from "./LowPassFilter.js";
4
+ export class Synthesizer {
5
+ oscillator = new Oscillator();
6
+ envelope = new Envelope();
7
+ render(events, options = {
8
+ sampleRate: 44100
9
+ }) {
10
+ if (events.length === 0) {
11
+ return {
12
+ left: new Float32Array(),
13
+ right: new Float32Array()
14
+ };
15
+ }
16
+ const totalDuration = Math.max(...events.map(event => event.startTime +
17
+ event.duration));
18
+ const totalSamples = Math.ceil(totalDuration *
19
+ options.sampleRate);
20
+ const left = new Float32Array(totalSamples);
21
+ const right = new Float32Array(totalSamples);
22
+ for (const event of events) {
23
+ this.renderEvent(left, right, event, options.sampleRate);
24
+ }
25
+ this.applyMasterGain(left, right);
26
+ this.softClip(left);
27
+ this.softClip(right);
28
+ return {
29
+ left,
30
+ right
31
+ };
32
+ }
33
+ renderEvent(left, right, event, sampleRate) {
34
+ const startSample = Math.floor(event.startTime *
35
+ sampleRate);
36
+ const eventSamples = Math.ceil(event.duration *
37
+ sampleRate);
38
+ const envelopeSettings = this.getEnvelopeForLayer(event.layer);
39
+ const cutoffFrequency = this.getCutoffFrequency(event.layer);
40
+ const leftFilter = new LowPassFilter();
41
+ const rightFilter = new LowPassFilter();
42
+ const pan = this.getPan(event);
43
+ const { leftGain, rightGain } = this.calculatePanGains(pan);
44
+ for (let index = 0; index < eventSamples; index += 1) {
45
+ const outputIndex = startSample +
46
+ index;
47
+ if (outputIndex >=
48
+ left.length) {
49
+ break;
50
+ }
51
+ const time = index /
52
+ sampleRate;
53
+ const timbreSample = this.getTimbreSample(event, time);
54
+ const filteredLeft = leftFilter.process(timbreSample, cutoffFrequency, sampleRate);
55
+ const filteredRight = rightFilter.process(timbreSample, cutoffFrequency, sampleRate);
56
+ const envelopeAmplitude = this.envelope.amplitude(time, event.duration, envelopeSettings);
57
+ const layerGain = this.getLayerGain(event.layer);
58
+ const sample = event.amplitude *
59
+ envelopeAmplitude *
60
+ layerGain;
61
+ left[outputIndex] +=
62
+ filteredLeft *
63
+ sample *
64
+ leftGain;
65
+ right[outputIndex] +=
66
+ filteredRight *
67
+ sample *
68
+ rightGain;
69
+ }
70
+ }
71
+ getTimbreSample(event, time) {
72
+ switch (event.layer) {
73
+ case "melody": {
74
+ const primary = this.oscillator.sample(event.waveform, event.frequency, time);
75
+ const sine = this.oscillator.sample("sine", event.frequency, time);
76
+ return (primary * 0.7 +
77
+ sine * 0.3);
78
+ }
79
+ case "bass": {
80
+ const sine = this.oscillator.sample("sine", event.frequency, time);
81
+ const triangle = this.oscillator.sample("triangle", event.frequency, time);
82
+ return (sine * 0.85 +
83
+ triangle * 0.15);
84
+ }
85
+ case "pad": {
86
+ const sine = this.oscillator.sample("sine", event.frequency, time);
87
+ const triangle = this.oscillator.sample("triangle", event.frequency, time);
88
+ return (sine * 0.6 +
89
+ triangle * 0.4);
90
+ }
91
+ }
92
+ }
93
+ getPan(event) {
94
+ switch (event.layer) {
95
+ case "bass":
96
+ return 0;
97
+ case "melody":
98
+ return this.getMelodyPan(event.frequency);
99
+ case "pad":
100
+ return this.getPadPan(event.frequency);
101
+ }
102
+ }
103
+ getMelodyPan(frequency) {
104
+ const normalized = Math.min(1, Math.max(0, (frequency - 130) /
105
+ (1046 - 130)));
106
+ return (normalized * 0.8 -
107
+ 0.4);
108
+ }
109
+ getPadPan(frequency) {
110
+ const normalized = Math.min(1, Math.max(0, (frequency - 130) /
111
+ (1046 - 130)));
112
+ return (normalized * 1.2 -
113
+ 0.6);
114
+ }
115
+ calculatePanGains(pan) {
116
+ const clampedPan = Math.min(1, Math.max(-1, pan));
117
+ const angle = (clampedPan + 1) *
118
+ Math.PI /
119
+ 4;
120
+ return {
121
+ leftGain: Math.cos(angle),
122
+ rightGain: Math.sin(angle)
123
+ };
124
+ }
125
+ getCutoffFrequency(layer) {
126
+ switch (layer) {
127
+ case "melody":
128
+ return 12000;
129
+ case "bass":
130
+ return 1800;
131
+ case "pad":
132
+ return 2500;
133
+ }
134
+ }
135
+ getEnvelopeForLayer(layer) {
136
+ switch (layer) {
137
+ case "melody":
138
+ return {
139
+ attack: 0.02,
140
+ decay: 0.12,
141
+ sustain: 0.6,
142
+ release: 0.2
143
+ };
144
+ case "bass":
145
+ return {
146
+ attack: 0.08,
147
+ decay: 0.2,
148
+ sustain: 0.8,
149
+ release: 0.35
150
+ };
151
+ case "pad":
152
+ return {
153
+ attack: 0.35,
154
+ decay: 0.25,
155
+ sustain: 0.65,
156
+ release: 0.6
157
+ };
158
+ }
159
+ }
160
+ getLayerGain(layer) {
161
+ switch (layer) {
162
+ case "melody":
163
+ return 0.9;
164
+ case "bass":
165
+ return 0.7;
166
+ case "pad":
167
+ return 0.3;
168
+ }
169
+ }
170
+ applyMasterGain(left, right) {
171
+ const masterGain = 0.85;
172
+ for (let index = 0; index < left.length; index += 1) {
173
+ left[index] *=
174
+ masterGain;
175
+ right[index] *=
176
+ masterGain;
177
+ }
178
+ }
179
+ softClip(samples) {
180
+ for (let index = 0; index < samples.length; index += 1) {
181
+ samples[index] =
182
+ Math.tanh(samples[index]);
183
+ }
184
+ }
185
+ }
186
+ //# sourceMappingURL=Synthesizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Synthesizer.js","sourceRoot":"","sources":["../../src/audio/Synthesizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { StereoBuffer } from "./StereoBuffer.js";
2
+ export interface WavEncoderOptions {
3
+ sampleRate: number;
4
+ }
5
+ export declare class WavEncoder {
6
+ encode(stereo: StereoBuffer, options?: WavEncoderOptions): Buffer;
7
+ private floatToInt16;
8
+ }
@@ -0,0 +1,56 @@
1
+ export class WavEncoder {
2
+ encode(stereo, options = {
3
+ sampleRate: 44100
4
+ }) {
5
+ if (stereo.left.length !==
6
+ stereo.right.length) {
7
+ throw new Error("Left and right channels must have the same length.");
8
+ }
9
+ const channels = 2;
10
+ const bitsPerSample = 16;
11
+ const bytesPerSample = bitsPerSample / 8;
12
+ const frameCount = stereo.left.length;
13
+ const dataSize = frameCount *
14
+ channels *
15
+ bytesPerSample;
16
+ const buffer = Buffer.alloc(44 + dataSize);
17
+ buffer.write("RIFF", 0);
18
+ buffer.writeUInt32LE(36 + dataSize, 4);
19
+ buffer.write("WAVE", 8);
20
+ buffer.write("fmt ", 12);
21
+ buffer.writeUInt32LE(16, 16);
22
+ buffer.writeUInt16LE(1, 20);
23
+ buffer.writeUInt16LE(channels, 22);
24
+ buffer.writeUInt32LE(options.sampleRate, 24);
25
+ const byteRate = options.sampleRate *
26
+ channels *
27
+ bytesPerSample;
28
+ buffer.writeUInt32LE(byteRate, 28);
29
+ const blockAlign = channels *
30
+ bytesPerSample;
31
+ buffer.writeUInt16LE(blockAlign, 32);
32
+ buffer.writeUInt16LE(bitsPerSample, 34);
33
+ buffer.write("data", 36);
34
+ buffer.writeUInt32LE(dataSize, 40);
35
+ let offset = 44;
36
+ for (let index = 0; index < frameCount; index += 1) {
37
+ const leftSample = this.floatToInt16(stereo.left[index] ?? 0);
38
+ const rightSample = this.floatToInt16(stereo.right[index] ?? 0);
39
+ buffer.writeInt16LE(leftSample, offset);
40
+ offset += 2;
41
+ buffer.writeInt16LE(rightSample, offset);
42
+ offset += 2;
43
+ }
44
+ return buffer;
45
+ }
46
+ floatToInt16(sample) {
47
+ const clamped = Math.max(-1, Math.min(1, sample));
48
+ if (clamped < 0) {
49
+ return Math.round(clamped *
50
+ 32768);
51
+ }
52
+ return Math.round(clamped *
53
+ 32767);
54
+ }
55
+ }
56
+ //# sourceMappingURL=WavEncoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WavEncoder.js","sourceRoot":"","sources":["../../src/audio/WavEncoder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerAnalyzeCommand(program: Command): void;
@@ -0,0 +1,90 @@
1
+ import { GitRepository } from "../../git/GitRepository.js";
2
+ import { GitHistoryParser } from "../../git/GitHistoryParser.js";
3
+ import { GitHistoryAnalyzer } from "../../git/GitHistoryAnalyzer.js";
4
+ import { MusicMapper } from "../../music/MusicMapper.js";
5
+ export function registerAnalyzeCommand(program) {
6
+ program
7
+ .command("analyze")
8
+ .description("Analyze the Git repository and display its musical profile")
9
+ .option("-b, --branch <branch>", "Analyze a specific Git branch")
10
+ .action(async (options) => {
11
+ const repository = new GitRepository(process.cwd());
12
+ const historyParser = new GitHistoryParser();
13
+ const historyAnalyzer = new GitHistoryAnalyzer();
14
+ const musicMapper = new MusicMapper();
15
+ const isRepository = await repository.isRepository();
16
+ if (!isRepository) {
17
+ console.error("Time Traveler must be run inside a Git repository.");
18
+ process.exitCode = 1;
19
+ return;
20
+ }
21
+ const hasCommits = await repository.hasCommits();
22
+ if (!hasCommits) {
23
+ console.error("Time Traveler cannot process a repository with no commits.");
24
+ process.exitCode = 1;
25
+ return;
26
+ }
27
+ const requestedBranch = options.branch;
28
+ if (requestedBranch !== undefined) {
29
+ const exists = await repository.branchExists(requestedBranch);
30
+ if (!exists) {
31
+ console.error(`Branch "${requestedBranch}" does not exist.`);
32
+ process.exitCode = 1;
33
+ return;
34
+ }
35
+ }
36
+ const branch = requestedBranch ??
37
+ await repository.getCurrentBranch();
38
+ const historyStream = repository.streamHistory(requestedBranch);
39
+ const commits = await historyParser.parseStream(historyStream);
40
+ if (commits.length === 0) {
41
+ console.error("Time Traveler could not find any commits in this repository.");
42
+ process.exitCode = 1;
43
+ return;
44
+ }
45
+ const stats = historyAnalyzer.analyze(commits);
46
+ const musicProfile = musicMapper.createProfile(stats);
47
+ console.log();
48
+ console.log("Time Traveler");
49
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
50
+ console.log();
51
+ console.log("Repository");
52
+ console.log(` Branch: ${branch}`);
53
+ console.log(` Commits: ${stats.commitCount}`);
54
+ console.log(` Age: ${stats.repositoryAgeDays.toFixed(1)} days`);
55
+ console.log();
56
+ console.log("Code Activity");
57
+ console.log(` Lines added: ${stats.totalInsertions.toLocaleString()}`);
58
+ console.log(` Lines deleted: ${stats.totalDeletions.toLocaleString()}`);
59
+ console.log(` Total lines changed: ${stats.totalLinesChanged.toLocaleString()}`);
60
+ console.log(` Files changed: ${stats.totalFilesChanged.toLocaleString()}`);
61
+ console.log(` Churn ratio: ${(stats.churnRatio * 100).toFixed(1)}%`);
62
+ console.log();
63
+ console.log("Commit Activity");
64
+ console.log(` Commits/day: ${stats.commitsPerDay.toFixed(2)}`);
65
+ console.log(` Avg lines/commit: ${stats.averageLinesChangedPerCommit.toFixed(1)}`);
66
+ console.log(` Busiest hour: ${formatHour(stats.busiestHour)}`);
67
+ console.log();
68
+ console.log("Musical Profile");
69
+ console.log(` Tempo: ${musicProfile.tempo} BPM`);
70
+ console.log(` Root note: ${musicProfile.rootNote}`);
71
+ console.log(` Scale: ${musicProfile.scale}`);
72
+ console.log(` Waveform: ${musicProfile.waveform}`);
73
+ console.log(` Intensity: ${musicProfile.intensity.toFixed(2)}`);
74
+ console.log(` Dissonance: ${musicProfile.dissonance.toFixed(2)}`);
75
+ console.log(` Note density: ${musicProfile.noteDensity.toFixed(2)}`);
76
+ });
77
+ }
78
+ function formatHour(hour) {
79
+ if (hour === null) {
80
+ return "N/A";
81
+ }
82
+ const suffix = hour >= 12
83
+ ? "PM"
84
+ : "AM";
85
+ const displayHour = hour % 12 === 0
86
+ ? 12
87
+ : hour % 12;
88
+ return `${displayHour}:00 ${suffix}`;
89
+ }
90
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../../src/cli/commands/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerExportCommand(program: Command): void;
@@ -0,0 +1,86 @@
1
+ import path from "node:path";
2
+ import { writeFile } from "node:fs/promises";
3
+ import { GitRepository } from "../../git/GitRepository.js";
4
+ import { GitHistoryParser } from "../../git/GitHistoryParser.js";
5
+ import { GitHistoryAnalyzer } from "../../git/GitHistoryAnalyzer.js";
6
+ import { MusicMapper } from "../../music/MusicMapper.js";
7
+ import { MusicComposer } from "../../music/MusicComposer.js";
8
+ import { Synthesizer } from "../../audio/Synthesizer.js";
9
+ import { WavEncoder } from "../../audio/WavEncoder.js";
10
+ export function registerExportCommand(program) {
11
+ program
12
+ .command("export")
13
+ .description("Generate a WAV soundscape from the current Git repository")
14
+ .option("-o, --output <file>", "Output WAV filename")
15
+ .option("-b, --branch <branch>", "Export a specific Git branch")
16
+ .action(async (options) => {
17
+ const repository = new GitRepository(process.cwd());
18
+ const historyParser = new GitHistoryParser();
19
+ const historyAnalyzer = new GitHistoryAnalyzer();
20
+ const musicMapper = new MusicMapper();
21
+ const musicComposer = new MusicComposer();
22
+ const synthesizer = new Synthesizer();
23
+ const wavEncoder = new WavEncoder();
24
+ const isRepository = await repository.isRepository();
25
+ if (!isRepository) {
26
+ console.error("Time Traveler must be run inside a Git repository.");
27
+ process.exitCode = 1;
28
+ return;
29
+ }
30
+ const hasCommits = await repository.hasCommits();
31
+ if (!hasCommits) {
32
+ console.error("Time Traveler cannot process a repository with no commits.");
33
+ process.exitCode = 1;
34
+ return;
35
+ }
36
+ const requestedBranch = options.branch;
37
+ if (requestedBranch !== undefined) {
38
+ const exists = await repository.branchExists(requestedBranch);
39
+ if (!exists) {
40
+ console.error(`Branch "${requestedBranch}" does not exist.`);
41
+ process.exitCode = 1;
42
+ return;
43
+ }
44
+ }
45
+ const branch = requestedBranch ??
46
+ await repository.getCurrentBranch();
47
+ const historyStream = repository.streamHistory(requestedBranch);
48
+ const commits = await historyParser.parseStream(historyStream);
49
+ if (commits.length === 0) {
50
+ console.error("Time Traveler could not find any commits in this repository.");
51
+ process.exitCode = 1;
52
+ return;
53
+ }
54
+ const latestCommit = commits.reduce((latest, commit) => commit.date >
55
+ latest.date
56
+ ? commit
57
+ : latest);
58
+ const stats = historyAnalyzer.analyze(commits);
59
+ const musicProfile = musicMapper.createProfile(stats);
60
+ const musicEvents = musicComposer.compose(musicProfile, commits);
61
+ const melodyCount = musicEvents.filter(event => event.layer ===
62
+ "melody").length;
63
+ const bassCount = musicEvents.filter(event => event.layer ===
64
+ "bass").length;
65
+ const padCount = musicEvents.filter(event => event.layer ===
66
+ "pad").length;
67
+ console.log();
68
+ console.log("Composition");
69
+ console.log(` Branch: ${branch}`);
70
+ console.log(` Total events: ${musicEvents.length}`);
71
+ console.log(` Melody: ${melodyCount}`);
72
+ console.log(` Bass: ${bassCount}`);
73
+ console.log(` Pad: ${padCount}`);
74
+ const stereo = synthesizer.render(musicEvents);
75
+ const wav = wavEncoder.encode(stereo);
76
+ const shortHash = latestCommit.hash.slice(0, 7);
77
+ const repositoryName = path.basename(process.cwd());
78
+ const safeBranchName = branch.replace(/[^a-zA-Z0-9._-]/g, "-");
79
+ const outputFileName = options.output ??
80
+ `${repositoryName}-${safeBranchName}-${shortHash}.wav`;
81
+ await writeFile(outputFileName, wav);
82
+ console.log();
83
+ console.log(`Audio written to ${outputFileName}`);
84
+ });
85
+ }
86
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerPlayCommand(program: Command): void;
@@ -0,0 +1,69 @@
1
+ import path from "node:path";
2
+ import os from "node:os";
3
+ import { writeFile } from "node:fs/promises";
4
+ import { GitRepository } from "../../git/GitRepository.js";
5
+ import { GitHistoryParser } from "../../git/GitHistoryParser.js";
6
+ import { GitHistoryAnalyzer } from "../../git/GitHistoryAnalyzer.js";
7
+ import { MusicMapper } from "../../music/MusicMapper.js";
8
+ import { MusicComposer } from "../../music/MusicComposer.js";
9
+ import { Synthesizer } from "../../audio/Synthesizer.js";
10
+ import { WavEncoder } from "../../audio/WavEncoder.js";
11
+ import { AudioPlayer } from "../../audio/AudioPlayer.js";
12
+ export function registerPlayCommand(program) {
13
+ program
14
+ .command("play")
15
+ .description("Generate and play a soundscape from the current Git repository")
16
+ .option("-b, --branch <branch>", "Play a specific Git branch")
17
+ .action(async (options) => {
18
+ const repository = new GitRepository(process.cwd());
19
+ const historyParser = new GitHistoryParser();
20
+ const historyAnalyzer = new GitHistoryAnalyzer();
21
+ const musicMapper = new MusicMapper();
22
+ const musicComposer = new MusicComposer();
23
+ const synthesizer = new Synthesizer();
24
+ const wavEncoder = new WavEncoder();
25
+ const audioPlayer = new AudioPlayer();
26
+ const isRepository = await repository.isRepository();
27
+ if (!isRepository) {
28
+ console.error("Time Traveler must be run inside a Git repository.");
29
+ process.exitCode = 1;
30
+ return;
31
+ }
32
+ const hasCommits = await repository.hasCommits();
33
+ if (!hasCommits) {
34
+ console.error("Time Traveler cannot process a repository with no commits.");
35
+ process.exitCode = 1;
36
+ return;
37
+ }
38
+ const requestedBranch = options.branch;
39
+ if (requestedBranch !== undefined) {
40
+ const exists = await repository.branchExists(requestedBranch);
41
+ if (!exists) {
42
+ console.error(`Branch "${requestedBranch}" does not exist.`);
43
+ process.exitCode = 1;
44
+ return;
45
+ }
46
+ }
47
+ const branch = requestedBranch ??
48
+ await repository.getCurrentBranch();
49
+ const historyStream = repository.streamHistory(requestedBranch);
50
+ const commits = await historyParser.parseStream(historyStream);
51
+ if (commits.length === 0) {
52
+ console.error("Time Traveler could not find any commits in this repository.");
53
+ process.exitCode = 1;
54
+ return;
55
+ }
56
+ console.log();
57
+ console.log(`Generating soundscape for ${branch}...`);
58
+ const stats = historyAnalyzer.analyze(commits);
59
+ const musicProfile = musicMapper.createProfile(stats);
60
+ const musicEvents = musicComposer.compose(musicProfile, commits);
61
+ const stereo = synthesizer.render(musicEvents);
62
+ const wav = wavEncoder.encode(stereo);
63
+ const temporaryFile = path.join(os.tmpdir(), `time-traveler-${process.pid}.wav`);
64
+ await writeFile(temporaryFile, wav);
65
+ console.log(`Playing ${commits.length} commits from ${branch}`);
66
+ await audioPlayer.play(temporaryFile);
67
+ });
68
+ }
69
+ //# sourceMappingURL=play.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"play.js","sourceRoot":"","sources":["../../../src/cli/commands/play.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface GitCommit {
2
+ hash: string;
3
+ authorName: string;
4
+ authorEmail: string;
5
+ date: Date;
6
+ message: string;
7
+ insertions: number;
8
+ deletions: number;
9
+ filesChanged: number;
10
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=GitCommit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitCommit.js","sourceRoot":"","sources":["../../src/git/GitCommit.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import type { GitCommit } from "./GitCommit.js";
2
+ import type { GitHistoryStats } from "./GitHistoryStats.js";
3
+ export declare class GitHistoryAnalyzer {
4
+ analyze(commits: GitCommit[]): GitHistoryStats;
5
+ private calculateRepositoryAgeDays;
6
+ private findBusiestHour;
7
+ }