@mulsense/xnew 0.1.8 → 0.1.9
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/dist/types/audio/audio.d.ts +55 -2
- package/dist/types/core/unit.d.ts +4 -3
- package/dist/types/core/xnew.d.ts +1 -11
- package/dist/types/index.d.ts +1 -8
- package/dist/xnew.d.ts +10 -23
- package/dist/xnew.js +89 -117
- package/dist/xnew.mjs +89 -117
- package/package.json +1 -1
|
@@ -1,2 +1,55 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
|
|
1
|
+
export declare const audio: {
|
|
2
|
+
load(path: string): AudioFile;
|
|
3
|
+
synthesizer(props: SynthProps): Synthesizer;
|
|
4
|
+
volume: number;
|
|
5
|
+
};
|
|
6
|
+
declare class AudioFile {
|
|
7
|
+
buffer?: AudioBuffer;
|
|
8
|
+
promise: Promise<void>;
|
|
9
|
+
source?: AudioBufferSourceNode;
|
|
10
|
+
amp?: GainNode;
|
|
11
|
+
start: number | null;
|
|
12
|
+
constructor(path: string);
|
|
13
|
+
play(offset?: number, loop?: boolean): void;
|
|
14
|
+
pause(): number | undefined;
|
|
15
|
+
}
|
|
16
|
+
type SynthProps = {
|
|
17
|
+
oscillator: OscillatorOptions;
|
|
18
|
+
amp: AmpOptions;
|
|
19
|
+
filter?: FilterOptions;
|
|
20
|
+
reverb?: ReverbOptions;
|
|
21
|
+
bpm?: number;
|
|
22
|
+
};
|
|
23
|
+
type OscillatorOptions = {
|
|
24
|
+
type: OscillatorType;
|
|
25
|
+
envelope?: Envelope;
|
|
26
|
+
LFO?: LFO;
|
|
27
|
+
};
|
|
28
|
+
type FilterOptions = {
|
|
29
|
+
type: BiquadFilterType;
|
|
30
|
+
cutoff: number;
|
|
31
|
+
};
|
|
32
|
+
type AmpOptions = {
|
|
33
|
+
envelope: Envelope;
|
|
34
|
+
};
|
|
35
|
+
type ReverbOptions = {
|
|
36
|
+
time: number;
|
|
37
|
+
mix: number;
|
|
38
|
+
};
|
|
39
|
+
type Envelope = {
|
|
40
|
+
amount: number;
|
|
41
|
+
ADSR: [number, number, number, number];
|
|
42
|
+
};
|
|
43
|
+
type LFO = {
|
|
44
|
+
amount: number;
|
|
45
|
+
type: OscillatorType;
|
|
46
|
+
rate: number;
|
|
47
|
+
};
|
|
48
|
+
declare class Synthesizer {
|
|
49
|
+
props: SynthProps;
|
|
50
|
+
constructor(props: SynthProps);
|
|
51
|
+
press(frequency: number | string, duration?: number | string, wait?: number): {
|
|
52
|
+
release: () => void;
|
|
53
|
+
} | undefined;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -43,13 +43,14 @@ export declare class UnitPromise {
|
|
|
43
43
|
export declare class Unit {
|
|
44
44
|
[key: string]: any;
|
|
45
45
|
_: UnitInternal;
|
|
46
|
-
constructor(parent: Unit | null,
|
|
46
|
+
constructor(parent: Unit | null, ...args: any[]);
|
|
47
47
|
get element(): UnitElement;
|
|
48
48
|
get components(): Function[];
|
|
49
49
|
start(): void;
|
|
50
50
|
stop(): void;
|
|
51
51
|
finalize(): void;
|
|
52
52
|
reboot(): void;
|
|
53
|
+
append(...args: any[]): void;
|
|
53
54
|
static initialize(unit: Unit, anchor: UnitElement | null): void;
|
|
54
55
|
static finalize(unit: Unit): void;
|
|
55
56
|
static nest(unit: Unit, tag: string): UnitElement;
|
|
@@ -67,9 +68,9 @@ export declare class Unit {
|
|
|
67
68
|
static scope(snapshot: Snapshot, func: Function, ...args: any[]): any;
|
|
68
69
|
static snapshot(unit: Unit): Snapshot;
|
|
69
70
|
static context(unit: Unit, key: string, value?: any): any;
|
|
70
|
-
static
|
|
71
|
+
static component2units: MapSet<Function, Unit>;
|
|
71
72
|
static find(component: Function): Unit[];
|
|
72
|
-
static
|
|
73
|
+
static type2units: MapSet<string, Unit>;
|
|
73
74
|
on(type: string, listener: Function, options?: boolean | AddEventListenerOptions): void;
|
|
74
75
|
off(type?: string, listener?: Function): void;
|
|
75
76
|
emit(type: string, ...args: any[]): void;
|
|
@@ -119,16 +119,6 @@ export declare const xnew: CreateUnit & {
|
|
|
119
119
|
* buttons.forEach(btn => btn.finalize())
|
|
120
120
|
*/
|
|
121
121
|
find(component: Function): Unit[];
|
|
122
|
-
/**
|
|
123
|
-
* Appends new components to existing component(s) in the tree
|
|
124
|
-
* @param anchor - Component function or Unit instance to append to
|
|
125
|
-
* @param args - Arguments to pass to xnew for creating child components
|
|
126
|
-
* @throws Error if anchor parameter is invalid
|
|
127
|
-
* @example
|
|
128
|
-
* xnew.append(MyContainer, ChildComponent, { prop: 'value' })
|
|
129
|
-
* xnew.append(unitInstance, AnotherComponent)
|
|
130
|
-
*/
|
|
131
|
-
append(anchor: Unit, ...args: any[]): void;
|
|
132
122
|
/**
|
|
133
123
|
* Executes a callback once after a delay, managed by component lifecycle
|
|
134
124
|
* @param callback - Function to execute after delay
|
|
@@ -138,7 +128,7 @@ export declare const xnew: CreateUnit & {
|
|
|
138
128
|
* const timer = xnew.timeout(() => console.log('Delayed'), 1000)
|
|
139
129
|
* // Cancel if needed: timer.clear()
|
|
140
130
|
*/
|
|
141
|
-
timeout(callback: Function, delay
|
|
131
|
+
timeout(callback: Function, delay?: number): any;
|
|
142
132
|
/**
|
|
143
133
|
* Executes a callback repeatedly at specified intervals, managed by component lifecycle
|
|
144
134
|
* @param callback - Function to execute at each interval
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,8 +10,7 @@ import { TabFrame, TabButton, TabContent } from './basics/Tab';
|
|
|
10
10
|
import { AccordionFrame, AccordionHeader, AccordionBullet, AccordionContent } from './basics/Accordion';
|
|
11
11
|
import { DragFrame, DragTarget } from './basics/Drag';
|
|
12
12
|
import { AnalogStick, DirectionalPad } from './basics/Controller';
|
|
13
|
-
import {
|
|
14
|
-
import { synthesizer } from './audio/synthesizer';
|
|
13
|
+
import { audio } from './audio/audio';
|
|
15
14
|
declare const basics: {
|
|
16
15
|
Screen: typeof Screen;
|
|
17
16
|
PointerEvent: typeof PointerEvent;
|
|
@@ -32,12 +31,6 @@ declare const basics: {
|
|
|
32
31
|
AnalogStick: typeof AnalogStick;
|
|
33
32
|
DirectionalPad: typeof DirectionalPad;
|
|
34
33
|
};
|
|
35
|
-
declare const audio: {
|
|
36
|
-
master: GainNode;
|
|
37
|
-
context: AudioContext;
|
|
38
|
-
synthesizer: typeof synthesizer;
|
|
39
|
-
load: typeof load;
|
|
40
|
-
};
|
|
41
34
|
declare namespace xnew {
|
|
42
35
|
type Unit = InstanceType<typeof Unit>;
|
|
43
36
|
}
|
package/dist/xnew.d.ts
CHANGED
|
@@ -69,13 +69,14 @@ declare class UnitPromise {
|
|
|
69
69
|
declare class Unit {
|
|
70
70
|
[key: string]: any;
|
|
71
71
|
_: UnitInternal;
|
|
72
|
-
constructor(parent: Unit | null,
|
|
72
|
+
constructor(parent: Unit | null, ...args: any[]);
|
|
73
73
|
get element(): UnitElement;
|
|
74
74
|
get components(): Function[];
|
|
75
75
|
start(): void;
|
|
76
76
|
stop(): void;
|
|
77
77
|
finalize(): void;
|
|
78
78
|
reboot(): void;
|
|
79
|
+
append(...args: any[]): void;
|
|
79
80
|
static initialize(unit: Unit, anchor: UnitElement | null): void;
|
|
80
81
|
static finalize(unit: Unit): void;
|
|
81
82
|
static nest(unit: Unit, tag: string): UnitElement;
|
|
@@ -93,9 +94,9 @@ declare class Unit {
|
|
|
93
94
|
static scope(snapshot: Snapshot, func: Function, ...args: any[]): any;
|
|
94
95
|
static snapshot(unit: Unit): Snapshot;
|
|
95
96
|
static context(unit: Unit, key: string, value?: any): any;
|
|
96
|
-
static
|
|
97
|
+
static component2units: MapSet<Function, Unit>;
|
|
97
98
|
static find(component: Function): Unit[];
|
|
98
|
-
static
|
|
99
|
+
static type2units: MapSet<string, Unit>;
|
|
99
100
|
on(type: string, listener: Function, options?: boolean | AddEventListenerOptions): void;
|
|
100
101
|
off(type?: string, listener?: Function): void;
|
|
101
102
|
emit(type: string, ...args: any[]): void;
|
|
@@ -223,16 +224,6 @@ declare const xnew$1: CreateUnit & {
|
|
|
223
224
|
* buttons.forEach(btn => btn.finalize())
|
|
224
225
|
*/
|
|
225
226
|
find(component: Function): Unit[];
|
|
226
|
-
/**
|
|
227
|
-
* Appends new components to existing component(s) in the tree
|
|
228
|
-
* @param anchor - Component function or Unit instance to append to
|
|
229
|
-
* @param args - Arguments to pass to xnew for creating child components
|
|
230
|
-
* @throws Error if anchor parameter is invalid
|
|
231
|
-
* @example
|
|
232
|
-
* xnew.append(MyContainer, ChildComponent, { prop: 'value' })
|
|
233
|
-
* xnew.append(unitInstance, AnotherComponent)
|
|
234
|
-
*/
|
|
235
|
-
append(anchor: Unit, ...args: any[]): void;
|
|
236
227
|
/**
|
|
237
228
|
* Executes a callback once after a delay, managed by component lifecycle
|
|
238
229
|
* @param callback - Function to execute after delay
|
|
@@ -242,7 +233,7 @@ declare const xnew$1: CreateUnit & {
|
|
|
242
233
|
* const timer = xnew.timeout(() => console.log('Delayed'), 1000)
|
|
243
234
|
* // Cancel if needed: timer.clear()
|
|
244
235
|
*/
|
|
245
|
-
timeout(callback: Function, delay
|
|
236
|
+
timeout(callback: Function, delay?: number): any;
|
|
246
237
|
/**
|
|
247
238
|
* Executes a callback repeatedly at specified intervals, managed by component lifecycle
|
|
248
239
|
* @param callback - Function to execute at each interval
|
|
@@ -395,7 +386,11 @@ declare function DirectionalPad(self: Unit, { size, diagonal, fill, fillOpacity,
|
|
|
395
386
|
strokeLinejoin?: string;
|
|
396
387
|
}): void;
|
|
397
388
|
|
|
398
|
-
declare
|
|
389
|
+
declare const audio: {
|
|
390
|
+
load(path: string): AudioFile;
|
|
391
|
+
synthesizer(props: SynthProps): Synthesizer;
|
|
392
|
+
volume: number;
|
|
393
|
+
};
|
|
399
394
|
declare class AudioFile {
|
|
400
395
|
buffer?: AudioBuffer;
|
|
401
396
|
promise: Promise<void>;
|
|
@@ -406,8 +401,6 @@ declare class AudioFile {
|
|
|
406
401
|
play(offset?: number, loop?: boolean): void;
|
|
407
402
|
pause(): number | undefined;
|
|
408
403
|
}
|
|
409
|
-
|
|
410
|
-
declare function synthesizer(props: SynthProps): Synthesizer;
|
|
411
404
|
type SynthProps = {
|
|
412
405
|
oscillator: OscillatorOptions;
|
|
413
406
|
amp: AmpOptions;
|
|
@@ -468,12 +461,6 @@ declare const basics: {
|
|
|
468
461
|
AnalogStick: typeof AnalogStick;
|
|
469
462
|
DirectionalPad: typeof DirectionalPad;
|
|
470
463
|
};
|
|
471
|
-
declare const audio: {
|
|
472
|
-
master: GainNode;
|
|
473
|
-
context: AudioContext;
|
|
474
|
-
synthesizer: typeof synthesizer;
|
|
475
|
-
load: typeof load;
|
|
476
|
-
};
|
|
477
464
|
declare namespace xnew {
|
|
478
465
|
type Unit = InstanceType<typeof Unit>;
|
|
479
466
|
}
|
package/dist/xnew.js
CHANGED
|
@@ -220,8 +220,26 @@
|
|
|
220
220
|
// unit
|
|
221
221
|
//----------------------------------------------------------------------------------------------------
|
|
222
222
|
class Unit {
|
|
223
|
-
constructor(parent,
|
|
223
|
+
constructor(parent, ...args) {
|
|
224
224
|
var _a;
|
|
225
|
+
let target;
|
|
226
|
+
if (args[0] instanceof HTMLElement || args[0] instanceof SVGElement) {
|
|
227
|
+
target = args.shift(); // an existing html element
|
|
228
|
+
}
|
|
229
|
+
else if (typeof args[0] === 'string' && args[0].match(/<((\w+)[^>]*?)\/?>/)) {
|
|
230
|
+
target = args.shift();
|
|
231
|
+
}
|
|
232
|
+
else if (typeof args[0] === 'string') {
|
|
233
|
+
const query = args.shift();
|
|
234
|
+
target = document.querySelector(query);
|
|
235
|
+
if (target === null)
|
|
236
|
+
throw new Error(`'${query}' can not be found.`);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
target = null;
|
|
240
|
+
}
|
|
241
|
+
const component = args.shift();
|
|
242
|
+
const props = args.shift();
|
|
225
243
|
let baseElement;
|
|
226
244
|
if (target instanceof HTMLElement || target instanceof SVGElement) {
|
|
227
245
|
baseElement = target;
|
|
@@ -274,6 +292,9 @@
|
|
|
274
292
|
Unit.finalize(this);
|
|
275
293
|
Unit.initialize(this, anchor);
|
|
276
294
|
}
|
|
295
|
+
append(...args) {
|
|
296
|
+
new Unit(this, ...args);
|
|
297
|
+
}
|
|
277
298
|
static initialize(unit, anchor) {
|
|
278
299
|
const backup = Unit.current;
|
|
279
300
|
Unit.current = unit;
|
|
@@ -315,7 +336,7 @@
|
|
|
315
336
|
unit._.systems.finalize.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
|
|
316
337
|
unit.off();
|
|
317
338
|
Unit.suboff(unit, null);
|
|
318
|
-
unit._.components.forEach((component) => Unit.
|
|
339
|
+
unit._.components.forEach((component) => Unit.component2units.delete(component, unit));
|
|
319
340
|
if (unit._.elements.length > 0) {
|
|
320
341
|
unit._.baseElement.removeChild(unit._.elements[0]);
|
|
321
342
|
unit._.currentElement = unit._.baseElement;
|
|
@@ -353,7 +374,7 @@
|
|
|
353
374
|
static extend(unit, component, props) {
|
|
354
375
|
var _a;
|
|
355
376
|
unit._.components.push(component);
|
|
356
|
-
Unit.
|
|
377
|
+
Unit.component2units.add(component, unit);
|
|
357
378
|
const defines = (_a = component(unit, props)) !== null && _a !== void 0 ? _a : {};
|
|
358
379
|
Object.keys(defines).forEach((key) => {
|
|
359
380
|
if (unit[key] !== undefined && unit._.defines[key] === undefined) {
|
|
@@ -361,12 +382,10 @@
|
|
|
361
382
|
}
|
|
362
383
|
const descriptor = Object.getOwnPropertyDescriptor(defines, key);
|
|
363
384
|
const wrapper = { configurable: true, enumerable: true };
|
|
364
|
-
if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get)
|
|
385
|
+
if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get)
|
|
365
386
|
wrapper.get = Unit.wrap(unit, descriptor.get);
|
|
366
|
-
|
|
367
|
-
if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set) {
|
|
387
|
+
if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)
|
|
368
388
|
wrapper.set = Unit.wrap(unit, descriptor.set);
|
|
369
|
-
}
|
|
370
389
|
if (typeof (descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) === 'function') {
|
|
371
390
|
wrapper.value = Unit.wrap(unit, descriptor.value);
|
|
372
391
|
}
|
|
@@ -452,7 +471,7 @@
|
|
|
452
471
|
}
|
|
453
472
|
static find(component) {
|
|
454
473
|
var _a;
|
|
455
|
-
return [...((_a = Unit.
|
|
474
|
+
return [...((_a = Unit.component2units.get(component)) !== null && _a !== void 0 ? _a : [])];
|
|
456
475
|
}
|
|
457
476
|
on(type, listener, options) {
|
|
458
477
|
if (this._.state === 'finalized')
|
|
@@ -464,7 +483,7 @@
|
|
|
464
483
|
if (this._.listeners1.has(type, listener) === false) {
|
|
465
484
|
const execute = Unit.wrap(Unit.current, listener);
|
|
466
485
|
this._.listeners1.set(type, listener, [this.element, execute]);
|
|
467
|
-
Unit.
|
|
486
|
+
Unit.type2units.add(type, this);
|
|
468
487
|
if (/^[A-Za-z]/.test(type)) {
|
|
469
488
|
this.element.addEventListener(type, execute, options);
|
|
470
489
|
}
|
|
@@ -488,7 +507,7 @@
|
|
|
488
507
|
}
|
|
489
508
|
});
|
|
490
509
|
if (this._.listeners1.has(type) === false) {
|
|
491
|
-
Unit.
|
|
510
|
+
Unit.type2units.delete(type, this);
|
|
492
511
|
}
|
|
493
512
|
});
|
|
494
513
|
}
|
|
@@ -497,7 +516,7 @@
|
|
|
497
516
|
if (this._.state === 'finalized')
|
|
498
517
|
return;
|
|
499
518
|
if (type[0] === '+') {
|
|
500
|
-
(_a = Unit.
|
|
519
|
+
(_a = Unit.type2units.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((unit) => {
|
|
501
520
|
var _a;
|
|
502
521
|
(_a = unit._.listeners1.get(type)) === null || _a === void 0 ? void 0 : _a.forEach(([_, execute]) => execute(...args));
|
|
503
522
|
});
|
|
@@ -531,36 +550,17 @@
|
|
|
531
550
|
});
|
|
532
551
|
}
|
|
533
552
|
}
|
|
534
|
-
Unit.
|
|
553
|
+
Unit.component2units = new MapSet();
|
|
535
554
|
//----------------------------------------------------------------------------------------------------
|
|
536
555
|
// event
|
|
537
556
|
//----------------------------------------------------------------------------------------------------
|
|
538
|
-
Unit.
|
|
557
|
+
Unit.type2units = new MapSet();
|
|
539
558
|
|
|
540
559
|
const xnew$1 = Object.assign(function (...args) {
|
|
541
560
|
if (Unit.root === undefined) {
|
|
542
561
|
Unit.reset();
|
|
543
562
|
}
|
|
544
|
-
|
|
545
|
-
if (args[0] instanceof HTMLElement || args[0] instanceof SVGElement) {
|
|
546
|
-
target = args.shift(); // an existing html element
|
|
547
|
-
}
|
|
548
|
-
else if (typeof args[0] === 'string') {
|
|
549
|
-
const str = args.shift(); // a selector for an existing html element
|
|
550
|
-
if (str.match(/<([^>]*)\/?>/)) {
|
|
551
|
-
target = str;
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
target = document.querySelector(str);
|
|
555
|
-
if (target == null) {
|
|
556
|
-
throw new Error(`'${str}' can not be found.`);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
target = null;
|
|
562
|
-
}
|
|
563
|
-
return new Unit(Unit.current, target, ...args);
|
|
563
|
+
return new Unit(Unit.current, ...args);
|
|
564
564
|
}, {
|
|
565
565
|
/**
|
|
566
566
|
* Creates a nested HTML/SVG element within the current component
|
|
@@ -733,27 +733,6 @@
|
|
|
733
733
|
throw new Error('xnew.find(component: Function): [component] is invalid.');
|
|
734
734
|
}
|
|
735
735
|
},
|
|
736
|
-
/**
|
|
737
|
-
* Appends new components to existing component(s) in the tree
|
|
738
|
-
* @param anchor - Component function or Unit instance to append to
|
|
739
|
-
* @param args - Arguments to pass to xnew for creating child components
|
|
740
|
-
* @throws Error if anchor parameter is invalid
|
|
741
|
-
* @example
|
|
742
|
-
* xnew.append(MyContainer, ChildComponent, { prop: 'value' })
|
|
743
|
-
* xnew.append(unitInstance, AnotherComponent)
|
|
744
|
-
*/
|
|
745
|
-
append(anchor, ...args) {
|
|
746
|
-
if (typeof anchor === 'function') {
|
|
747
|
-
const units = Unit.find(anchor);
|
|
748
|
-
Unit.scope(Unit.snapshot(units[0]), xnew$1, ...args);
|
|
749
|
-
}
|
|
750
|
-
else if (anchor instanceof Unit) {
|
|
751
|
-
Unit.scope(Unit.snapshot(anchor), xnew$1, ...args);
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
throw new Error('xnew.append(anchor: Function | Unit, xnew arguments): [anchor] is invalid.');
|
|
755
|
-
}
|
|
756
|
-
},
|
|
757
736
|
/**
|
|
758
737
|
* Executes a callback once after a delay, managed by component lifecycle
|
|
759
738
|
* @param callback - Function to execute after delay
|
|
@@ -763,7 +742,7 @@
|
|
|
763
742
|
* const timer = xnew.timeout(() => console.log('Delayed'), 1000)
|
|
764
743
|
* // Cancel if needed: timer.clear()
|
|
765
744
|
*/
|
|
766
|
-
timeout(callback, delay) {
|
|
745
|
+
timeout(callback, delay = 0) {
|
|
767
746
|
const snapshot = Unit.snapshot(Unit.current);
|
|
768
747
|
const unit = xnew$1((self) => {
|
|
769
748
|
const timer = new Timer(() => {
|
|
@@ -832,21 +811,21 @@
|
|
|
832
811
|
const timer = new Timer(() => {
|
|
833
812
|
Unit.scope(snapshot, callback, 1.0);
|
|
834
813
|
self.finalize();
|
|
835
|
-
}, (
|
|
836
|
-
if (
|
|
814
|
+
}, (x) => {
|
|
815
|
+
if (x < 1.0) {
|
|
837
816
|
if (easing === 'ease-out') {
|
|
838
|
-
|
|
817
|
+
x = Math.pow((1.0 - Math.pow((1.0 - x), 2.0)), 0.5);
|
|
839
818
|
}
|
|
840
819
|
else if (easing === 'ease-in') {
|
|
841
|
-
|
|
820
|
+
x = Math.pow((1.0 - Math.pow((1.0 - x), 0.5)), 2.0);
|
|
842
821
|
}
|
|
843
822
|
else if (easing === 'ease') {
|
|
844
|
-
|
|
823
|
+
x = (1.0 - Math.cos(x * Math.PI)) / 2.0;
|
|
845
824
|
}
|
|
846
825
|
else if (easing === 'ease-in-out') {
|
|
847
|
-
|
|
826
|
+
x = (1.0 - Math.cos(x * Math.PI)) / 2.0;
|
|
848
827
|
}
|
|
849
|
-
Unit.scope(snapshot, callback,
|
|
828
|
+
Unit.scope(snapshot, callback, x);
|
|
850
829
|
}
|
|
851
830
|
}, interval);
|
|
852
831
|
self.on('finalize', () => {
|
|
@@ -1473,29 +1452,41 @@
|
|
|
1473
1452
|
const master = context.createGain();
|
|
1474
1453
|
master.gain.value = 1.0;
|
|
1475
1454
|
master.connect(context.destination);
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1455
|
+
window.addEventListener('touchstart', initialize, true);
|
|
1456
|
+
window.addEventListener('mousedown', initialize, true);
|
|
1457
|
+
function initialize() {
|
|
1458
|
+
new Synthesizer({ oscillator: { type: 'sine' }, amp: { envelope: { amount: 0, ADSR: [0, 0, 0, 0] } } }).press(440);
|
|
1459
|
+
window.removeEventListener('touchstart', initialize, true);
|
|
1460
|
+
window.removeEventListener('mousedown', initialize, true);
|
|
1479
1461
|
}
|
|
1480
|
-
const
|
|
1462
|
+
const audio = {
|
|
1463
|
+
load(path) {
|
|
1464
|
+
return new AudioFile(path);
|
|
1465
|
+
},
|
|
1466
|
+
synthesizer(props) {
|
|
1467
|
+
return new Synthesizer(props);
|
|
1468
|
+
},
|
|
1469
|
+
get volume() {
|
|
1470
|
+
return master.gain.value;
|
|
1471
|
+
},
|
|
1472
|
+
set volume(value) {
|
|
1473
|
+
master.gain.value = value;
|
|
1474
|
+
}
|
|
1475
|
+
};
|
|
1476
|
+
//----------------------------------------------------------------------------------------------------
|
|
1477
|
+
// audio file
|
|
1478
|
+
//----------------------------------------------------------------------------------------------------
|
|
1481
1479
|
class AudioFile {
|
|
1482
1480
|
constructor(path) {
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
this.buffer = response;
|
|
1493
|
-
})
|
|
1494
|
-
.catch(() => {
|
|
1495
|
-
console.warn(`"${path}" could not be loaded.`);
|
|
1496
|
-
});
|
|
1497
|
-
store.set(path, this.buffer);
|
|
1498
|
-
}
|
|
1481
|
+
this.promise = fetch(path)
|
|
1482
|
+
.then((response) => response.arrayBuffer())
|
|
1483
|
+
.then((response) => context.decodeAudioData(response))
|
|
1484
|
+
.then((response) => {
|
|
1485
|
+
this.buffer = response;
|
|
1486
|
+
})
|
|
1487
|
+
.catch(() => {
|
|
1488
|
+
console.warn(`"${path}" could not be loaded.`);
|
|
1489
|
+
});
|
|
1499
1490
|
this.start = null;
|
|
1500
1491
|
}
|
|
1501
1492
|
// set volume(value: number) {
|
|
@@ -1534,17 +1525,6 @@
|
|
|
1534
1525
|
}
|
|
1535
1526
|
}
|
|
1536
1527
|
}
|
|
1537
|
-
|
|
1538
|
-
function synthesizer(props) {
|
|
1539
|
-
return new Synthesizer(props);
|
|
1540
|
-
}
|
|
1541
|
-
window.addEventListener('touchstart', initialize, true);
|
|
1542
|
-
window.addEventListener('mousedown', initialize, true);
|
|
1543
|
-
function initialize() {
|
|
1544
|
-
new Synthesizer({ oscillator: { type: 'sine' }, amp: { envelope: { amount: 0, ADSR: [0, 0, 0, 0] } } }).press(440);
|
|
1545
|
-
window.removeEventListener('touchstart', initialize, true);
|
|
1546
|
-
window.removeEventListener('mousedown', initialize, true);
|
|
1547
|
-
}
|
|
1548
1528
|
const keymap = {
|
|
1549
1529
|
'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
|
|
1550
1530
|
'C1': 32.703, 'C#1': 34.648, 'D1': 36.708, 'D#1': 38.891, 'E1': 41.203, 'F1': 43.654, 'F#1': 46.249, 'G1': 48.999, 'G#1': 51.913, 'A1': 55.000, 'A#1': 58.270, 'B1': 61.735,
|
|
@@ -1560,23 +1540,21 @@
|
|
|
1560
1540
|
'1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
|
|
1561
1541
|
};
|
|
1562
1542
|
class Synthesizer {
|
|
1563
|
-
constructor(props) {
|
|
1564
|
-
this.props = props;
|
|
1565
|
-
}
|
|
1543
|
+
constructor(props) { this.props = props; }
|
|
1566
1544
|
press(frequency, duration, wait) {
|
|
1567
1545
|
var _a;
|
|
1568
1546
|
const props = this.props;
|
|
1569
|
-
const
|
|
1570
|
-
const
|
|
1547
|
+
const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
|
|
1548
|
+
const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
|
|
1571
1549
|
const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
|
|
1572
1550
|
const nodes = {};
|
|
1573
1551
|
nodes.oscillator = context.createOscillator();
|
|
1574
1552
|
nodes.oscillator.type = props.oscillator.type;
|
|
1575
|
-
nodes.oscillator.frequency.value =
|
|
1553
|
+
nodes.oscillator.frequency.value = fv;
|
|
1576
1554
|
if (props.oscillator.LFO) {
|
|
1577
1555
|
nodes.oscillatorLFO = context.createOscillator();
|
|
1578
1556
|
nodes.oscillatorLFODepth = context.createGain();
|
|
1579
|
-
nodes.oscillatorLFODepth.gain.value =
|
|
1557
|
+
nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
|
|
1580
1558
|
nodes.oscillatorLFO.type = props.oscillator.LFO.type;
|
|
1581
1559
|
nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
|
|
1582
1560
|
nodes.oscillatorLFO.start(start);
|
|
@@ -1589,7 +1567,7 @@
|
|
|
1589
1567
|
nodes.target.gain.value = 1.0;
|
|
1590
1568
|
nodes.amp.connect(nodes.target);
|
|
1591
1569
|
nodes.target.connect(master);
|
|
1592
|
-
if (props.filter
|
|
1570
|
+
if (props.filter) {
|
|
1593
1571
|
nodes.filter = context.createBiquadFilter();
|
|
1594
1572
|
nodes.filter.type = props.filter.type;
|
|
1595
1573
|
nodes.filter.frequency.value = props.filter.cutoff;
|
|
@@ -1611,22 +1589,22 @@
|
|
|
1611
1589
|
nodes.convolverDepth.connect(master);
|
|
1612
1590
|
}
|
|
1613
1591
|
if (props.oscillator.envelope) {
|
|
1614
|
-
const amount =
|
|
1615
|
-
startEnvelope(nodes.oscillator.frequency,
|
|
1592
|
+
const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
|
|
1593
|
+
startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
|
|
1616
1594
|
}
|
|
1617
1595
|
if (props.amp.envelope) {
|
|
1618
1596
|
startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
|
|
1619
1597
|
}
|
|
1620
|
-
let stop = null;
|
|
1621
1598
|
nodes.oscillator.start(start);
|
|
1622
|
-
if (
|
|
1599
|
+
if (dv > 0) {
|
|
1623
1600
|
release();
|
|
1624
1601
|
}
|
|
1625
1602
|
else {
|
|
1626
1603
|
return { release };
|
|
1627
1604
|
}
|
|
1628
1605
|
function release() {
|
|
1629
|
-
|
|
1606
|
+
let stop = null;
|
|
1607
|
+
const end = dv > 0 ? dv : (context.currentTime - start);
|
|
1630
1608
|
if (props.amp.envelope) {
|
|
1631
1609
|
const ADSR = props.amp.envelope.ADSR;
|
|
1632
1610
|
const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
|
|
@@ -1640,8 +1618,8 @@
|
|
|
1640
1618
|
nodes.oscillatorLFO.stop(stop);
|
|
1641
1619
|
}
|
|
1642
1620
|
if (props.oscillator.envelope) {
|
|
1643
|
-
const amount =
|
|
1644
|
-
stopEnvelope(nodes.oscillator.frequency,
|
|
1621
|
+
const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
|
|
1622
|
+
stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
|
|
1645
1623
|
}
|
|
1646
1624
|
if (props.amp.envelope) {
|
|
1647
1625
|
stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
|
|
@@ -1660,15 +1638,15 @@
|
|
|
1660
1638
|
}, 2000);
|
|
1661
1639
|
}
|
|
1662
1640
|
function stopEnvelope(param, base, amount, ADSR) {
|
|
1663
|
-
const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(
|
|
1641
|
+
const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(dv / (ADSR[0] / 1000), 1.0);
|
|
1664
1642
|
if (rate < 1.0) {
|
|
1665
1643
|
param.cancelScheduledValues(start);
|
|
1666
1644
|
param.setValueAtTime(base, start);
|
|
1667
1645
|
param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
|
|
1668
1646
|
param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
|
|
1669
1647
|
}
|
|
1670
|
-
param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate,
|
|
1671
|
-
param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate,
|
|
1648
|
+
param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
|
|
1649
|
+
param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv) + ADSR[3] / 1000);
|
|
1672
1650
|
}
|
|
1673
1651
|
function startEnvelope(param, base, amount, ADSR) {
|
|
1674
1652
|
param.value = base;
|
|
@@ -1710,12 +1688,6 @@
|
|
|
1710
1688
|
AnalogStick,
|
|
1711
1689
|
DirectionalPad,
|
|
1712
1690
|
};
|
|
1713
|
-
const audio = {
|
|
1714
|
-
master,
|
|
1715
|
-
context,
|
|
1716
|
-
synthesizer,
|
|
1717
|
-
load
|
|
1718
|
-
};
|
|
1719
1691
|
const xnew = Object.assign(xnew$1, {
|
|
1720
1692
|
basics,
|
|
1721
1693
|
audio
|
package/dist/xnew.mjs
CHANGED
|
@@ -214,8 +214,26 @@ class UnitPromise {
|
|
|
214
214
|
// unit
|
|
215
215
|
//----------------------------------------------------------------------------------------------------
|
|
216
216
|
class Unit {
|
|
217
|
-
constructor(parent,
|
|
217
|
+
constructor(parent, ...args) {
|
|
218
218
|
var _a;
|
|
219
|
+
let target;
|
|
220
|
+
if (args[0] instanceof HTMLElement || args[0] instanceof SVGElement) {
|
|
221
|
+
target = args.shift(); // an existing html element
|
|
222
|
+
}
|
|
223
|
+
else if (typeof args[0] === 'string' && args[0].match(/<((\w+)[^>]*?)\/?>/)) {
|
|
224
|
+
target = args.shift();
|
|
225
|
+
}
|
|
226
|
+
else if (typeof args[0] === 'string') {
|
|
227
|
+
const query = args.shift();
|
|
228
|
+
target = document.querySelector(query);
|
|
229
|
+
if (target === null)
|
|
230
|
+
throw new Error(`'${query}' can not be found.`);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
target = null;
|
|
234
|
+
}
|
|
235
|
+
const component = args.shift();
|
|
236
|
+
const props = args.shift();
|
|
219
237
|
let baseElement;
|
|
220
238
|
if (target instanceof HTMLElement || target instanceof SVGElement) {
|
|
221
239
|
baseElement = target;
|
|
@@ -268,6 +286,9 @@ class Unit {
|
|
|
268
286
|
Unit.finalize(this);
|
|
269
287
|
Unit.initialize(this, anchor);
|
|
270
288
|
}
|
|
289
|
+
append(...args) {
|
|
290
|
+
new Unit(this, ...args);
|
|
291
|
+
}
|
|
271
292
|
static initialize(unit, anchor) {
|
|
272
293
|
const backup = Unit.current;
|
|
273
294
|
Unit.current = unit;
|
|
@@ -309,7 +330,7 @@ class Unit {
|
|
|
309
330
|
unit._.systems.finalize.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
|
|
310
331
|
unit.off();
|
|
311
332
|
Unit.suboff(unit, null);
|
|
312
|
-
unit._.components.forEach((component) => Unit.
|
|
333
|
+
unit._.components.forEach((component) => Unit.component2units.delete(component, unit));
|
|
313
334
|
if (unit._.elements.length > 0) {
|
|
314
335
|
unit._.baseElement.removeChild(unit._.elements[0]);
|
|
315
336
|
unit._.currentElement = unit._.baseElement;
|
|
@@ -347,7 +368,7 @@ class Unit {
|
|
|
347
368
|
static extend(unit, component, props) {
|
|
348
369
|
var _a;
|
|
349
370
|
unit._.components.push(component);
|
|
350
|
-
Unit.
|
|
371
|
+
Unit.component2units.add(component, unit);
|
|
351
372
|
const defines = (_a = component(unit, props)) !== null && _a !== void 0 ? _a : {};
|
|
352
373
|
Object.keys(defines).forEach((key) => {
|
|
353
374
|
if (unit[key] !== undefined && unit._.defines[key] === undefined) {
|
|
@@ -355,12 +376,10 @@ class Unit {
|
|
|
355
376
|
}
|
|
356
377
|
const descriptor = Object.getOwnPropertyDescriptor(defines, key);
|
|
357
378
|
const wrapper = { configurable: true, enumerable: true };
|
|
358
|
-
if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get)
|
|
379
|
+
if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get)
|
|
359
380
|
wrapper.get = Unit.wrap(unit, descriptor.get);
|
|
360
|
-
|
|
361
|
-
if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set) {
|
|
381
|
+
if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)
|
|
362
382
|
wrapper.set = Unit.wrap(unit, descriptor.set);
|
|
363
|
-
}
|
|
364
383
|
if (typeof (descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) === 'function') {
|
|
365
384
|
wrapper.value = Unit.wrap(unit, descriptor.value);
|
|
366
385
|
}
|
|
@@ -446,7 +465,7 @@ class Unit {
|
|
|
446
465
|
}
|
|
447
466
|
static find(component) {
|
|
448
467
|
var _a;
|
|
449
|
-
return [...((_a = Unit.
|
|
468
|
+
return [...((_a = Unit.component2units.get(component)) !== null && _a !== void 0 ? _a : [])];
|
|
450
469
|
}
|
|
451
470
|
on(type, listener, options) {
|
|
452
471
|
if (this._.state === 'finalized')
|
|
@@ -458,7 +477,7 @@ class Unit {
|
|
|
458
477
|
if (this._.listeners1.has(type, listener) === false) {
|
|
459
478
|
const execute = Unit.wrap(Unit.current, listener);
|
|
460
479
|
this._.listeners1.set(type, listener, [this.element, execute]);
|
|
461
|
-
Unit.
|
|
480
|
+
Unit.type2units.add(type, this);
|
|
462
481
|
if (/^[A-Za-z]/.test(type)) {
|
|
463
482
|
this.element.addEventListener(type, execute, options);
|
|
464
483
|
}
|
|
@@ -482,7 +501,7 @@ class Unit {
|
|
|
482
501
|
}
|
|
483
502
|
});
|
|
484
503
|
if (this._.listeners1.has(type) === false) {
|
|
485
|
-
Unit.
|
|
504
|
+
Unit.type2units.delete(type, this);
|
|
486
505
|
}
|
|
487
506
|
});
|
|
488
507
|
}
|
|
@@ -491,7 +510,7 @@ class Unit {
|
|
|
491
510
|
if (this._.state === 'finalized')
|
|
492
511
|
return;
|
|
493
512
|
if (type[0] === '+') {
|
|
494
|
-
(_a = Unit.
|
|
513
|
+
(_a = Unit.type2units.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((unit) => {
|
|
495
514
|
var _a;
|
|
496
515
|
(_a = unit._.listeners1.get(type)) === null || _a === void 0 ? void 0 : _a.forEach(([_, execute]) => execute(...args));
|
|
497
516
|
});
|
|
@@ -525,36 +544,17 @@ class Unit {
|
|
|
525
544
|
});
|
|
526
545
|
}
|
|
527
546
|
}
|
|
528
|
-
Unit.
|
|
547
|
+
Unit.component2units = new MapSet();
|
|
529
548
|
//----------------------------------------------------------------------------------------------------
|
|
530
549
|
// event
|
|
531
550
|
//----------------------------------------------------------------------------------------------------
|
|
532
|
-
Unit.
|
|
551
|
+
Unit.type2units = new MapSet();
|
|
533
552
|
|
|
534
553
|
const xnew$1 = Object.assign(function (...args) {
|
|
535
554
|
if (Unit.root === undefined) {
|
|
536
555
|
Unit.reset();
|
|
537
556
|
}
|
|
538
|
-
|
|
539
|
-
if (args[0] instanceof HTMLElement || args[0] instanceof SVGElement) {
|
|
540
|
-
target = args.shift(); // an existing html element
|
|
541
|
-
}
|
|
542
|
-
else if (typeof args[0] === 'string') {
|
|
543
|
-
const str = args.shift(); // a selector for an existing html element
|
|
544
|
-
if (str.match(/<([^>]*)\/?>/)) {
|
|
545
|
-
target = str;
|
|
546
|
-
}
|
|
547
|
-
else {
|
|
548
|
-
target = document.querySelector(str);
|
|
549
|
-
if (target == null) {
|
|
550
|
-
throw new Error(`'${str}' can not be found.`);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
555
|
-
target = null;
|
|
556
|
-
}
|
|
557
|
-
return new Unit(Unit.current, target, ...args);
|
|
557
|
+
return new Unit(Unit.current, ...args);
|
|
558
558
|
}, {
|
|
559
559
|
/**
|
|
560
560
|
* Creates a nested HTML/SVG element within the current component
|
|
@@ -727,27 +727,6 @@ const xnew$1 = Object.assign(function (...args) {
|
|
|
727
727
|
throw new Error('xnew.find(component: Function): [component] is invalid.');
|
|
728
728
|
}
|
|
729
729
|
},
|
|
730
|
-
/**
|
|
731
|
-
* Appends new components to existing component(s) in the tree
|
|
732
|
-
* @param anchor - Component function or Unit instance to append to
|
|
733
|
-
* @param args - Arguments to pass to xnew for creating child components
|
|
734
|
-
* @throws Error if anchor parameter is invalid
|
|
735
|
-
* @example
|
|
736
|
-
* xnew.append(MyContainer, ChildComponent, { prop: 'value' })
|
|
737
|
-
* xnew.append(unitInstance, AnotherComponent)
|
|
738
|
-
*/
|
|
739
|
-
append(anchor, ...args) {
|
|
740
|
-
if (typeof anchor === 'function') {
|
|
741
|
-
const units = Unit.find(anchor);
|
|
742
|
-
Unit.scope(Unit.snapshot(units[0]), xnew$1, ...args);
|
|
743
|
-
}
|
|
744
|
-
else if (anchor instanceof Unit) {
|
|
745
|
-
Unit.scope(Unit.snapshot(anchor), xnew$1, ...args);
|
|
746
|
-
}
|
|
747
|
-
else {
|
|
748
|
-
throw new Error('xnew.append(anchor: Function | Unit, xnew arguments): [anchor] is invalid.');
|
|
749
|
-
}
|
|
750
|
-
},
|
|
751
730
|
/**
|
|
752
731
|
* Executes a callback once after a delay, managed by component lifecycle
|
|
753
732
|
* @param callback - Function to execute after delay
|
|
@@ -757,7 +736,7 @@ const xnew$1 = Object.assign(function (...args) {
|
|
|
757
736
|
* const timer = xnew.timeout(() => console.log('Delayed'), 1000)
|
|
758
737
|
* // Cancel if needed: timer.clear()
|
|
759
738
|
*/
|
|
760
|
-
timeout(callback, delay) {
|
|
739
|
+
timeout(callback, delay = 0) {
|
|
761
740
|
const snapshot = Unit.snapshot(Unit.current);
|
|
762
741
|
const unit = xnew$1((self) => {
|
|
763
742
|
const timer = new Timer(() => {
|
|
@@ -826,21 +805,21 @@ const xnew$1 = Object.assign(function (...args) {
|
|
|
826
805
|
const timer = new Timer(() => {
|
|
827
806
|
Unit.scope(snapshot, callback, 1.0);
|
|
828
807
|
self.finalize();
|
|
829
|
-
}, (
|
|
830
|
-
if (
|
|
808
|
+
}, (x) => {
|
|
809
|
+
if (x < 1.0) {
|
|
831
810
|
if (easing === 'ease-out') {
|
|
832
|
-
|
|
811
|
+
x = Math.pow((1.0 - Math.pow((1.0 - x), 2.0)), 0.5);
|
|
833
812
|
}
|
|
834
813
|
else if (easing === 'ease-in') {
|
|
835
|
-
|
|
814
|
+
x = Math.pow((1.0 - Math.pow((1.0 - x), 0.5)), 2.0);
|
|
836
815
|
}
|
|
837
816
|
else if (easing === 'ease') {
|
|
838
|
-
|
|
817
|
+
x = (1.0 - Math.cos(x * Math.PI)) / 2.0;
|
|
839
818
|
}
|
|
840
819
|
else if (easing === 'ease-in-out') {
|
|
841
|
-
|
|
820
|
+
x = (1.0 - Math.cos(x * Math.PI)) / 2.0;
|
|
842
821
|
}
|
|
843
|
-
Unit.scope(snapshot, callback,
|
|
822
|
+
Unit.scope(snapshot, callback, x);
|
|
844
823
|
}
|
|
845
824
|
}, interval);
|
|
846
825
|
self.on('finalize', () => {
|
|
@@ -1467,29 +1446,41 @@ const context = new AudioContext();
|
|
|
1467
1446
|
const master = context.createGain();
|
|
1468
1447
|
master.gain.value = 1.0;
|
|
1469
1448
|
master.connect(context.destination);
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1449
|
+
window.addEventListener('touchstart', initialize, true);
|
|
1450
|
+
window.addEventListener('mousedown', initialize, true);
|
|
1451
|
+
function initialize() {
|
|
1452
|
+
new Synthesizer({ oscillator: { type: 'sine' }, amp: { envelope: { amount: 0, ADSR: [0, 0, 0, 0] } } }).press(440);
|
|
1453
|
+
window.removeEventListener('touchstart', initialize, true);
|
|
1454
|
+
window.removeEventListener('mousedown', initialize, true);
|
|
1473
1455
|
}
|
|
1474
|
-
const
|
|
1456
|
+
const audio = {
|
|
1457
|
+
load(path) {
|
|
1458
|
+
return new AudioFile(path);
|
|
1459
|
+
},
|
|
1460
|
+
synthesizer(props) {
|
|
1461
|
+
return new Synthesizer(props);
|
|
1462
|
+
},
|
|
1463
|
+
get volume() {
|
|
1464
|
+
return master.gain.value;
|
|
1465
|
+
},
|
|
1466
|
+
set volume(value) {
|
|
1467
|
+
master.gain.value = value;
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
//----------------------------------------------------------------------------------------------------
|
|
1471
|
+
// audio file
|
|
1472
|
+
//----------------------------------------------------------------------------------------------------
|
|
1475
1473
|
class AudioFile {
|
|
1476
1474
|
constructor(path) {
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
this.buffer = response;
|
|
1487
|
-
})
|
|
1488
|
-
.catch(() => {
|
|
1489
|
-
console.warn(`"${path}" could not be loaded.`);
|
|
1490
|
-
});
|
|
1491
|
-
store.set(path, this.buffer);
|
|
1492
|
-
}
|
|
1475
|
+
this.promise = fetch(path)
|
|
1476
|
+
.then((response) => response.arrayBuffer())
|
|
1477
|
+
.then((response) => context.decodeAudioData(response))
|
|
1478
|
+
.then((response) => {
|
|
1479
|
+
this.buffer = response;
|
|
1480
|
+
})
|
|
1481
|
+
.catch(() => {
|
|
1482
|
+
console.warn(`"${path}" could not be loaded.`);
|
|
1483
|
+
});
|
|
1493
1484
|
this.start = null;
|
|
1494
1485
|
}
|
|
1495
1486
|
// set volume(value: number) {
|
|
@@ -1528,17 +1519,6 @@ class AudioFile {
|
|
|
1528
1519
|
}
|
|
1529
1520
|
}
|
|
1530
1521
|
}
|
|
1531
|
-
|
|
1532
|
-
function synthesizer(props) {
|
|
1533
|
-
return new Synthesizer(props);
|
|
1534
|
-
}
|
|
1535
|
-
window.addEventListener('touchstart', initialize, true);
|
|
1536
|
-
window.addEventListener('mousedown', initialize, true);
|
|
1537
|
-
function initialize() {
|
|
1538
|
-
new Synthesizer({ oscillator: { type: 'sine' }, amp: { envelope: { amount: 0, ADSR: [0, 0, 0, 0] } } }).press(440);
|
|
1539
|
-
window.removeEventListener('touchstart', initialize, true);
|
|
1540
|
-
window.removeEventListener('mousedown', initialize, true);
|
|
1541
|
-
}
|
|
1542
1522
|
const keymap = {
|
|
1543
1523
|
'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
|
|
1544
1524
|
'C1': 32.703, 'C#1': 34.648, 'D1': 36.708, 'D#1': 38.891, 'E1': 41.203, 'F1': 43.654, 'F#1': 46.249, 'G1': 48.999, 'G#1': 51.913, 'A1': 55.000, 'A#1': 58.270, 'B1': 61.735,
|
|
@@ -1554,23 +1534,21 @@ const notemap = {
|
|
|
1554
1534
|
'1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
|
|
1555
1535
|
};
|
|
1556
1536
|
class Synthesizer {
|
|
1557
|
-
constructor(props) {
|
|
1558
|
-
this.props = props;
|
|
1559
|
-
}
|
|
1537
|
+
constructor(props) { this.props = props; }
|
|
1560
1538
|
press(frequency, duration, wait) {
|
|
1561
1539
|
var _a;
|
|
1562
1540
|
const props = this.props;
|
|
1563
|
-
const
|
|
1564
|
-
const
|
|
1541
|
+
const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
|
|
1542
|
+
const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
|
|
1565
1543
|
const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
|
|
1566
1544
|
const nodes = {};
|
|
1567
1545
|
nodes.oscillator = context.createOscillator();
|
|
1568
1546
|
nodes.oscillator.type = props.oscillator.type;
|
|
1569
|
-
nodes.oscillator.frequency.value =
|
|
1547
|
+
nodes.oscillator.frequency.value = fv;
|
|
1570
1548
|
if (props.oscillator.LFO) {
|
|
1571
1549
|
nodes.oscillatorLFO = context.createOscillator();
|
|
1572
1550
|
nodes.oscillatorLFODepth = context.createGain();
|
|
1573
|
-
nodes.oscillatorLFODepth.gain.value =
|
|
1551
|
+
nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
|
|
1574
1552
|
nodes.oscillatorLFO.type = props.oscillator.LFO.type;
|
|
1575
1553
|
nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
|
|
1576
1554
|
nodes.oscillatorLFO.start(start);
|
|
@@ -1583,7 +1561,7 @@ class Synthesizer {
|
|
|
1583
1561
|
nodes.target.gain.value = 1.0;
|
|
1584
1562
|
nodes.amp.connect(nodes.target);
|
|
1585
1563
|
nodes.target.connect(master);
|
|
1586
|
-
if (props.filter
|
|
1564
|
+
if (props.filter) {
|
|
1587
1565
|
nodes.filter = context.createBiquadFilter();
|
|
1588
1566
|
nodes.filter.type = props.filter.type;
|
|
1589
1567
|
nodes.filter.frequency.value = props.filter.cutoff;
|
|
@@ -1605,22 +1583,22 @@ class Synthesizer {
|
|
|
1605
1583
|
nodes.convolverDepth.connect(master);
|
|
1606
1584
|
}
|
|
1607
1585
|
if (props.oscillator.envelope) {
|
|
1608
|
-
const amount =
|
|
1609
|
-
startEnvelope(nodes.oscillator.frequency,
|
|
1586
|
+
const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
|
|
1587
|
+
startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
|
|
1610
1588
|
}
|
|
1611
1589
|
if (props.amp.envelope) {
|
|
1612
1590
|
startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
|
|
1613
1591
|
}
|
|
1614
|
-
let stop = null;
|
|
1615
1592
|
nodes.oscillator.start(start);
|
|
1616
|
-
if (
|
|
1593
|
+
if (dv > 0) {
|
|
1617
1594
|
release();
|
|
1618
1595
|
}
|
|
1619
1596
|
else {
|
|
1620
1597
|
return { release };
|
|
1621
1598
|
}
|
|
1622
1599
|
function release() {
|
|
1623
|
-
|
|
1600
|
+
let stop = null;
|
|
1601
|
+
const end = dv > 0 ? dv : (context.currentTime - start);
|
|
1624
1602
|
if (props.amp.envelope) {
|
|
1625
1603
|
const ADSR = props.amp.envelope.ADSR;
|
|
1626
1604
|
const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
|
|
@@ -1634,8 +1612,8 @@ class Synthesizer {
|
|
|
1634
1612
|
nodes.oscillatorLFO.stop(stop);
|
|
1635
1613
|
}
|
|
1636
1614
|
if (props.oscillator.envelope) {
|
|
1637
|
-
const amount =
|
|
1638
|
-
stopEnvelope(nodes.oscillator.frequency,
|
|
1615
|
+
const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
|
|
1616
|
+
stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
|
|
1639
1617
|
}
|
|
1640
1618
|
if (props.amp.envelope) {
|
|
1641
1619
|
stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
|
|
@@ -1654,15 +1632,15 @@ class Synthesizer {
|
|
|
1654
1632
|
}, 2000);
|
|
1655
1633
|
}
|
|
1656
1634
|
function stopEnvelope(param, base, amount, ADSR) {
|
|
1657
|
-
const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(
|
|
1635
|
+
const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(dv / (ADSR[0] / 1000), 1.0);
|
|
1658
1636
|
if (rate < 1.0) {
|
|
1659
1637
|
param.cancelScheduledValues(start);
|
|
1660
1638
|
param.setValueAtTime(base, start);
|
|
1661
1639
|
param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
|
|
1662
1640
|
param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
|
|
1663
1641
|
}
|
|
1664
|
-
param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate,
|
|
1665
|
-
param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate,
|
|
1642
|
+
param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
|
|
1643
|
+
param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv) + ADSR[3] / 1000);
|
|
1666
1644
|
}
|
|
1667
1645
|
function startEnvelope(param, base, amount, ADSR) {
|
|
1668
1646
|
param.value = base;
|
|
@@ -1704,12 +1682,6 @@ const basics = {
|
|
|
1704
1682
|
AnalogStick,
|
|
1705
1683
|
DirectionalPad,
|
|
1706
1684
|
};
|
|
1707
|
-
const audio = {
|
|
1708
|
-
master,
|
|
1709
|
-
context,
|
|
1710
|
-
synthesizer,
|
|
1711
|
-
load
|
|
1712
|
-
};
|
|
1713
1685
|
const xnew = Object.assign(xnew$1, {
|
|
1714
1686
|
basics,
|
|
1715
1687
|
audio
|