@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.
@@ -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 { }