Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web #103

Merged
merged 34 commits into from
Jul 15, 2024
Merged

Web #103

Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2f5e680
moved into new folder some sources
alnitak Jun 23, 2024
218c800
++
alnitak Jun 29, 2024
9ed9b83
Revert "++"
alnitak Jun 29, 2024
617ad7c
++
alnitak Jun 29, 2024
422764a
it works
alnitak Jun 29, 2024
26a17fe
ok
alnitak Jun 29, 2024
52a92fa
potato
alnitak Jun 29, 2024
2cac7ad
ananas
alnitak Jun 29, 2024
bf969bd
Loader splitted for web
alnitak Jun 29, 2024
512275e
ananas
alnitak Jun 30, 2024
1957918
it works!
alnitak Jul 1, 2024
90c5bb8
chore on AudioData
alnitak Jul 1, 2024
08e228f
added loadMode to loadMem() & dart fix
alnitak Jul 1, 2024
46b7cb3
added WEB_NOTES.md
alnitak Jul 2, 2024
948b825
fix pick audio in visualizer page
alnitak Jul 2, 2024
d3616e0
prevent to stream event if already did in the callback
alnitak Jul 4, 2024
8171263
fixed largest32BitInt
alnitak Jul 4, 2024
8b566f3
try to speed up loadMem
alnitak Jul 4, 2024
36441a0
examples fixed
alnitak Jul 4, 2024
285f11a
WEB_NOTES update
alnitak Jul 4, 2024
4cc95f0
wasm built with full optimizations
alnitak Jul 4, 2024
e4a9f92
tests.dart rewritten with a GUI
alnitak Jul 5, 2024
e3c61b6
workarounf for loadMem() problem
alnitak Jul 6, 2024
ed8fb01
WEB_NOTES update
alnitak Jul 8, 2024
ecc4d0a
chore
alnitak Jul 8, 2024
5983e36
chore
alnitak Jul 8, 2024
8611db9
dart fix
alnitak Jul 8, 2024
065f161
added loader stub
alnitak Jul 9, 2024
a34835b
some review fixes
alnitak Jul 9, 2024
c284151
some review fixes
alnitak Jul 9, 2024
425536e
some review fixes
alnitak Jul 9, 2024
d6c635b
moved the Worker class to web/worker.dart
alnitak Jul 10, 2024
402c60f
removed js_util in favor of js_interop_unsafe
alnitak Jul 10, 2024
c0ad160
WEB_NOTES.md update
alnitak Jul 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@
"intelliSenseMode": "linux-clang-x64",
"configurationProvider": "ms-vscode.cmake-tools"
},
{
"name": "Chrome",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/src",
"/usr/lib/emscripten/system/include"
],
"defines": ["WITH_MINIAUDIO", "DR_MP3_IMPLEMENTATION", "__EMSCRIPTEN__"], // to see the code in between "#if defined"
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "${default}",
"configurationProvider": "ms-vscode.cmake-tools"
},
{
"name": "Win32",
"includePath": [
Expand Down
6 changes: 6 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
"request": "launch",
"program": "${workspaceFolder}/example/build/linux/x64/debug/bundle/flutter_soloud_example",
"cwd": "${workspaceFolder}"
},
{
"name": "Chrome",
"type": "chrome",
"preLaunchTask": "compile web debug",
"request": "launch"
}
]
}
28 changes: 3 additions & 25 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,27 @@
{
"label": "compile linux debug",
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/main.dart --debug",
// "args": ["build", "linux"],
"type": "shell"
},
{
"label": "compile linux test debug",
"command": "cd ${workspaceFolder}/example; flutter build linux -t tests/tests.dart --debug",
// "args": ["build", "linux"],
"type": "shell"
},
{
"label": "compile windows debug verbose",
"command": "cd ${workspaceFolder}/example; flutter build windows -t lib/main.dart --debug --verbose",
// "args": ["build", "linux"],
"type": "shell"
},
{
"label": "compile windows debug",
"command": "cd ${workspaceFolder}/example; flutter build windows -t lib/main.dart --debug",
// "args": ["build", "linux"],
"type": "shell"
},
{
"type": "cppbuild",
"label": "C/C++: gcc build active file",
"command": "/usr/bin/gcc",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
"label": "compile web debug",
"command": "cd ${workspaceFolder}/example; flutter run -d chrome --web-renderer canvaskit --web-browser-flag '--disable-web-security' -t lib/main.dart --release",
"type": "shell"
}
]
}
84 changes: 84 additions & 0 deletions WEB_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Web Notes


## Description

The web platform is now supported, but some testing is welcome.

## How to use

In the `web` directory, there is a `compile_wasm.sh` script that generates the `.js` and `.wasm` files for the native C code located in the `src` dir. Run it after installing *emscripten*. There is also a `compile_web.sh` to compile the web worker needed by native code to communicate with Dart. The generated files are already provided, but if it is needed to modify C/C++ code or the `web/worker.dart` code, the scripts must be run to reflect the changes.

The `compile_wasm.sh` script uses the `-O3` code optimization flag.

To see a better errors logs, use `-O0 -g -s ASSERTIONS=1` in `compile_wasm.sh`.

To add the plugin to a web app, add the following line to the `<body>` section of `web/index.html`:
`<script src="assets/packages/flutter_soloud/web/libflutter_soloud_plugin.js" defer></script>`

## The problems

The AudioIsolate was causing many problems related to web workers when trying to port to web. It was used to monitor all the sound states and to send some operations to the native code. These operations were not working inside a JS Worker because web audio is not supported.

The `AudioIsolate` [has been removed](https://github.com/alnitak/flutter_soloud/pull/89) and all the logic has been implemented natively. Events like `voice ended` are sent from C back to Dart. However, since it is not possible to call Dart from a native thread (the audio thread), a new web worker is created using the WASM `EM_ASM` directive. This allows sending the `audio finished` event back to Dart via the worker. Here an example just to let me explain and receive suggestions/critiques:
```CPP
// On C side define an inline function to create the Web Worker and another
// to send messages to the listening worker in Dart.

void createWorkerInWasm()
{
EM_ASM({
...
// Create a new Worker
var workerUri = "assets/packages/flutter_soloud/web/worker.dart.js";
Module.wasmWorker = new Worker(workerUri);
});
}

void sendToWorker(const char *message, int value)
{
EM_ASM({
// Send the message
Module.wasmWorker.postMessage(JSON.stringify({
"message" : UTF8ToString($0),
"value" : $1
}));
}, message, value);
}

```

```Dart
// On Dart side listen to the messages.
@JS('Module.wasmWorker')
external web.Worker wasmWorker;

void setDartEventCallbacks() {
web.Worker _worker = wasmWorker;
_worker?.onmessage = ((web.MessageEvent event) {
_outputController?.add(event.data.dartify());
}).toJS;
}
```

The same problem happens using `dart:ffi`, it is also not possible to call a function directly from a native thread (audio thread) to Dart without using send. Here, `NativeCallable` helps. With `NativeCallable`, it's not necessary to import sendPort and receivePort into the native code, which are part of ffi and thus not compatible with the web.


## Notes

Acquiring audio data is (was?) experimental, the following methods are now deprecated and this functionality is now in the `AudioData` class.
- `@experimental SoLoud.getAudioTexture2D()`
- `@experimental SoLoudCapture.getCaptureAudioTexture2D()`

It is not possible to read a local audio file directly on the web. For this reason, `loadMem()` has been added, which requires the `Uint8List` byte buffer of the audio file.
**IMPORTANT**: `loadMem()` with mode `LoadMode.memory` used the on web platform will freezy the UI for the time needed to decompress the audio file. Please use it with mode `LoadMode.disk` or load your sound when the app starts.

In addition to the `getAudioTexture2D`, with `AudioData` class is now possible to acquire audio as `linear`, which represents the FFT+wave vector, or just the `wave` data vector for better performance. With this class, it is also possible to choose to acquire data from the player or from the microphone.


**`loadUrl()`** produces the following error when the app is run on a remote server:
>> Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.

This is due for the default beavior of http servers which don't allow to make requests outside their domain. Refer [here](https://enable-cors.org/server.html) to learn how to enable your server to handle this situation.
Instead, if you run the app locally, you could run the app with something like the following command:
`flutter run -d chrome --web-renderer canvaskit --web-browser-flag '--disable-web-security' -t lib/main.dart --release`
16 changes: 8 additions & 8 deletions example/.metadata
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
# This file should be version controlled and should not be manually edited.

version:
revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
channel: stable
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
- platform: windows
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: web
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49

# User provided section

Expand Down
4 changes: 2 additions & 2 deletions example/assets/shaders/test9.frag
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,11 @@ void mainImage( out vec4 fragColor, in vec2 fragCoord )

// rotate view
float a;
a = -0.6;
a = -0.8;
rd = rotateX(rd, a);
ro = rotateX(ro, a);

a = 1.5;
a = 1.8;
// a = sin(iTime)*.5 + 1.570796327;
rd = rotateY(rd, a);
ro = rotateY(ro, a);
Expand Down
12 changes: 6 additions & 6 deletions example/lib/controls.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_soloud/flutter_soloud.dart';
import 'package:permission_handler/permission_handler.dart';
Expand Down Expand Up @@ -29,8 +29,8 @@ class _ControlsState extends State<Controls> {
// ignore: avoid_positional_boolean_parameters
ButtonStyle buttonStyle(bool enabled) {
return enabled
? ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.green))
: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.red));
? ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.green))
: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.red));
}

@override
Expand All @@ -49,7 +49,8 @@ class _ControlsState extends State<Controls> {
ElevatedButton(
onPressed: () async {
/// Ask recording permission on mobile
if (Platform.isAndroid || Platform.isIOS) {
if (defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS) {
final p = await Permission.microphone.isGranted;
if (!p) {
unawaited(Permission.microphone.request());
Expand All @@ -76,9 +77,8 @@ class _ControlsState extends State<Controls> {
blurSigmaX: 6,
blurSigmaY: 6,
),
linearShapeParams: LinearShapeParams(
linearShapeParams: const LinearShapeParams(
angle: -90,
space: Platform.isAndroid || Platform.isIOS ? -10 : 10,
alignment: LinearAlignment.left,
),
),
Expand Down
1 change: 0 additions & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ class MyHomePage extends StatelessWidget {
Widget build(BuildContext context) {
return DefaultTabController(
length: 5,
initialIndex: 2,
child: SafeArea(
child: Scaffold(
body: Column(
Expand Down
5 changes: 3 additions & 2 deletions example/lib/page_3d_audio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,12 @@ class _Page3DAudioState extends State<Page3DAudio> {

/// load the audio file
currentSound = await SoLoud.instance.loadUrl(
'https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3',
// From https://freetestdata.com/audio-files/mp3/
'https://marcobavagnoli.com/Free_Test_Data_500KB_MP3.mp3',
);

/// play it
await SoLoud.instance.play3d(currentSound!, 0, 0, 0);
await SoLoud.instance.play3d(currentSound!, 0, 0, 0, looping: true);

spinAround = true;

Expand Down
Loading