英文:
Angular: Build standalone bundle for worklet that shares code with the main project
问题
我有一个使用 Angular 13 的音频应用程序,使用了音频工作线程。UI 部分是 Angular/TypeScript,但工作线程是纯 JavaScript。通过调用 Worklet.addModule()
并提供存储在 assets
文件夹中的 JavaScript 文件路径来加载工作线程。
我想用 TypeScript 开发工作线程,并与 UI 共享一些代码。由于 addModule()
需要单个 JavaScript 文件,我正在尝试学习如何将工作线程代码和任何导入的模块转译为独立的 JavaScript 文件,而项目的 UI 部分则像往常一样捆绑。
我对 Angular 和 TypeScript 感到相当熟悉,但因为觉得解决方案可能在其中,所以刚刚开始学习更多关于 tsc 和 webpack。我走在正确的道路上吗?
我已经在寻找解决方案,并发现了一些与 Angular 和 AudioWorklet 相关的信息,但没有取得突破。我已经让 Web Workers 与 Angular 协同工作,但还没有找到将其扩展到工作线程的方法。
英文:
I have an Angular 13 audio application that uses an audio worklet. The UI is Angular/Typescript but the worklet is straight Javascript. The worklet is loaded by calling Worklet.addModule()
with the path to the javascript file stored in the assets
folder.
I would like to develop the worklet in Typescript and share some of the code with the UI. Since addModule()
needs a single Javascript file I'm trying to learn how to get the worklet code and any imported modules transpiled into a standalone Javascript file while the UI part of the project is bundled as usual.
I'm pretty comfortable with Angular and Typescript but have just started to learn more about tsc and webpack because I feel like the solution is somewhere in there. Am I on the right track?
I've poked around for solutions and have found a few hits related to Angular and AudioWorklet, but no breakthroughs. I've gotten web workers to work with Angular but haven't found a way to extend that to worklets.
答案1
得分: 0
我找到了一个满足我两个要求的解决方案:
- 用Typescript编写AudioWorkletProcessor
- 与Angular UI共享Typescript模块
由于现代化工作代码也是一个目标,我将Angular从13升级到了16,将Webpack升级到了版本5,将Typescript升级到了5.0.2。我认为以下解决方案对于旧版本也应该适用。
高级别
- 安装
custom-webpack
NPM包 - 安装
@types/audioworklet
NPM包 - 向现有的Angular项目添加一个子项目
- 编辑工作子项目的webpack和tsconfig文件
配置
angular.json
原始项目的angular.json
设置保持不变。工作子项目的构建设置配置为使用custom-webpack
作为构建器
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "projects/reader-worklet/webpack-reader.config.js",
"verbose": {
"properties": [
"entry"
]
}
},
"outputPath": "dist/reader-worklet",
"main": "projects/reader-worklet/src/worklet.ts",
"tsConfig": "projects/reader-worklet/tsconfig.app.json",
...
}
}
}
webpack-reader.config.js
工作线程的Webpack配置很简单。产生影响的关键设置是将target: "webworker"
设置为避免DOM引用,以及optimization.runtimeChunk: false
以便将rumtime.js
不作为单独的.js文件构建,而是包含在单一的输出文件render-worklet.js
中。是的,我在一个地方称之为reader-worklet
,在另一个地方称之为render-worklet
,我仍然不确定应该怎么称呼它
"use strict";
const path = require('path');
const mode = process.env.NODE_ENV === "production" ? "production" : "development";
module.exports = {
// WARNING: MUST set the 'mode' manually because it isn't done by NX/NG cli
mode,
entry: {
main: {
import: path.resolve(__dirname, './src/worklet.ts'),
filename: "render-worklet.js",
},
},
output: {
clean: true
},
optimization: {
runtimeChunk: false
},
target: "webworker",
devtool: 'source-map',
module: {
rules: [],
},
plugins: []
};
tsconfig.app.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/app",
"module": "es2022",
"esModuleInterop": true,
"target": "es2022",
"lib": ["es2022"],
"moduleResolution": "node",
"sourceMap": true,
"types": [ "@types/audioworklet"]
},
"files": ["src/worklet.ts"],
"include": [ "src/**/*.d.ts"]
}
AudioWorkletProcessor
这是一个基本的类,扩展了AudioWorkletProcessor
。它导入了一个与UI代码共享的类WTrackSettings
。这里没有用于任何内容,只是用来验证它能够在工作线程中导入和使用的,process()
方法生成一个方波输出。
import {WTrackSettings} from "../../../src/app/include/shared_defs";
export class TSWorklet extends AudioWorkletProcessor {
active = true;
constructor() {
super();
this.sayHello();
}
public sayHello(): void {
const ts: WTrackSettings = new WTrackSettings();
console.table(ts);
console.log("Hello!!!");
}
process(inputs: Float32Array[][], outputs: Float32Array[][], parameters: Record<string, Float32Array>): boolean {
let buflen = 0;
let outBuffer = outputs[0];
if (outBuffer.length > 0) {
buflen = outBuffer[0].length;
}
let frameCount = 0;
for (; frameCount < buflen / 2; frameCount++) {
outBuffer[0][frameCount] = 1;
outBuffer[1][frameCount] = 0
}
for (; frameCount < buflen; frameCount++) {
outBuffer[0][frameCount] = 0;
outBuffer[1][frameCount] = 1
}
return this.active;
}
}
registerProcessor('ts-reader-worklet', TSWorklet);
我没有展示AudioWorkletNode
的实现,因为当AudioWorkletProcessor
用JavaScript编写时,它完全相同。
英文:
I found a solution that gives me the two requirements I was after:
- Write AudioWorkletProcessor in Typescript
- Share Typescript modules with the Angular UI
Since modernizing the worklet code was also a goal I upgraded Angular from 13 to 16, Webpack to version 5 and Typescript to 5.0.2. I don't see any reason why the following solution wouldn't work for older versions.
High level
- Install
custom-webpack
NPM package - Install
@types/audioworklet
NPM package - Add a subproject to the existing Angular project
- Edit webpack and tsconfig files for worklet subproject
Configuration
angular.json
The angular.json
settings for the original project are unchanged. The worklet sub-project build settings are configured to use custom-webpack
as the builder
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "projects/reader-worklet/webpack-reader.config.js",
"verbose": {
"properties": [
"entry"
]
}
},
"outputPath": "dist/reader-worklet",
"main": "projects/reader-worklet/src/worklet.ts",
"tsConfig": "projects/reader-worklet/tsconfig.app.json",
...
webpack-reader.config.js
The webpack configuration for the worklet is minimal. The key settings that made a difference were to set target:"webworker"
to avoid DOM references and optimization.runtimeChunk: false
so that rumtime.js
is not built as a separate .js file, but rather included in the single output file render-worklet.js
. Yes, I call it reader-worklet
in one place and render-worklet
in another, I'm still not sure what to call it
"use strict";
const path = require('path');
const mode = process.env.NODE_ENV === "production" ? "production" : "development";
module.exports = {
// WARNING: MUST set the 'mode' manually because it isn't done by NX/NG cli
mode,
entry: {
main: {
import: path.resolve(__dirname, './src/worklet.ts'),
filename: "render-worklet.js",
},
},
output: {
clean: true
},
optimization: {
runtimeChunk: false
},
target: "webworker",
devtool: 'source-map',
module: {
rules: [],
},
plugins: []
};
tsconfig.app.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/app",
"module": "es2022",
"esModuleInterop": true,
"target": "es2022",
"lib": ["es2022"],
"moduleResolution": "node",
"sourceMap": true,
"types": [ "@types/audioworklet"]
},
"files": ["src/worklet.ts"],
"include": [ "src/**/*.d.ts"]
}
AudioWorkletProcessor
Here is a basic class that extends AudioWorkletProcessor
. It imports a class WTrackSettings
that is shared with the UI code. It's not used for anything here, just dumped to the console to verify that it's able to be imported and used in the worklet. The process()
method generates a square wave output.
import {WTrackSettings} from "../../../src/app/include/shared_defs";
export class TSWorklet extends AudioWorkletProcessor {
active = true;
constructor() {
super();
this.sayHello();
}
public sayHello(): void {
const ts: WTrackSettings = new WTrackSettings();
console.table(ts);
console.log("Hello!!!");
}
process(inputs: Float32Array[][], outputs: Float32Array[][], parameters: Record<string, Float32Array>): boolean {
let buflen = 0;
let outBuffer = outputs[0];
if (outBuffer.length > 0) {
buflen = outBuffer[0].length;
}
let frameCount = 0;
for (; frameCount < buflen / 2; frameCount++) {
outBuffer[0][frameCount] = 1;
outBuffer[1][frameCount] = 0
}
for (; frameCount < buflen; frameCount++) {
outBuffer[0][frameCount] = 0;
outBuffer[1][frameCount] = 1
}
return this.active;
}
}
registerProcessor('ts-reader-worklet', TSWorklet);
I haven't shown the AudioWorkletNode
implementation because it's exactly the same as when the AudioWorkletProcessor
is written in Javascript.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论