@grame/faustwasm 0.12.2 → 0.13.1
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/.prettierrc +5 -0
- package/README.md +21 -3
- package/assets/standalone/faustwasm/index.d.ts +38 -16
- package/assets/standalone/faustwasm/index.js +1593 -404
- package/assets/standalone/faustwasm/index.js.map +2 -2
- package/dist/cjs/index.d.ts +38 -16
- package/dist/cjs/index.js +1592 -404
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs-bundle/index.d.ts +38 -16
- package/dist/cjs-bundle/index.js +1595 -406
- package/dist/cjs-bundle/index.js.map +2 -2
- package/dist/esm/index.d.ts +38 -16
- package/dist/esm/index.js +1593 -404
- package/dist/esm/index.js.map +2 -2
- package/dist/esm-bundle/index.d.ts +38 -16
- package/dist/esm-bundle/index.js +1595 -406
- package/dist/esm-bundle/index.js.map +2 -2
- package/eslint.config.js +11 -0
- package/package.json +57 -51
- package/sound.cpp +163 -0
- package/sound.dsp +4 -0
- package/sound.json +1 -0
- package/sound.wasm +0 -0
- package/src/FaustAudioWorkletCommunicator.ts +150 -143
- package/src/FaustAudioWorkletNode.ts +173 -75
- package/src/FaustAudioWorkletProcessor.ts +218 -90
- package/src/FaustCmajor.ts +9 -3
- package/src/FaustCompiler.ts +112 -35
- package/src/FaustDspGenerator.ts +589 -117
- package/src/FaustDspInstance.ts +58 -24
- package/src/FaustFFTAudioWorkletProcessor.ts +822 -612
- package/src/FaustOfflineProcessor.ts +187 -56
- package/src/FaustScriptProcessorNode.ts +156 -49
- package/src/FaustSensors.ts +426 -96
- package/src/FaustSvgDiagrams.ts +18 -5
- package/src/FaustWasmInstantiator.ts +224 -70
- package/src/FaustWebAudioDsp.ts +1060 -349
- package/src/LibFaust.ts +13 -4
- package/src/SoundfileReader.ts +175 -131
- package/src/WavDecoder.ts +42 -20
- package/src/WavEncoder.ts +35 -21
- package/src/copyWebStandaloneAssets.d.ts +20 -5
- package/src/copyWebStandaloneAssets.js +203 -75
- package/src/exports-bundle.ts +5 -5
- package/src/exports.ts +24 -24
- package/src/faust2CmajorFiles.d.ts +7 -3
- package/src/faust2cmajorFiles.js +10 -8
- package/src/faust2svgFiles.d.ts +7 -3
- package/src/faust2svgFiles.js +9 -7
- package/src/faust2wasmFiles.d.ts +10 -5
- package/src/faust2wasmFiles.js +34 -17
- package/src/faust2wavFiles.d.ts +12 -3
- package/src/faust2wavFiles.js +25 -12
- package/src/index-bundle-iife.ts +2 -2
- package/src/index-bundle.ts +1 -1
- package/src/index.ts +1 -1
- package/src/instantiateFaustModule.ts +43 -36
- package/src/instantiateFaustModuleFromFile.ts +30 -14
- package/src/types.ts +101 -25
- package/test/faustlive-wasm/faustwasm/index.d.ts +38 -16
- package/test/faustlive-wasm/faustwasm/index.js +1593 -404
- package/test/faustlive-wasm/faustwasm/index.js.map +2 -2
- package/test/web/create-faust-node.html +53 -0
- package/test/web/create-faust-node.js +181 -0
- package/test/organ/create-node.js +0 -252
- package/test/organ/dsp-meta.json +0 -131
- package/test/organ/dsp-module.wasm +0 -0
- package/test/organ/faust-pwa.js +0 -344
- package/test/organ/faust-ui/index.css +0 -442
- package/test/organ/faust-ui/index.css.map +0 -1
- package/test/organ/faust-ui/index.d.ts +0 -1
- package/test/organ/faust-ui/index.js +0 -3756
- package/test/organ/faust-ui/index.js.map +0 -1
- package/test/organ/faustwasm/index.d.ts +0 -1705
- package/test/organ/faustwasm/index.js +0 -4773
- package/test/organ/faustwasm/index.js.map +0 -7
- package/test/organ/icon.png +0 -0
- package/test/organ/index.html +0 -76
- package/test/organ/index.js +0 -69
- package/test/organ/manifest.json +0 -17
- package/test/organ/service-worker.js +0 -165
package/src/LibFaust.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FaustModule, LibFaustWasm, FaustInfoType } from
|
|
1
|
+
import type { FaustModule, LibFaustWasm, FaustInfoType } from './types';
|
|
2
2
|
|
|
3
3
|
export interface ILibFaust extends LibFaustWasm {
|
|
4
4
|
module(): FaustModule;
|
|
@@ -24,8 +24,18 @@ class LibFaust implements ILibFaust {
|
|
|
24
24
|
version() {
|
|
25
25
|
return this.fCompiler.version();
|
|
26
26
|
}
|
|
27
|
-
createDSPFactory(
|
|
28
|
-
|
|
27
|
+
createDSPFactory(
|
|
28
|
+
name: string,
|
|
29
|
+
code: string,
|
|
30
|
+
args: string,
|
|
31
|
+
useInternalMemory: boolean
|
|
32
|
+
) {
|
|
33
|
+
return this.fCompiler.createDSPFactory(
|
|
34
|
+
name,
|
|
35
|
+
code,
|
|
36
|
+
args,
|
|
37
|
+
useInternalMemory
|
|
38
|
+
);
|
|
29
39
|
}
|
|
30
40
|
deleteDSPFactory(cFactory: number) {
|
|
31
41
|
return this.fCompiler.deleteDSPFactory(cFactory);
|
|
@@ -51,7 +61,6 @@ class LibFaust implements ILibFaust {
|
|
|
51
61
|
toString() {
|
|
52
62
|
return `LibFaust module: ${this.fModule}, compiler: ${this.fCompiler}`;
|
|
53
63
|
}
|
|
54
|
-
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
export default LibFaust;
|
package/src/SoundfileReader.ts
CHANGED
|
@@ -1,131 +1,175 @@
|
|
|
1
|
-
import { FaustBaseWebAudioDsp } from
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
* @param
|
|
23
|
-
* @returns : the
|
|
24
|
-
*/
|
|
25
|
-
private static
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* @
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
1
|
+
import { FaustBaseWebAudioDsp } from './FaustWebAudioDsp';
|
|
2
|
+
import type {
|
|
3
|
+
AudioData,
|
|
4
|
+
FaustDspMeta,
|
|
5
|
+
FaustUIItem,
|
|
6
|
+
LooseFaustDspFactory
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
/** Read metadata and fetch soundfiles */
|
|
10
|
+
class SoundfileReader {
|
|
11
|
+
// Set the fallback paths
|
|
12
|
+
static get fallbackPaths() {
|
|
13
|
+
return [
|
|
14
|
+
location.href,
|
|
15
|
+
this.getParentUrl(location.href),
|
|
16
|
+
location.origin
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Extract the parent URL from an URL.
|
|
22
|
+
* @param url : the URL
|
|
23
|
+
* @returns : the parent URL
|
|
24
|
+
*/
|
|
25
|
+
private static getParentUrl(url: string) {
|
|
26
|
+
return url.substring(0, url.lastIndexOf('/') + 1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Convert an audio buffer to audio data.
|
|
31
|
+
*
|
|
32
|
+
* @param audioBuffer : the audio buffer to convert
|
|
33
|
+
* @returns : the audio data
|
|
34
|
+
*/
|
|
35
|
+
private static toAudioData(audioBuffer: AudioBuffer): AudioData {
|
|
36
|
+
const { sampleRate, numberOfChannels } = audioBuffer;
|
|
37
|
+
return {
|
|
38
|
+
sampleRate,
|
|
39
|
+
audioBuffer: new Array(numberOfChannels)
|
|
40
|
+
.fill(null)
|
|
41
|
+
.map((v, i) => audioBuffer.getChannelData(i))
|
|
42
|
+
} as AudioData;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Extract the URLs from the metadata.
|
|
47
|
+
*
|
|
48
|
+
* @param dspMeta : the metadata
|
|
49
|
+
* @returns : the URLs
|
|
50
|
+
*/
|
|
51
|
+
static findSoundfilesFromMeta(
|
|
52
|
+
dspMeta: FaustDspMeta
|
|
53
|
+
): LooseFaustDspFactory['soundfiles'] {
|
|
54
|
+
const soundfiles: LooseFaustDspFactory['soundfiles'] = {};
|
|
55
|
+
const callback = (item: FaustUIItem) => {
|
|
56
|
+
if (item.type === 'soundfile') {
|
|
57
|
+
const urls = FaustBaseWebAudioDsp.splitSoundfileNames(item.url);
|
|
58
|
+
// soundfiles.map[item.label] = urls;
|
|
59
|
+
urls.forEach((url) => (soundfiles[url] = null));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
FaustBaseWebAudioDsp.parseUI(dspMeta.ui, callback);
|
|
63
|
+
return soundfiles;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if the file exists.
|
|
67
|
+
*
|
|
68
|
+
* @param url : the url of the file to check
|
|
69
|
+
* @returns : true if the file exists, otherwise false
|
|
70
|
+
*/
|
|
71
|
+
private static async checkFileExists(url: string): Promise<boolean> {
|
|
72
|
+
try {
|
|
73
|
+
console.log(`"checkFileExists" url: ${url}`);
|
|
74
|
+
// Fetch in "HEAD" mode does not properly work with the service-worker.js cache, so use "GET" mode for now
|
|
75
|
+
//const response = await fetch(url, { method: "HEAD" });
|
|
76
|
+
const response = await fetch(url);
|
|
77
|
+
console.log(`"checkFileExists" response.ok: ${response.ok}`);
|
|
78
|
+
return response.ok; // Will be true if the status code is 200-299
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('Fetch error:', error);
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Fetch the soundfile.
|
|
87
|
+
*
|
|
88
|
+
* @param url : the url of the soundfile
|
|
89
|
+
* @param audioCtx : the audio context
|
|
90
|
+
* @returns : the audio data
|
|
91
|
+
*/
|
|
92
|
+
private static async fetchSoundfile(
|
|
93
|
+
url: string,
|
|
94
|
+
audioCtx: BaseAudioContext
|
|
95
|
+
): Promise<AudioData> {
|
|
96
|
+
console.log(`Loading sound file from ${url}`);
|
|
97
|
+
const response = await fetch(url);
|
|
98
|
+
if (!response.ok)
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Failed to load sound file from ${url}: ${response.statusText}`
|
|
101
|
+
);
|
|
102
|
+
// Decode the audio data
|
|
103
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
104
|
+
const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
|
|
105
|
+
return this.toAudioData(audioBuffer);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Load the soundfile.
|
|
110
|
+
*
|
|
111
|
+
* @param filename : the filename
|
|
112
|
+
* @param metaUrls : the metadata URLs
|
|
113
|
+
* @param soundfiles : the soundfiles
|
|
114
|
+
* @param audioCtx : the audio context
|
|
115
|
+
*/
|
|
116
|
+
private static async loadSoundfile(
|
|
117
|
+
filename: string,
|
|
118
|
+
metaUrls: string[],
|
|
119
|
+
soundfiles: LooseFaustDspFactory['soundfiles'],
|
|
120
|
+
audioCtx: BaseAudioContext
|
|
121
|
+
): Promise<void> {
|
|
122
|
+
if (soundfiles?.[filename]) return;
|
|
123
|
+
const urlsToCheck = [
|
|
124
|
+
filename,
|
|
125
|
+
...[...metaUrls, ...this.fallbackPaths].map(
|
|
126
|
+
(path) =>
|
|
127
|
+
new URL(filename, path.endsWith('/') ? path : `${path}/`)
|
|
128
|
+
.href
|
|
129
|
+
)
|
|
130
|
+
];
|
|
131
|
+
const checkResults = await Promise.all(
|
|
132
|
+
urlsToCheck.map((url) => this.checkFileExists(url))
|
|
133
|
+
);
|
|
134
|
+
const successIndex = checkResults.findIndex((r) => !!r);
|
|
135
|
+
if (successIndex === -1)
|
|
136
|
+
throw new Error(
|
|
137
|
+
`Failed to load sound file ${filename}, all check failed.`
|
|
138
|
+
);
|
|
139
|
+
soundfiles![filename] = await this.fetchSoundfile(
|
|
140
|
+
urlsToCheck[successIndex],
|
|
141
|
+
audioCtx
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Load the soundfiles, public API.
|
|
147
|
+
*
|
|
148
|
+
* @param dspMeta : the metadata
|
|
149
|
+
* @param soundfilesIn : the soundfiles
|
|
150
|
+
* @param audioCtx : the audio context
|
|
151
|
+
* @returns : the soundfiles
|
|
152
|
+
*/
|
|
153
|
+
static async loadSoundfiles(
|
|
154
|
+
dspMeta: FaustDspMeta,
|
|
155
|
+
soundfilesIn: LooseFaustDspFactory['soundfiles'],
|
|
156
|
+
audioCtx: BaseAudioContext
|
|
157
|
+
): Promise<LooseFaustDspFactory['soundfiles']> {
|
|
158
|
+
const metaUrls = FaustBaseWebAudioDsp.extractUrlsFromMeta(dspMeta);
|
|
159
|
+
const soundfiles = this.findSoundfilesFromMeta(dspMeta);
|
|
160
|
+
for (const id in soundfiles) {
|
|
161
|
+
if (soundfilesIn?.[id]) {
|
|
162
|
+
soundfiles[id] = soundfilesIn[id];
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
await this.loadSoundfile(id, metaUrls, soundfiles, audioCtx);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error(error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return soundfiles;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default SoundfileReader;
|
package/src/WavDecoder.ts
CHANGED
|
@@ -19,12 +19,12 @@ class WavDecoder {
|
|
|
19
19
|
static decode(buffer: ArrayBuffer, options?: WavDecoderOptions) {
|
|
20
20
|
const dataView = new DataView(buffer);
|
|
21
21
|
const reader = new Reader(dataView);
|
|
22
|
-
if (reader.string(4) !==
|
|
23
|
-
throw new TypeError(
|
|
22
|
+
if (reader.string(4) !== 'RIFF') {
|
|
23
|
+
throw new TypeError('Invalid WAV file');
|
|
24
24
|
}
|
|
25
25
|
reader.uint32(); // skip file length
|
|
26
|
-
if (reader.string(4) !==
|
|
27
|
-
throw new TypeError(
|
|
26
|
+
if (reader.string(4) !== 'WAVE') {
|
|
27
|
+
throw new TypeError('Invalid WAV file');
|
|
28
28
|
}
|
|
29
29
|
let format: Format | null = null;
|
|
30
30
|
let audioData: {
|
|
@@ -36,10 +36,15 @@ class WavDecoder {
|
|
|
36
36
|
do {
|
|
37
37
|
const chunkType = reader.string(4);
|
|
38
38
|
const chunkSize = reader.uint32();
|
|
39
|
-
if (chunkType ===
|
|
39
|
+
if (chunkType === 'fmt ') {
|
|
40
40
|
format = this.decodeFormat(reader, chunkSize);
|
|
41
|
-
} else if (chunkType ===
|
|
42
|
-
audioData = this.decodeData(
|
|
41
|
+
} else if (chunkType === 'data') {
|
|
42
|
+
audioData = this.decodeData(
|
|
43
|
+
reader,
|
|
44
|
+
chunkSize,
|
|
45
|
+
format as Format,
|
|
46
|
+
options || {}
|
|
47
|
+
);
|
|
43
48
|
} else {
|
|
44
49
|
reader.skip(chunkSize);
|
|
45
50
|
}
|
|
@@ -48,12 +53,14 @@ class WavDecoder {
|
|
|
48
53
|
}
|
|
49
54
|
private static decodeFormat(reader: Reader, chunkSize: number) {
|
|
50
55
|
const formats = {
|
|
51
|
-
0x0001:
|
|
52
|
-
0x0003:
|
|
56
|
+
0x0001: 'lpcm',
|
|
57
|
+
0x0003: 'lpcm'
|
|
53
58
|
};
|
|
54
59
|
const formatId = reader.uint16();
|
|
55
|
-
if (!
|
|
56
|
-
throw new TypeError(
|
|
60
|
+
if (!Object.prototype.hasOwnProperty.call(formats, formatId)) {
|
|
61
|
+
throw new TypeError(
|
|
62
|
+
'Unsupported format in WAV file: 0x' + formatId.toString(16)
|
|
63
|
+
);
|
|
57
64
|
}
|
|
58
65
|
const format: Format = {
|
|
59
66
|
formatId: formatId,
|
|
@@ -67,14 +74,21 @@ class WavDecoder {
|
|
|
67
74
|
reader.skip(chunkSize - 16);
|
|
68
75
|
return format;
|
|
69
76
|
}
|
|
70
|
-
private static decodeData(
|
|
77
|
+
private static decodeData(
|
|
78
|
+
reader: Reader,
|
|
79
|
+
chunkSizeIn: number,
|
|
80
|
+
format: Format,
|
|
81
|
+
options: WavDecoderOptions
|
|
82
|
+
) {
|
|
71
83
|
const chunkSize = Math.min(chunkSizeIn, reader.remain());
|
|
72
84
|
const length = Math.floor(chunkSize / format.blockSize);
|
|
73
85
|
const numberOfChannels = format.numberOfChannels;
|
|
74
86
|
const sampleRate = format.sampleRate;
|
|
75
87
|
const channelData: Float32Array[] = new Array(numberOfChannels);
|
|
76
88
|
for (let ch = 0; ch < numberOfChannels; ch++) {
|
|
77
|
-
const AB = options.shared
|
|
89
|
+
const AB = options.shared
|
|
90
|
+
? globalThis.SharedArrayBuffer || globalThis.ArrayBuffer
|
|
91
|
+
: globalThis.ArrayBuffer;
|
|
78
92
|
const ab = new AB(length * Float32Array.BYTES_PER_ELEMENT);
|
|
79
93
|
channelData[ch] = new Float32Array(ab);
|
|
80
94
|
}
|
|
@@ -86,12 +100,20 @@ class WavDecoder {
|
|
|
86
100
|
channelData
|
|
87
101
|
};
|
|
88
102
|
}
|
|
89
|
-
private static readPCM(
|
|
103
|
+
private static readPCM(
|
|
104
|
+
reader: Reader,
|
|
105
|
+
channelData: Float32Array[],
|
|
106
|
+
length: number,
|
|
107
|
+
format: Format,
|
|
108
|
+
options: WavDecoderOptions
|
|
109
|
+
) {
|
|
90
110
|
const bitDepth = format.bitDepth;
|
|
91
|
-
const decoderOption = format.float ?
|
|
92
|
-
const methodName =
|
|
111
|
+
const decoderOption = format.float ? 'f' : options.symmetric ? 's' : '';
|
|
112
|
+
const methodName = ('pcm' +
|
|
113
|
+
bitDepth +
|
|
114
|
+
decoderOption) as `pcm${8 | 16 | 32}${'f' | 's' | ''}`;
|
|
93
115
|
if (!(reader as any)[methodName]) {
|
|
94
|
-
throw new TypeError(
|
|
116
|
+
throw new TypeError('Not supported bit depth: ' + format.bitDepth);
|
|
95
117
|
}
|
|
96
118
|
const read: () => number = (reader as any)[methodName].bind(reader);
|
|
97
119
|
const numberOfChannels = format.numberOfChannels;
|
|
@@ -136,7 +158,7 @@ class Reader {
|
|
|
136
158
|
return data;
|
|
137
159
|
}
|
|
138
160
|
string(n: number) {
|
|
139
|
-
let data =
|
|
161
|
+
let data = '';
|
|
140
162
|
for (let i = 0; i < n; i++) {
|
|
141
163
|
data += String.fromCharCode(this.uint8());
|
|
142
164
|
}
|
|
@@ -166,7 +188,7 @@ class Reader {
|
|
|
166
188
|
const x0 = this.dataView.getUint8(this.pos + 0);
|
|
167
189
|
const x1 = this.dataView.getUint8(this.pos + 1);
|
|
168
190
|
const x2 = this.dataView.getUint8(this.pos + 2);
|
|
169
|
-
const xx =
|
|
191
|
+
const xx = x0 + (x1 << 8) + (x2 << 16);
|
|
170
192
|
|
|
171
193
|
const data = xx > 0x800000 ? xx - 0x1000000 : xx;
|
|
172
194
|
this.pos += 3;
|
|
@@ -176,7 +198,7 @@ class Reader {
|
|
|
176
198
|
const x0 = this.dataView.getUint8(this.pos + 0);
|
|
177
199
|
const x1 = this.dataView.getUint8(this.pos + 1);
|
|
178
200
|
const x2 = this.dataView.getUint8(this.pos + 2);
|
|
179
|
-
const xx =
|
|
201
|
+
const xx = x0 + (x1 << 8) + (x2 << 16);
|
|
180
202
|
|
|
181
203
|
const data = xx > 0x800000 ? xx - 0x1000000 : xx;
|
|
182
204
|
this.pos += 3;
|
package/src/WavEncoder.ts
CHANGED
|
@@ -24,11 +24,12 @@ class WavEncoder {
|
|
|
24
24
|
const numberOfChannels = audioBuffer.length;
|
|
25
25
|
const length = audioBuffer[0].length;
|
|
26
26
|
const { shared, float } = options;
|
|
27
|
-
const bitDepth = float ? 32 :
|
|
27
|
+
const bitDepth = float ? 32 : options.bitDepth | 0 || 16;
|
|
28
28
|
const byteDepth = bitDepth >> 3;
|
|
29
29
|
const byteLength = length * numberOfChannels * byteDepth;
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const AB = shared
|
|
31
|
+
? globalThis.SharedArrayBuffer || globalThis.ArrayBuffer
|
|
32
|
+
: globalThis.ArrayBuffer;
|
|
32
33
|
const ab = new AB((44 + byteLength) * Uint8Array.BYTES_PER_ELEMENT);
|
|
33
34
|
const dataView = new DataView(ab);
|
|
34
35
|
const writer = new Writer(dataView);
|
|
@@ -47,11 +48,18 @@ class WavEncoder {
|
|
|
47
48
|
return ab;
|
|
48
49
|
}
|
|
49
50
|
private static writeHeader(writer: Writer, format: Format) {
|
|
50
|
-
const {
|
|
51
|
-
|
|
51
|
+
const {
|
|
52
|
+
formatId,
|
|
53
|
+
sampleRate,
|
|
54
|
+
bitDepth,
|
|
55
|
+
numberOfChannels,
|
|
56
|
+
length,
|
|
57
|
+
byteDepth
|
|
58
|
+
} = format;
|
|
59
|
+
writer.string('RIFF');
|
|
52
60
|
writer.uint32(writer.dataView.byteLength - 8);
|
|
53
|
-
writer.string(
|
|
54
|
-
writer.string(
|
|
61
|
+
writer.string('WAVE');
|
|
62
|
+
writer.string('fmt ');
|
|
55
63
|
writer.uint32(16);
|
|
56
64
|
writer.uint16(formatId);
|
|
57
65
|
writer.uint16(numberOfChannels);
|
|
@@ -59,11 +67,15 @@ class WavEncoder {
|
|
|
59
67
|
writer.uint32(sampleRate * numberOfChannels * byteDepth);
|
|
60
68
|
writer.uint16(numberOfChannels * byteDepth);
|
|
61
69
|
writer.uint16(bitDepth);
|
|
62
|
-
writer.string(
|
|
70
|
+
writer.string('data');
|
|
63
71
|
writer.uint32(length * numberOfChannels * byteDepth);
|
|
64
72
|
return writer.pos;
|
|
65
73
|
}
|
|
66
|
-
private static writeData(
|
|
74
|
+
private static writeData(
|
|
75
|
+
writer: Writer,
|
|
76
|
+
audioBuffer: Float32Array[],
|
|
77
|
+
format: Format
|
|
78
|
+
) {
|
|
67
79
|
const { bitDepth, float, length, numberOfChannels, symmetric } = format;
|
|
68
80
|
if (bitDepth === 32 && float) {
|
|
69
81
|
const { dataView, pos } = writer;
|
|
@@ -81,14 +93,16 @@ class WavEncoder {
|
|
|
81
93
|
}
|
|
82
94
|
return;
|
|
83
95
|
}
|
|
84
|
-
const encoderOption = float ?
|
|
85
|
-
const methodName =
|
|
96
|
+
const encoderOption = float ? 'f' : symmetric ? 's' : '';
|
|
97
|
+
const methodName = 'pcm' + bitDepth + encoderOption;
|
|
86
98
|
|
|
87
99
|
if (!(writer as any)[methodName]) {
|
|
88
|
-
throw new TypeError(
|
|
100
|
+
throw new TypeError('Not supported bit depth: ' + bitDepth);
|
|
89
101
|
}
|
|
90
102
|
|
|
91
|
-
const write: (value: number) => void = (writer as any)[methodName].bind(
|
|
103
|
+
const write: (value: number) => void = (writer as any)[methodName].bind(
|
|
104
|
+
writer
|
|
105
|
+
);
|
|
92
106
|
|
|
93
107
|
for (let i = 0; i < length; i++) {
|
|
94
108
|
for (let j = 0; j < numberOfChannels; j++) {
|
|
@@ -126,14 +140,14 @@ class Writer {
|
|
|
126
140
|
value = Math.max(-1, Math.min(value, +1));
|
|
127
141
|
value = (value * 0.5 + 0.5) * 255;
|
|
128
142
|
value = Math.round(value) | 0;
|
|
129
|
-
this.dataView.setUint8(this.pos, value/* , true*/);
|
|
143
|
+
this.dataView.setUint8(this.pos, value /* , true*/);
|
|
130
144
|
this.pos += 1;
|
|
131
145
|
}
|
|
132
146
|
pcm8s(valueIn: number) {
|
|
133
147
|
let value = valueIn;
|
|
134
148
|
value = Math.round(value * 128) + 128;
|
|
135
149
|
value = Math.max(0, Math.min(value, 255));
|
|
136
|
-
this.dataView.setUint8(this.pos, value/* , true*/);
|
|
150
|
+
this.dataView.setUint8(this.pos, value /* , true*/);
|
|
137
151
|
this.pos += 1;
|
|
138
152
|
}
|
|
139
153
|
pcm16(valueIn: number) {
|
|
@@ -157,9 +171,9 @@ class Writer {
|
|
|
157
171
|
value = value < 0 ? 0x1000000 + value * 8388608 : value * 8388607;
|
|
158
172
|
value = Math.round(value) | 0;
|
|
159
173
|
|
|
160
|
-
const x0 = (value >> 0) &
|
|
161
|
-
const x1 = (value >> 8) &
|
|
162
|
-
const x2 = (value >> 16) &
|
|
174
|
+
const x0 = (value >> 0) & 0xff;
|
|
175
|
+
const x1 = (value >> 8) & 0xff;
|
|
176
|
+
const x2 = (value >> 16) & 0xff;
|
|
163
177
|
|
|
164
178
|
this.dataView.setUint8(this.pos + 0, x0);
|
|
165
179
|
this.dataView.setUint8(this.pos + 1, x1);
|
|
@@ -171,9 +185,9 @@ class Writer {
|
|
|
171
185
|
value = Math.round(value * 8388608);
|
|
172
186
|
value = Math.max(-8388608, Math.min(value, 8388607));
|
|
173
187
|
|
|
174
|
-
const x0 = (value >> 0) &
|
|
175
|
-
const x1 = (value >> 8) &
|
|
176
|
-
const x2 = (value >> 16) &
|
|
188
|
+
const x0 = (value >> 0) & 0xff;
|
|
189
|
+
const x1 = (value >> 8) & 0xff;
|
|
190
|
+
const x2 = (value >> 16) & 0xff;
|
|
177
191
|
|
|
178
192
|
this.dataView.setUint8(this.pos + 0, x0);
|
|
179
193
|
this.dataView.setUint8(this.pos + 1, x1);
|
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
declare const copyWebStandaloneAssets: (
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
declare const copyWebStandaloneAssets: (
|
|
2
|
+
outputDir: string,
|
|
3
|
+
dspName: string,
|
|
4
|
+
poly?: boolean,
|
|
5
|
+
effect?: boolean
|
|
6
|
+
) => void;
|
|
7
|
+
declare const copyWebPWAAssets: (
|
|
8
|
+
outputDir: string,
|
|
9
|
+
dspName: string,
|
|
10
|
+
poly?: boolean,
|
|
11
|
+
effect?: boolean
|
|
12
|
+
) => void;
|
|
13
|
+
declare const copyWebTemplateAssets: (
|
|
14
|
+
outputDir: string,
|
|
15
|
+
dspName: string,
|
|
16
|
+
poly?: boolean,
|
|
17
|
+
effect?: boolean
|
|
18
|
+
) => void;
|
|
19
|
+
|
|
20
|
+
export { copyWebStandaloneAssets, copyWebPWAAssets, copyWebTemplateAssets };
|