@react-synth/synth 0.0.6-alpha
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/README.md +187 -0
- package/dist/cli.cjs +127 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +126 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1702 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +388 -0
- package/dist/index.js +1701 -0
- package/dist/index.js.map +1 -0
- package/package.json +80 -0
- package/src/samples/bd_haus.flac +0 -0
- package/src/samples/drum_cymbal_closed.flac +0 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { AudioContext as AudioContext_2 } from 'node-web-audio-api';
|
|
2
|
+
import { Context } from 'react';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
declare type Accidental = "" | "#" | "b" | "##" | "bb";
|
|
6
|
+
|
|
7
|
+
export declare const ADSR_DEFAULTS: {
|
|
8
|
+
readonly attack: 0;
|
|
9
|
+
readonly attack_level: 1;
|
|
10
|
+
readonly decay: 0;
|
|
11
|
+
readonly sustain: 0;
|
|
12
|
+
readonly sustain_level: 1;
|
|
13
|
+
readonly release: 1;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export declare type ADSRProps = {
|
|
17
|
+
/** Attack time in beats - time from silence to attack_level (default: 0) */
|
|
18
|
+
attack?: number;
|
|
19
|
+
/** Peak amplitude at end of attack, multiplied by amp (default: 1) */
|
|
20
|
+
attack_level?: number;
|
|
21
|
+
/** Decay time in beats - time from attack_level to decay_level (default: 0) */
|
|
22
|
+
decay?: number;
|
|
23
|
+
/** Amplitude at end of decay, multiplied by amp (default: sustain_level) */
|
|
24
|
+
decay_level?: number;
|
|
25
|
+
/** Sustain time in beats - time at sustain_level (default: 0) */
|
|
26
|
+
sustain?: number;
|
|
27
|
+
/** Sustained amplitude level, multiplied by amp (default: 1) */
|
|
28
|
+
sustain_level?: number;
|
|
29
|
+
/** Release time in beats - time from sustain_level to silence (default: 1) */
|
|
30
|
+
release?: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export declare function applyADSREnvelope(gainNode: GainNode, audioTime: number, props: ADSRProps, amp: number, beatsToSeconds: (beats: number) => number): number;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Chord component - plays multiple notes simultaneously using Web Audio oscillators
|
|
37
|
+
*
|
|
38
|
+
* When used inside a Loop or Sequence, the chord will be scheduled
|
|
39
|
+
* with sample-accurate timing via the scheduler.
|
|
40
|
+
*
|
|
41
|
+
* The chord duration is determined by the ADSR envelope:
|
|
42
|
+
* total duration = attack + decay + sustain + release
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Using chord names (powered by Tonal)
|
|
46
|
+
* <Chord notes="Cmaj7" amp={0.5} />
|
|
47
|
+
* <Chord notes="Am:4" oscillator="sawtooth" /> // A minor in octave 4
|
|
48
|
+
* <Chord notes="Dm7/F" release={2} /> // D minor 7 with F bass
|
|
49
|
+
*
|
|
50
|
+
* // Using note arrays
|
|
51
|
+
* <Chord notes={["a3", "c4", "e4"]} amp={0.5} attack={0.1} sustain={2} release={0.5} />
|
|
52
|
+
* <Chord notes={[440, 550, 660]} oscillator="sawtooth" decay={0.2} sustain_level={0.7} />
|
|
53
|
+
*
|
|
54
|
+
* // With filter and voice overrides
|
|
55
|
+
* <Chord notes="Em7" filter={{ cutoff: 1000, resonance: 4 }} />
|
|
56
|
+
* <Chord notes="Cmaj" voices={{ count: 2, detune: 8 }} />
|
|
57
|
+
*/
|
|
58
|
+
export declare function Chord({ notes, amp, oscillator, filter, voices, attack, attack_level, decay, decay_level, sustain, sustain_level, release, __stepIndex, }: ChordProps): ReactNode;
|
|
59
|
+
|
|
60
|
+
declare type ChordProps = ADSRProps & SynthOverrides & {
|
|
61
|
+
/**
|
|
62
|
+
* Chord specification - can be:
|
|
63
|
+
* - A chord name string (e.g., "Cmaj7", "Am", "F#m7", "Dm/F")
|
|
64
|
+
* - An array of note names (e.g., ["A4", "C#3", "E4"])
|
|
65
|
+
* - An array of frequencies in Hz (e.g., [440, 550, 660])
|
|
66
|
+
*
|
|
67
|
+
* When using chord names, specify the octave with a colon (e.g., "Cmaj7:4" for octave 4)
|
|
68
|
+
* Default octave is 3 if not specified.
|
|
69
|
+
*/
|
|
70
|
+
notes: string | (NoteName | number)[];
|
|
71
|
+
/** Amplitude 0-1 (default: 0.3) */
|
|
72
|
+
amp?: number;
|
|
73
|
+
/** Step index when inside a Sequence (injected by Sequence) */
|
|
74
|
+
__stepIndex?: number;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Cutoff value that can be either a static number or a dynamic line pattern
|
|
79
|
+
*/
|
|
80
|
+
export declare type CutoffType = number | {
|
|
81
|
+
/** Starting value (MIDI note number) */
|
|
82
|
+
from: number;
|
|
83
|
+
/** Ending value (MIDI note number) */
|
|
84
|
+
to: number;
|
|
85
|
+
/** Number of interpolation steps */
|
|
86
|
+
steps: number;
|
|
87
|
+
/** Whether to mirror the values (up then down) */
|
|
88
|
+
mirror?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Manual step index override.
|
|
91
|
+
* Use this when you need a global step counter across nested sequences.
|
|
92
|
+
* If omitted, uses __stepIndex from the parent Sequence.
|
|
93
|
+
*/
|
|
94
|
+
step?: number;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export declare const DEFAULT_SYNTH_CONFIG: SynthConfig;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Configuration for the synthesizer's filter
|
|
101
|
+
*/
|
|
102
|
+
export declare type FilterConfig = {
|
|
103
|
+
/** Filter type */
|
|
104
|
+
type: FilterType;
|
|
105
|
+
/** Cutoff type */
|
|
106
|
+
cutoff: CutoffType;
|
|
107
|
+
/** Resonance/Q factor */
|
|
108
|
+
resonance: number;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export declare type FilterType = "lowpass" | "highpass" | "bandpass" | "lowshelf" | "highshelf" | "peaking" | "notch" | "allpass";
|
|
112
|
+
|
|
113
|
+
export declare function getScheduler(bpm: number): Scheduler;
|
|
114
|
+
|
|
115
|
+
export declare function getSynthPreset(type: SynthType): SynthConfig;
|
|
116
|
+
|
|
117
|
+
declare type Letter = "A" | "B" | "C" | "D" | "E" | "F" | "G";
|
|
118
|
+
|
|
119
|
+
/** Generate an array of linearly interpolated values */
|
|
120
|
+
export declare function line(start: number, end: number, steps: number): number[];
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Loop component - plays its children repeatedly at the specified interval
|
|
124
|
+
*
|
|
125
|
+
* Children should use the useLoop() hook to register their audio callbacks,
|
|
126
|
+
* which will be called with precise timing by the scheduler.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* <Loop id="kick" interval={1}>
|
|
130
|
+
* <Note note="C2" />
|
|
131
|
+
* </Loop>
|
|
132
|
+
*/
|
|
133
|
+
export declare function Loop({ id, interval, children, }: LoopProps): ReactNode;
|
|
134
|
+
|
|
135
|
+
declare type LoopProps = {
|
|
136
|
+
/** Unique identifier for this loop */
|
|
137
|
+
id: string;
|
|
138
|
+
/** Interval in beats between each loop iteration */
|
|
139
|
+
interval: number;
|
|
140
|
+
/** Content to play on each loop */
|
|
141
|
+
children: ReactNode;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export declare function midiToFrequency(midi: number): number;
|
|
145
|
+
|
|
146
|
+
/** Mirror an array: [1,2,3] → [1,2,3,3,2,1] */
|
|
147
|
+
export declare function mirror(values: number[]): number[];
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Note component - plays a note using Web Audio oscillator with ADSR envelope
|
|
151
|
+
*
|
|
152
|
+
* When used inside a Loop or Sequence, the note will be scheduled
|
|
153
|
+
* with sample-accurate timing via the scheduler.
|
|
154
|
+
*
|
|
155
|
+
* The note duration is determined by the ADSR envelope:
|
|
156
|
+
* total duration = attack + decay + sustain + release
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* <Note note="A4" amp={0.5} attack={0.1} sustain={0.5} release={0.2} />
|
|
160
|
+
* <Note note={440} oscillator="sawtooth" attack={0.05} decay={0.1} sustain_level={0.7} />
|
|
161
|
+
* <Note note="C4" filter={{ cutoff: 800, resonance: 5 }} />
|
|
162
|
+
* <Note note="E4" voices={{ count: 3, detune: 10, spread: 0.5 }} />
|
|
163
|
+
*/
|
|
164
|
+
export declare function Note({ note, amp, oscillator, filter, voices, attack, attack_level, decay, decay_level, sustain, sustain_level, release, __stepIndex, }: NoteProps): ReactNode;
|
|
165
|
+
|
|
166
|
+
export declare type NoteInput = NoteName | number;
|
|
167
|
+
|
|
168
|
+
export declare type NoteName = `${Letter}${Accidental}${Octave}`;
|
|
169
|
+
|
|
170
|
+
declare type NoteProps = ADSRProps & SynthOverrides & {
|
|
171
|
+
/** Note name (e.g., "A4", "C#3") or frequency in Hz */
|
|
172
|
+
note: NoteName | number;
|
|
173
|
+
/** Amplitude 0-1 (default: 0.3) */
|
|
174
|
+
amp?: number;
|
|
175
|
+
/** Step index when inside a Sequence (injected by Sequence) */
|
|
176
|
+
__stepIndex?: number;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export declare function noteToFrequency(note: NoteName | string): number;
|
|
180
|
+
|
|
181
|
+
declare type Octave = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
|
182
|
+
|
|
183
|
+
declare type OscillatorType_2 = "sine" | "square" | "sawtooth" | "triangle";
|
|
184
|
+
export { OscillatorType_2 as OscillatorType }
|
|
185
|
+
|
|
186
|
+
export declare type PitchClass = `${Letter}${Accidental}`;
|
|
187
|
+
|
|
188
|
+
export declare function resetScheduler(bpm: number): Scheduler;
|
|
189
|
+
|
|
190
|
+
export declare function resolveChordNotes(notes: string | (string | number)[]): (string | number)[];
|
|
191
|
+
|
|
192
|
+
export declare function resolveCutoff(cutoff: CutoffType, stepIndex: number): number;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Sample component - plays an audio sample file with optional effects
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* <Sample name="bd_haus" amp={2} cutoff={100} />
|
|
199
|
+
* <Sample name="drum_cymbal_closed" amp={0.5} rate={1.5} pan={0.1} />
|
|
200
|
+
*/
|
|
201
|
+
export declare function Sample({ name, amp, cutoff, rate, pan, __stepIndex, }: SampleProps): ReactNode;
|
|
202
|
+
|
|
203
|
+
export declare type SampleName = "bd_haus" | "drum_cymbal_closed";
|
|
204
|
+
|
|
205
|
+
declare type SampleProps = {
|
|
206
|
+
/** Sample name (without extension, e.g., "bd_haus") */
|
|
207
|
+
name: SampleName;
|
|
208
|
+
/** Amplitude/volume (default: 1) */
|
|
209
|
+
amp?: number;
|
|
210
|
+
/**
|
|
211
|
+
* Filter cutoff as MIDI note number or Line pattern (Sonic Pi compatible)
|
|
212
|
+
*
|
|
213
|
+
* Common values:
|
|
214
|
+
* - 60 => ~262 Hz (C4)
|
|
215
|
+
* - 80 => ~831 Hz
|
|
216
|
+
* - 100 => ~2637 Hz
|
|
217
|
+
* - 110 => ~4699 Hz
|
|
218
|
+
* - 130 => ~20kHz (essentially no filtering)
|
|
219
|
+
*
|
|
220
|
+
* Can also be a Line pattern: { from: 60, to: 120, steps: 8 }
|
|
221
|
+
*/
|
|
222
|
+
cutoff?: CutoffType;
|
|
223
|
+
/** Playback rate multiplier (default: 1) */
|
|
224
|
+
rate?: number;
|
|
225
|
+
/** Stereo pan position from -1 (left) to 1 (right) (default: 0) */
|
|
226
|
+
pan?: number;
|
|
227
|
+
/** Step index when inside a Sequence (injected by Sequence) */
|
|
228
|
+
__stepIndex?: number;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export declare type ScheduleCallback = (audioTime: number, beatTime: number) => void;
|
|
232
|
+
|
|
233
|
+
export declare const ScheduleNoteContext: Context<ScheduleNoteContextValue | null>;
|
|
234
|
+
|
|
235
|
+
export declare type ScheduleNoteContextValue = {
|
|
236
|
+
/**
|
|
237
|
+
* Schedule a note to be played.
|
|
238
|
+
* @param id - Unique identifier for this note callback
|
|
239
|
+
* @param callback - The audio callback to invoke at the scheduled time
|
|
240
|
+
* @param stepIndex - Optional step index when inside a Sequence
|
|
241
|
+
*/
|
|
242
|
+
scheduleNote: (id: string, callback: ScheduleCallback, stepIndex?: number) => void;
|
|
243
|
+
/**
|
|
244
|
+
* Unschedule a previously scheduled note.
|
|
245
|
+
* @param id - The identifier used when scheduling
|
|
246
|
+
*/
|
|
247
|
+
unscheduleNote: (id: string) => void;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export declare class Scheduler {
|
|
251
|
+
private context;
|
|
252
|
+
private _bpm;
|
|
253
|
+
private startTime;
|
|
254
|
+
private events;
|
|
255
|
+
private timerID;
|
|
256
|
+
private running;
|
|
257
|
+
private readonly lookahead;
|
|
258
|
+
private readonly scheduleInterval;
|
|
259
|
+
/** Grace period before actually removing a loop - allows React HMR to re-add it */
|
|
260
|
+
private readonly removalGracePeriod;
|
|
261
|
+
constructor(bpm: number);
|
|
262
|
+
get audioContext(): AudioContext_2;
|
|
263
|
+
get bpm(): number;
|
|
264
|
+
set bpm(value: number);
|
|
265
|
+
start(): void;
|
|
266
|
+
stop(): void;
|
|
267
|
+
clear(): void;
|
|
268
|
+
addLoop(id: string, intervalBeats: number, callback: ScheduleCallback, startBeat?: number): void;
|
|
269
|
+
remove(id: string): void;
|
|
270
|
+
beatsToSeconds(beats: number): number;
|
|
271
|
+
private secondsToBeats;
|
|
272
|
+
private getCurrentBeat;
|
|
273
|
+
private beatToAudioTime;
|
|
274
|
+
private schedule;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Sequence component - plays children one after another with precise timing
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* <Sequence interval={0.25}>
|
|
282
|
+
* <Note note="C4" />
|
|
283
|
+
* <Note note="E4" />
|
|
284
|
+
* <Note note="G4" />
|
|
285
|
+
* </Sequence>
|
|
286
|
+
*/
|
|
287
|
+
export declare function Sequence({ interval, children, __stepIndex, }: SequenceProps): ReactNode;
|
|
288
|
+
|
|
289
|
+
declare type SequenceProps = {
|
|
290
|
+
/** Time interval between each child in beats */
|
|
291
|
+
interval: number;
|
|
292
|
+
/** Children to play in sequence */
|
|
293
|
+
children: ReactNode;
|
|
294
|
+
/** Step index when nested inside another Sequence (injected by parent Sequence) */
|
|
295
|
+
__stepIndex?: number;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Synth component - defines the synthesizer for child Note/Chord components
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* // Using a preset synth type
|
|
303
|
+
* <Synth type="prophet">
|
|
304
|
+
* <Note note="C4" />
|
|
305
|
+
* </Synth>
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* // Overriding preset parameters with nested structure
|
|
309
|
+
* <Synth type="prophet" filter={{ cutoff: 2000, resonance: 6 }}>
|
|
310
|
+
* <Chord notes="Am7" />
|
|
311
|
+
* </Synth>
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* // Creating thick unison sound
|
|
315
|
+
* <Synth type="saw" voices={{ count: 4, detune: 15, spread: 0.8 }}>
|
|
316
|
+
* <Sequence interval={0.25}>
|
|
317
|
+
* <Note note="A3" />
|
|
318
|
+
* <Note note="C4" />
|
|
319
|
+
* </Sequence>
|
|
320
|
+
* </Synth>
|
|
321
|
+
*/
|
|
322
|
+
export declare function Synth({ type, oscillator, filter, voices, children, }: SynthProps): ReactNode;
|
|
323
|
+
|
|
324
|
+
export declare const SYNTH_PRESETS: Record<SynthType, SynthConfig>;
|
|
325
|
+
|
|
326
|
+
export declare type SynthConfig = {
|
|
327
|
+
/** Oscillator waveform type */
|
|
328
|
+
oscillator: OscillatorType_2;
|
|
329
|
+
/** Filter configuration */
|
|
330
|
+
filter: FilterConfig;
|
|
331
|
+
/** Voice/unison configuration */
|
|
332
|
+
voices: VoiceConfig;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export declare type SynthOverrides = {
|
|
336
|
+
oscillator?: OscillatorType_2;
|
|
337
|
+
filter?: Partial<FilterConfig>;
|
|
338
|
+
voices?: Partial<VoiceConfig>;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
declare type SynthProps = SynthOverrides & {
|
|
342
|
+
type: SynthType;
|
|
343
|
+
children: React.ReactNode;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export declare type SynthType = "sine" | "saw" | "square" | "tri" | "prophet" | "hollow" | "dark_ambience" | "bass" | "pluck";
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Track component - wraps your song and provides timing context
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* <Track bpm={120}>
|
|
353
|
+
* <Loop id="drums" interval={1}>
|
|
354
|
+
* <Note note="C4" />
|
|
355
|
+
* </Loop>
|
|
356
|
+
* </Track>
|
|
357
|
+
*/
|
|
358
|
+
export declare function Track({ bpm, children }: TrackProps): ReactNode;
|
|
359
|
+
|
|
360
|
+
declare type TrackContextValue = {
|
|
361
|
+
audioContext: AudioContext_2;
|
|
362
|
+
scheduler: Scheduler;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
declare type TrackProps = {
|
|
366
|
+
bpm: number;
|
|
367
|
+
children: ReactNode;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
export declare function useScheduleNote(): ScheduleNoteContextValue;
|
|
371
|
+
|
|
372
|
+
export declare function useSynth(): SynthConfig;
|
|
373
|
+
|
|
374
|
+
export declare function useTrack(): TrackContextValue;
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Configuration for oscillator voices (for unison/detune effects)
|
|
378
|
+
*/
|
|
379
|
+
export declare type VoiceConfig = {
|
|
380
|
+
/** Number of oscillator voices */
|
|
381
|
+
count: number;
|
|
382
|
+
/** Detune spread in cents between voices */
|
|
383
|
+
detune: number;
|
|
384
|
+
/** Stereo spread for voices 0-1 */
|
|
385
|
+
spread: number;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
export { }
|