@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.
- package/LICENSE +7 -0
- package/README.md +320 -0
- package/dist/audio/AudioPlayer.d.ts +4 -0
- package/dist/audio/AudioPlayer.js +28 -0
- package/dist/audio/AudioPlayer.js.map +1 -0
- package/dist/audio/Envelope.d.ts +9 -0
- package/dist/audio/Envelope.js +29 -0
- package/dist/audio/Envelope.js.map +1 -0
- package/dist/audio/LowPassFilter.d.ts +5 -0
- package/dist/audio/LowPassFilter.js +14 -0
- package/dist/audio/LowPassFilter.js.map +1 -0
- package/dist/audio/Oscillator.d.ts +8 -0
- package/dist/audio/Oscillator.js +42 -0
- package/dist/audio/Oscillator.js.map +1 -0
- package/dist/audio/StereoBuffer.d.ts +4 -0
- package/dist/audio/StereoBuffer.js +2 -0
- package/dist/audio/StereoBuffer.js.map +1 -0
- package/dist/audio/Synthesizer.d.ts +21 -0
- package/dist/audio/Synthesizer.js +186 -0
- package/dist/audio/Synthesizer.js.map +1 -0
- package/dist/audio/WavEncoder.d.ts +8 -0
- package/dist/audio/WavEncoder.js +56 -0
- package/dist/audio/WavEncoder.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +2 -0
- package/dist/cli/commands/analyze.js +90 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/export.d.ts +2 -0
- package/dist/cli/commands/export.js +86 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/play.d.ts +2 -0
- package/dist/cli/commands/play.js +69 -0
- package/dist/cli/commands/play.js.map +1 -0
- package/dist/git/GitCommit.d.ts +10 -0
- package/dist/git/GitCommit.js +2 -0
- package/dist/git/GitCommit.js.map +1 -0
- package/dist/git/GitHistoryAnalyzer.d.ts +7 -0
- package/dist/git/GitHistoryAnalyzer.js +97 -0
- package/dist/git/GitHistoryAnalyzer.js.map +1 -0
- package/dist/git/GitHistoryParser.d.ts +7 -0
- package/dist/git/GitHistoryParser.js +97 -0
- package/dist/git/GitHistoryParser.js.map +1 -0
- package/dist/git/GitHistoryStats.d.ts +15 -0
- package/dist/git/GitHistoryStats.js +2 -0
- package/dist/git/GitHistoryStats.js.map +1 -0
- package/dist/git/GitRepository.d.ts +10 -0
- package/dist/git/GitRepository.js +115 -0
- package/dist/git/GitRepository.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/music/MusicComposer.d.ts +13 -0
- package/dist/music/MusicComposer.js +257 -0
- package/dist/music/MusicComposer.js.map +1 -0
- package/dist/music/MusicEvent.d.ts +11 -0
- package/dist/music/MusicEvent.js +2 -0
- package/dist/music/MusicEvent.js.map +1 -0
- package/dist/music/MusicMapper.d.ts +9 -0
- package/dist/music/MusicMapper.js +67 -0
- package/dist/music/MusicMapper.js.map +1 -0
- package/dist/music/MusicProfile.d.ts +11 -0
- package/dist/music/MusicProfile.js +2 -0
- package/dist/music/MusicProfile.js.map +1 -0
- package/dist/music/midi.d.ts +2 -0
- package/dist/music/midi.js +26 -0
- package/dist/music/midi.js.map +1 -0
- package/dist/music/normalize.d.ts +1 -0
- package/dist/music/normalize.js +8 -0
- package/dist/music/normalize.js.map +1 -0
- package/dist/music/notes.d.ts +1 -0
- package/dist/music/notes.js +24 -0
- package/dist/music/notes.js.map +1 -0
- package/dist/music/scales.d.ts +2 -0
- package/dist/music/scales.js +7 -0
- package/dist/music/scales.js.map +1 -0
- 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
|
+
[](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,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,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,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,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 @@
|
|
|
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
|
+
}
|