@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
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 amanogaw01
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,320 @@
1
+ # Time Traveler
2
+
3
+ [![CI](https://github.com/amanogawa01/time-traveler/actions/workflows/ci.yml/badge.svg)](https://github.com/amanogawa01/time-traveler/actions/workflows/ci.yml)
4
+
5
+ Turn your Git history into music.
6
+
7
+ Time Traveler is a command-line tool that analyzes the history of a Git repository and transforms its development activity into a deterministic musical soundscape.
8
+
9
+ Instead of generating music with AI or randomly assigning notes, Time Traveler maps real repository data — commits, code changes, file activity, timestamps, and development patterns — directly to musical properties.
10
+
11
+ ## Features
12
+
13
+ - Analyze the history of any local Git repository
14
+ - Convert repository activity into a musical profile
15
+ - Generate melody, bass, and pad layers
16
+ - Export the result as a stereo WAV file
17
+ - Play generated soundscapes directly from the CLI
18
+ - Analyze or export specific Git branches
19
+ - Handle repositories ranging from a few commits to more than a million
20
+ - Stream large Git histories without loading the entire raw Git log into memory
21
+ - Adaptively aggregate enormous repositories while preserving their overall history
22
+ - Produce deterministic output from repository data
23
+ - No AI-generated music
24
+ - No external audio libraries required for synthesis
25
+
26
+ ## How It Works
27
+
28
+ Time Traveler reads Git history and maps development activity to musical characteristics.
29
+
30
+ Repository-wide statistics influence the overall musical profile:
31
+
32
+ | Git data | Musical property |
33
+ | --- | --- |
34
+ | Commits per day | Tempo |
35
+ | Average lines changed | Intensity |
36
+ | Deletion/churn ratio | Dissonance and scale |
37
+ | Average files changed | Note density |
38
+ | Busiest commit hour | Root note |
39
+ | Code-change intensity | Waveform |
40
+
41
+ Individual commits influence the composition:
42
+
43
+ | Commit data | Musical property |
44
+ | --- | --- |
45
+ | Insertions and files changed | Scale degree |
46
+ | Deletion ratio | Harmonic tension |
47
+ | Files changed | Note duration |
48
+ | Commit size | Amplitude |
49
+ | Commit time | Octave |
50
+ | Time between commits | Event spacing |
51
+
52
+ Each musical commit produces five events:
53
+
54
+ - 1 melody voice
55
+ - 1 bass voice
56
+ - 3 pad voices forming a chord
57
+
58
+ The synthesizer then renders those events into stereo PCM audio and writes a WAV file.
59
+
60
+ ## Large Repositories
61
+
62
+ Repositories with up to 1,000 commits are represented commit-by-commit.
63
+
64
+ For larger histories, Time Traveler automatically groups neighboring commits into chronological buckets. Each bucket combines the activity of the commits it represents.
65
+
66
+ This prevents extremely large repositories from producing millions of simultaneous music events while still representing the complete repository history.
67
+
68
+ Time Traveler has been stress-tested against repositories including:
69
+
70
+ - Linux kernel — more than 1.4 million commits processed
71
+ - LLVM — more than 590,000 commits processed
72
+
73
+ ## Requirements
74
+
75
+ - Node.js 20 or newer
76
+ - Git
77
+
78
+ ## Installation
79
+
80
+ Clone the repository:
81
+
82
+ ```bash
83
+ git clone https://github.com/amanogawa01/time-traveler.git
84
+ cd time-traveler
85
+ ```
86
+
87
+ Install dependencies:
88
+
89
+ ```bash
90
+ npm install
91
+ ```
92
+
93
+ Build the project:
94
+
95
+ ```bash
96
+ npm run build
97
+ ```
98
+
99
+ Link the CLI globally:
100
+
101
+ ```bash
102
+ npm link
103
+ ```
104
+
105
+ You can then use `time-traveler` from inside any Git repository.
106
+
107
+ ## Usage
108
+
109
+ ### Analyze a repository
110
+
111
+ Run:
112
+
113
+ ```bash
114
+ time-traveler analyze
115
+ ```
116
+
117
+ Example output:
118
+
119
+ ```text
120
+ Time Traveler
121
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
122
+
123
+ Repository
124
+ Branch: main
125
+ Commits: 18
126
+ Age: 5.1 days
127
+
128
+ Code Activity
129
+ Lines added: 7,279
130
+ Lines deleted: 1,142
131
+ Total lines changed: 8,421
132
+ Files changed: 86
133
+ Churn ratio: 13.6%
134
+
135
+ Commit Activity
136
+ Commits/day: 3.50
137
+ Avg lines/commit: 467.8
138
+ Busiest hour: 3:00 PM
139
+
140
+ Musical Profile
141
+ Tempo: 85 BPM
142
+ Root note: G
143
+ Scale: major
144
+ Waveform: triangle
145
+ Intensity: 0.47
146
+ Dissonance: 0.14
147
+ Note density: 0.24
148
+ ```
149
+
150
+ ### Export a WAV file
151
+
152
+ ```bash
153
+ time-traveler export
154
+ ```
155
+
156
+ The default filename contains the repository name, branch, and latest commit hash:
157
+
158
+ ```text
159
+ time-traveler-main-abcdef1.wav
160
+ ```
161
+
162
+ Specify a custom filename with:
163
+
164
+ ```bash
165
+ time-traveler export --output my-history.wav
166
+ ```
167
+
168
+ or:
169
+
170
+ ```bash
171
+ time-traveler export -o my-history.wav
172
+ ```
173
+
174
+ ### Play the repository
175
+
176
+ ```bash
177
+ time-traveler play
178
+ ```
179
+
180
+ Time Traveler generates the composition and opens it using the operating system's default audio player.
181
+
182
+ ### Use a specific branch
183
+
184
+ All repository commands support a local branch with `--branch` or `-b`.
185
+
186
+ ```bash
187
+ time-traveler analyze --branch develop
188
+ ```
189
+
190
+ ```bash
191
+ time-traveler export --branch develop
192
+ ```
193
+
194
+ ```bash
195
+ time-traveler play --branch develop
196
+ ```
197
+
198
+ ## Development
199
+
200
+ Run the CLI directly from TypeScript:
201
+
202
+ ```bash
203
+ npm run dev -- analyze
204
+ ```
205
+
206
+ Type-check the project:
207
+
208
+ ```bash
209
+ npm run typecheck
210
+ ```
211
+
212
+ Run the test suite:
213
+
214
+ ```bash
215
+ npm test
216
+ ```
217
+
218
+ Build:
219
+
220
+ ```bash
221
+ npm run build
222
+ ```
223
+
224
+ ## Testing
225
+
226
+ Time Traveler includes unit and integration coverage for:
227
+
228
+ - Git history parsing
229
+ - Real temporary Git repositories
230
+ - Repository and branch detection
231
+ - Streaming Git history
232
+ - Musical profile mapping
233
+ - Layered composition
234
+ - Large-history aggregation
235
+ - Deterministic composition
236
+ - WAV encoding and stereo PCM output
237
+
238
+ The project is also manually stress-tested against very large real-world Git repositories.
239
+
240
+ GitHub Actions automatically runs type checking, tests, and the production build on pushes and pull requests.
241
+
242
+ ## Architecture
243
+
244
+ ```text
245
+ Git Repository
246
+
247
+
248
+ GitRepository
249
+
250
+
251
+ GitHistoryParser
252
+
253
+
254
+ GitCommit[]
255
+
256
+ ├──────────────► GitHistoryAnalyzer
257
+ │ │
258
+ │ ▼
259
+ │ GitHistoryStats
260
+ │ │
261
+ │ ▼
262
+ │ MusicMapper
263
+ │ │
264
+ │ ▼
265
+ │ MusicProfile
266
+
267
+
268
+ MusicComposer
269
+
270
+
271
+ MusicEvent[]
272
+
273
+
274
+ Synthesizer
275
+
276
+
277
+ StereoBuffer
278
+
279
+
280
+ WavEncoder
281
+
282
+
283
+ WAV file
284
+ ```
285
+
286
+ ## Audio Engine
287
+
288
+ Time Traveler implements its own lightweight synthesis pipeline.
289
+
290
+ The generated soundscape uses:
291
+
292
+ - melody, bass, and pad layers
293
+ - ADSR envelopes
294
+ - sine, triangle, square, and sawtooth waveforms
295
+ - layer-specific timbre blending
296
+ - low-pass filtering
297
+ - stereo panning
298
+ - equal-power stereo mixing
299
+ - soft clipping
300
+ - 44.1 kHz 16-bit stereo PCM WAV output
301
+
302
+ ## Philosophy
303
+
304
+ Time Traveler is not intended to make Git history sound like a conventional song.
305
+
306
+ The goal is to create an audible representation of how a repository developed.
307
+
308
+ Two repositories with different development patterns should produce different musical profiles, while running Time Traveler repeatedly on the same history should produce the same composition.
309
+
310
+ The repository itself is the score.
311
+
312
+ ## Contributing
313
+
314
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
315
+
316
+ Please also read the [Code of Conduct](CODE_OF_CONDUCT.md) and [Security Policy](SECURITY.md).
317
+
318
+ ## License
319
+
320
+ Time Traveler is released under the MIT License. See [LICENSE](LICENSE).
@@ -0,0 +1,4 @@
1
+ export declare class AudioPlayer {
2
+ play(filePath: string): Promise<void>;
3
+ private playDetached;
4
+ }
@@ -0,0 +1,28 @@
1
+ import { spawn } from "node:child_process";
2
+ import { platform } from "node:os";
3
+ export class AudioPlayer {
4
+ async play(filePath) {
5
+ const currentPlatform = platform();
6
+ switch (currentPlatform) {
7
+ case "win32":
8
+ this.playDetached("explorer.exe", [filePath]);
9
+ return;
10
+ case "darwin":
11
+ this.playDetached("open", [filePath]);
12
+ return;
13
+ case "linux":
14
+ this.playDetached("xdg-open", [filePath]);
15
+ return;
16
+ default:
17
+ throw new Error(`Unsupported platform: ${currentPlatform}`);
18
+ }
19
+ }
20
+ playDetached(command, args) {
21
+ const player = spawn(command, args, {
22
+ detached: true,
23
+ stdio: "ignore"
24
+ });
25
+ player.unref();
26
+ }
27
+ }
28
+ //# sourceMappingURL=AudioPlayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioPlayer.js","sourceRoot":"","sources":["../../src/audio/AudioPlayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,MAAM,OAAO,WAAW;IACf,KAAK,CAAC,IAAI,CACf,QAAgB;QAEhB,MAAM,eAAe,GACnB,QAAQ,EAAE,CAAC;QAEb,QAAQ,eAAe,EAAE,CAAC;YACxB,KAAK,OAAO;gBACV,IAAI,CAAC,YAAY,CACf,cAAc,EACd,CAAC,QAAQ,CAAC,CACX,CAAC;gBACF,OAAO;YAET,KAAK,QAAQ;gBACX,IAAI,CAAC,YAAY,CACf,MAAM,EACN,CAAC,QAAQ,CAAC,CACX,CAAC;gBACF,OAAO;YAET,KAAK,OAAO;gBACV,IAAI,CAAC,YAAY,CACf,UAAU,EACV,CAAC,QAAQ,CAAC,CACX,CAAC;gBACF,OAAO;YAET;gBACE,MAAM,IAAI,KAAK,CACb,yBAAyB,eAAe,EAAE,CAC3C,CAAC;QACN,CAAC;IACH,CAAC;IAEO,YAAY,CAClB,OAAe,EACf,IAAc;QAEd,MAAM,MAAM,GACV,KAAK,CACH,OAAO,EACP,IAAI,EACJ;YACE,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,QAAQ;SAChB,CACF,CAAC;QAEJ,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ export interface EnvelopeSettings {
2
+ attack: number;
3
+ decay: number;
4
+ sustain: number;
5
+ release: number;
6
+ }
7
+ export declare class Envelope {
8
+ amplitude(time: number, duration: number, settings: EnvelopeSettings): number;
9
+ }
@@ -0,0 +1,29 @@
1
+ export class Envelope {
2
+ amplitude(time, duration, settings) {
3
+ const { attack, decay, sustain, release } = settings;
4
+ if (time < 0) {
5
+ return 0;
6
+ }
7
+ if (time < attack) {
8
+ return time / attack;
9
+ }
10
+ if (time < attack + decay) {
11
+ const decayProgress = (time - attack) / decay;
12
+ return (1 -
13
+ decayProgress *
14
+ (1 - sustain));
15
+ }
16
+ const releaseStart = Math.max(attack + decay, duration - release);
17
+ if (time < releaseStart) {
18
+ return sustain;
19
+ }
20
+ if (time < duration) {
21
+ const releaseProgress = (time - releaseStart) /
22
+ Math.max(duration - releaseStart, 0.0001);
23
+ return (sustain *
24
+ (1 - releaseProgress));
25
+ }
26
+ return 0;
27
+ }
28
+ }
29
+ //# sourceMappingURL=Envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Envelope.js","sourceRoot":"","sources":["../../src/audio/Envelope.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,QAAQ;IACZ,SAAS,CACd,IAAY,EACZ,QAAgB,EAChB,QAA0B;QAE1B,MAAM,EACJ,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACR,GAAG,QAAQ,CAAC;QAEb,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,OAAO,CAAC,CAAC;QACX,CAAC;QAED,IAAI,IAAI,GAAG,MAAM,EAAE,CAAC;YAClB,OAAO,IAAI,GAAG,MAAM,CAAC;QACvB,CAAC;QAED,IAAI,IAAI,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC;YAC1B,MAAM,aAAa,GACjB,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC;YAE1B,OAAO,CACL,CAAC;gBACD,aAAa;oBACX,CAAC,CAAC,GAAG,OAAO,CAAC,CAChB,CAAC;QACJ,CAAC;QAED,MAAM,YAAY,GAChB,IAAI,CAAC,GAAG,CACN,MAAM,GAAG,KAAK,EACd,QAAQ,GAAG,OAAO,CACnB,CAAC;QAEJ,IAAI,IAAI,GAAG,YAAY,EAAE,CAAC;YACxB,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;YACpB,MAAM,eAAe,GACnB,CAAC,IAAI,GAAG,YAAY,CAAC;gBACrB,IAAI,CAAC,GAAG,CACN,QAAQ,GAAG,YAAY,EACvB,MAAM,CACP,CAAC;YAEJ,OAAO,CACL,OAAO;gBACP,CAAC,CAAC,GAAG,eAAe,CAAC,CACtB,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,CAAC;IACX,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ export declare class LowPassFilter {
2
+ private previousOutput;
3
+ process(input: number, cutoffFrequency: number, sampleRate: number): number;
4
+ reset(): void;
5
+ }
@@ -0,0 +1,14 @@
1
+ export class LowPassFilter {
2
+ previousOutput = 0;
3
+ process(input, cutoffFrequency, sampleRate) {
4
+ const deltaTime = 1 / sampleRate;
5
+ const rc = 1 / (2 * Math.PI * cutoffFrequency);
6
+ const alpha = deltaTime / (rc + deltaTime);
7
+ this.previousOutput = this.previousOutput + alpha * (input - this.previousOutput);
8
+ return this.previousOutput;
9
+ }
10
+ reset() {
11
+ this.previousOutput = 0;
12
+ }
13
+ }
14
+ //# sourceMappingURL=LowPassFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LowPassFilter.js","sourceRoot":"","sources":["../../src/audio/LowPassFilter.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,aAAa;IAChB,cAAc,GAAG,CAAC,CAAC;IACpB,OAAO,CAAE,KAAa,EAAE,eAAuB,EAAE,UAAkB;QACxE,MAAM,SAAS,GAAG,CAAC,GAAG,UAAU,CAAC;QACjC,MAAM,EAAE,GAAG,CAAC,GAAE,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,eAAe,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,SAAS,GAAG,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,GAAG,KAAK,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;QAClF,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IACM,KAAK;QACV,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;IAC1B,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import type { Waveform } from "../music/MusicProfile.js";
2
+ export declare class Oscillator {
3
+ sample(waveform: Waveform, frequency: number, time: number): number;
4
+ private sine;
5
+ private triangle;
6
+ private sawtooth;
7
+ private square;
8
+ }
@@ -0,0 +1,42 @@
1
+ export class Oscillator {
2
+ sample(waveform, frequency, time) {
3
+ switch (waveform) {
4
+ case "sine":
5
+ return this.sine(frequency, time);
6
+ case "triangle":
7
+ return this.triangle(frequency, time);
8
+ case "sawtooth":
9
+ return this.sawtooth(frequency, time);
10
+ case "square":
11
+ return this.square(frequency, time);
12
+ }
13
+ }
14
+ sine(frequency, time) {
15
+ return Math.sin(2 *
16
+ Math.PI *
17
+ frequency *
18
+ time);
19
+ }
20
+ triangle(frequency, time) {
21
+ return (2 /
22
+ Math.PI *
23
+ Math.asin(Math.sin(2 *
24
+ Math.PI *
25
+ frequency *
26
+ time)));
27
+ }
28
+ sawtooth(frequency, time) {
29
+ return (2 *
30
+ (frequency * time -
31
+ Math.floor(frequency * time + 0.5)));
32
+ }
33
+ square(frequency, time) {
34
+ return (Math.sin(2 *
35
+ Math.PI *
36
+ frequency *
37
+ time) >= 0
38
+ ? 1
39
+ : -1);
40
+ }
41
+ }
42
+ //# sourceMappingURL=Oscillator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Oscillator.js","sourceRoot":"","sources":["../../src/audio/Oscillator.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,UAAU;IACd,MAAM,CACX,QAAkB,EAClB,SAAiB,EACjB,IAAY;QAEZ,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,MAAM;gBACT,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAEpC,KAAK,UAAU;gBACb,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAExC,KAAK,UAAU;gBACb,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAExC,KAAK,QAAQ;gBACX,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAEO,IAAI,CACV,SAAiB,EACjB,IAAY;QAEZ,OAAO,IAAI,CAAC,GAAG,CACb,CAAC;YACD,IAAI,CAAC,EAAE;YACP,SAAS;YACT,IAAI,CACL,CAAC;IACJ,CAAC;IAEO,QAAQ,CACd,SAAiB,EACjB,IAAY;QAEZ,OAAO,CACL,CAAC;YACC,IAAI,CAAC,EAAE;YACP,IAAI,CAAC,IAAI,CACP,IAAI,CAAC,GAAG,CACN,CAAC;gBACD,IAAI,CAAC,EAAE;gBACP,SAAS;gBACT,IAAI,CACL,CACF,CACJ,CAAC;IACJ,CAAC;IAEO,QAAQ,CACd,SAAiB,EACjB,IAAY;QAEZ,OAAO,CACL,CAAC;YACD,CACE,SAAS,GAAG,IAAI;gBAChB,IAAI,CAAC,KAAK,CACR,SAAS,GAAG,IAAI,GAAG,GAAG,CACvB,CACF,CACF,CAAC;IACJ,CAAC;IAEO,MAAM,CACZ,SAAiB,EACjB,IAAY;QAEZ,OAAO,CACL,IAAI,CAAC,GAAG,CACN,CAAC;YACD,IAAI,CAAC,EAAE;YACP,SAAS;YACT,IAAI,CACL,IAAI,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,CAAC,CAAC,CAAC,CAAC,CACP,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export interface StereoBuffer {
2
+ left: Float32Array;
3
+ right: Float32Array;
4
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=StereoBuffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StereoBuffer.js","sourceRoot":"","sources":["../../src/audio/StereoBuffer.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import type { MusicEvent } from "../music/MusicEvent.js";
2
+ import type { StereoBuffer } from "./StereoBuffer.js";
3
+ export interface SynthesizerOptions {
4
+ sampleRate: number;
5
+ }
6
+ export declare class Synthesizer {
7
+ private readonly oscillator;
8
+ private readonly envelope;
9
+ render(events: MusicEvent[], options?: SynthesizerOptions): StereoBuffer;
10
+ private renderEvent;
11
+ private getTimbreSample;
12
+ private getPan;
13
+ private getMelodyPan;
14
+ private getPadPan;
15
+ private calculatePanGains;
16
+ private getCutoffFrequency;
17
+ private getEnvelopeForLayer;
18
+ private getLayerGain;
19
+ private applyMasterGain;
20
+ private softClip;
21
+ }