Skip to content

Commit df33668

Browse files
authored
Feature: Update FMOD to 2.03.09 (#1)
* update script for linux & docker-compose deprecation * updated to 20309 web(html5), android, linux, win32 * 2.03.09 update, workaround html5 build * fakeCwrap test workaround * clean after success test html5 * 10 - Android FMOD 2.02 is out of date (#11) Android has been updated to 2.03.09 * 13 - Windows 2.02.05 Is Out Of Date (#15) Test evidence shared * 7 - Update macOS and iOS To 2.03.09 (#14) * 7 - Update macOS and iOS To 2.03.09 * 7 - commit libfmodbridge * 7 - new lines * 12 - Readme 2.02.05 Is Out Of Date (#16) * 12 - Readme 2.02.05 Is Out Of Date * 12 - switch to png banner * 12 - rm unused logo * 6 - Extra Emscripten flags * 17 - Fmod 2.03.09 pthread Support (#18) * 17 - Wasm Pthread Support * 17 - testing clarification * 17 - attempted removing pthread pool size * 17 - better manifest * 21 - Enhanced Properties & Investigate `lib_path` (#22) * 21 - Enhanced Properties * 21 - fix ext.properties * 19 - Add Manuals and API Reference (#20) * 19 - Add Manuals and API Reference * 19 - added further instructions, generator for .script_api file
1 parent 0ba3b3c commit df33668

File tree

74 files changed

+12179
-185
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+12179
-185
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Trigger site rebuild
2+
3+
on: [push]
4+
5+
jobs:
6+
site-rebuild:
7+
runs-on: ubuntu-latest
8+
9+
steps: [
10+
{
11+
name: 'Repository dispatch',
12+
uses: defold/[email protected],
13+
with: {
14+
repo: 'defold/defold.github.io',
15+
token: '${{ secrets.SERVICES_GITHUB_TOKEN }}',
16+
17+
action: 'extension-fmod'
18+
}
19+
}]

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ Release
2424
/stub/res/x86-win32/fmod*.dll
2525
/stub/lib/ios/libfmod*.a
2626
/stub/lib/js-web/libfmod*.a
27+
/.editor_settings

CONTRIBUTE.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Alternatively, if you don't want to install Python on your system, you can run
2323
it through Docker:
2424

2525
```
26-
docker-compose -f bridge/docker-compose.generate_bindings.yml up
26+
docker compose -f bridge/docker-compose.generate_bindings.yml up
2727
```
2828

2929
The bridge library comes with compilation scripts for each platform:
@@ -57,7 +57,7 @@ You can build all of these in one go with Docker:
5757

5858
```bash
5959
cd bridge
60-
docker-compose up
60+
docker compose up
6161
```
6262

6363
Or use the respective Makefiles to build manually. Make sure to match
@@ -82,4 +82,38 @@ common prefix of the files you downloaded as the first argument:
8282
```bash
8383
# For example, if you downloaded FMOD 2.01.07:
8484
./update_fmod.sh ~/Downloads/fmodstudioapi20107
85-
```
85+
```
86+
87+
## Testing HTML5/WASM pthread builds locally
88+
89+
WASM pthread builds require specific CORS headers to enable `SharedArrayBuffer`.
90+
Regular `python3 -m http.server` will **not** work so please send required headers.
91+
92+
**Required headers:**
93+
- `Cross-Origin-Opener-Policy: same-origin`
94+
- `Cross-Origin-Embedder-Policy: require-corp`
95+
96+
So for python serving, you can add the request CORS handler like
97+
```python3
98+
class CORSRequestHandler(SimpleHTTPRequestHandler):
99+
(...)
100+
def end_headers(self):
101+
self.send_header('Cross-Origin-Opener-Policy', 'same-origin')
102+
self.send_header('Cross-Origin-Embedder-Policy', 'require-corp')
103+
```
104+
105+
106+
**Build with both architectures** (loader needs both for fallback):
107+
108+
```bash
109+
java -jar bob.jar build bundle --platform js-web \
110+
--architectures wasm-web,wasm_pthread-web \
111+
--bundle-format html5
112+
(...)
113+
```
114+
115+
Validate in the console
116+
117+
```javascript
118+
console.log("Pthread:", Module.isWASMPthreadSupported); // Should be: true
119+
```

README.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
![fmod](./docs/fmod_logo.png)
1+
![](https://img.shields.io/badge/Defold-1.11.2+-green)
2+
![](https://img.shields.io/badge/FMOD-2.03.09-green)
3+
![](https://img.shields.io/badge/Emscripten-4.0.6+3.1.67-green)
24

3-
# FMOD extension for Defold
5+
![](./docs/defmod_banner.png)
46

7+
# Defold FMOD Extension
58
> [FMOD] sound engine bindings for Defold
69
710
**Commercial usage of FMOD products may require a separate license directly with
@@ -15,9 +18,22 @@ least the words "FMOD" (OR "FMOD STUDIO" IF APPLICABLE) AND "FIRELIGHT TECHNOLOG
1518

1619
## Installation
1720

18-
Go to the [Releases page](https://github.com/dapetcu21/defold-fmod/releases),
21+
Go to the [Releases page](https://github.com/defold/extension-fmod/releases),
1922
copy a dependency URL, then add it to your dependencies in `game.project`.
2023

24+
### Running in the editor
25+
26+
The game will bundle fine, but in order for FMOD to be available when running
27+
from the editor, an extra step is required.
28+
29+
Copy the `fmod/res` directory from this repo to a directory in your project
30+
and add the path to that directory to your `game.project`:
31+
32+
```
33+
[fmod]
34+
lib_path = path/to/fmod/res
35+
```
36+
2137
### Set the speaker mode
2238

2339
This step is [only required if you use Studio banks][set_software_format].
@@ -53,19 +69,6 @@ depends from game to game, but for this example to run, it needs a 512MB heap.
5369
heap_size = 512
5470
```
5571

56-
### Running in the editor
57-
58-
The game will bundle fine, but in order for FMOD to be available when running
59-
from the editor, an extra step is required.
60-
61-
Copy the `fmod/res` directory from this repo to a directory in your project
62-
and add the path to that directory to your `game.project`:
63-
64-
```
65-
[fmod]
66-
lib_path = path/to/fmod/res
67-
```
68-
6972
## Usage
7073

7174
Structs and classes are exposed on the `fmod` and `fmod.studio` namespaces. All

bridge/Makefile.android

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ HEADERS = \
1313
JAVA_FILES = \
1414
src/me/petcu/fmodbridge/BridgeHelper.java
1515

16-
ANDROID_NDK ?= $(HOME)/Tools/android-ndk-r17c
17-
HOST_TAG ?= `uname | tr A-Z a-z`-x86_64
18-
19-
CC_ARM := $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(HOST_TAG)/bin/clang -target armv7a-linux-androideabi14 --sysroot=$(ANDROID_NDK)/sysroot -I$(ANDROID_NDK)/sysroot/usr/include/arm-linux-androideabi
20-
CC_ARM64 := $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(HOST_TAG)/bin/clang -target aarch64-linux-androideabi21 --sysroot=$(ANDROID_NDK)/sysroot -I$(ANDROID_NDK)/sysroot/usr/include/aarch64-linux-android
21-
AR := $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(HOST_TAG)/bin/llvm-ar
16+
ANDROID_NDK ?= /opt/android-ndk-r25b
17+
HOST_TAG ?= linux-x86_64
18+
19+
# prebuilt compiler from the NDK toolchain
20+
TOOLCHAIN_PREFIX := $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(HOST_TAG)
21+
CC_ARM := $(TOOLCHAIN_PREFIX)/bin/armv7a-linux-androideabi19-clang
22+
CC_ARM64 := $(TOOLCHAIN_PREFIX)/bin/aarch64-linux-android21-clang
23+
AR := $(TOOLCHAIN_PREFIX)/bin/llvm-ar
2224

2325
CFLAGS := -std=c11 -O3 -fvisibility=hidden -I./include -fpic -fomit-frame-pointer \
2426
-fno-strict-aliasing -funwind-tables -ffunction-sections -fstack-protector \

bridge/Makefile.emscripten

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
LIBPATH := ../fmod/lib/web/libfmodbridge.a
2+
LIBPATH_PTHREAD := ../fmod/lib/wasm_pthread-web/libfmodbridge.a
23

34
SOURCES = \
45
src/fmod_init.c \
@@ -9,19 +10,30 @@ HEADERS = src/fmod_bridge.h
910
CC := emcc
1011
AR := emar
1112

12-
CFLAGS := -O3 -fvisibility=hidden -I./include
13+
CFLAGS := -O3 -fvisibility=hidden -I./include -s ALLOW_MEMORY_GROWTH=1 -s LEGACY_RUNTIME=1
14+
CFLAGS_PTHREAD := -O3 -visibility=hidden -I./include -s ALLOW_MEMORY_GROWTH=1 -pthread
1315

14-
all: $(LIBPATH)
16+
all: $(LIBPATH) $(LIBPATH_PTHREAD)
1517

16-
OBJECTS = $(patsubst src/%.c,build/js-%.bc,$(SOURCES))
18+
# Regular build
19+
OBJECTS = $(patsubst src/%.c,build/js-%.o,$(SOURCES))
1720

18-
build/js-%.bc: src/%.c $(HEADERS)
21+
build/js-%.o: src/%.c $(HEADERS)
1922
$(CC) $(CFLAGS) -c $< -o $@
2023

2124
$(LIBPATH): $(OBJECTS)
2225
$(AR) rcs $@ $^
2326

27+
# Pthread build
28+
OBJECTS_PTHREAD = $(patsubst src/%.c,build/js-pthread-%.o,$(SOURCES))
29+
30+
build/js-pthread-%.o: src/%.c $(HEADERS)
31+
$(CC) $(CFLAGS_PTHREAD) -c $< -o $@
32+
33+
$(LIBPATH_PTHREAD): $(OBJECTS_PTHREAD)
34+
$(AR) rcs $@ $^
35+
2436
clean:
25-
rm -f $(LIBPATH) build/js-*.bc
37+
rm -f $(LIBPATH) $(LIBPATH_PTHREAD) build/js-*.o build/js-pthread-*.o
2638

2739
.PHONY: all clean

bridge/api_from_bindings.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import re
2+
from typing import List, Tuple, Any, Protocol
3+
from jinja2 import Environment, FileSystemLoader
4+
5+
6+
class ParsedStruct(Protocol):
7+
name: str
8+
is_class: bool
9+
methods: List[Tuple[str, Any]]
10+
properties: List[Any]
11+
12+
13+
class ParsedTypeDecl(Protocol):
14+
c_type: str
15+
type: int
16+
17+
18+
class ParsedMethod(Protocol):
19+
name: str
20+
args: List[Any]
21+
generated: bool
22+
23+
24+
TYPE_BASIC = 1
25+
TYPE_STRUCT = 2
26+
TYPE_CLASS = 3
27+
TYPE_POINTER = 4
28+
29+
INPUT_USAGES = ("input", "input_ptr", "input_deref")
30+
OUTPUT_USAGES = ("output", "output_ptr")
31+
32+
BASIC_NUMERIC_TYPES = {"float", "double"}
33+
INTEGER_TYPES = {"int", "short", "long", "char", "FMOD_BOOL"}
34+
35+
TYPE_HANDLERS = {
36+
TYPE_BASIC: lambda c_type: _convert_basic_type(c_type),
37+
TYPE_POINTER: lambda c_type: _convert_pointer_type(c_type),
38+
TYPE_STRUCT: lambda c_type: _convert_struct_type(c_type),
39+
TYPE_CLASS: lambda c_type: _convert_struct_type(c_type),
40+
}
41+
42+
PARAM_DESCRIPTIONS = {
43+
"system": "FMOD system handle",
44+
"sound": "Sound handle",
45+
"channel": "Channel handle",
46+
"name": "Name or path",
47+
"filename": "Name or path",
48+
"length": "Length or size value",
49+
"size": "Length or size value",
50+
"mode": "Mode flags",
51+
"volume": "Volume level (0.0 to 1.0)",
52+
"position": "Position value",
53+
"paused": "Paused state",
54+
"index": "Index value",
55+
}
56+
57+
58+
def _convert_basic_type(c_type: str) -> str:
59+
if "FMOD_VECTOR" in c_type:
60+
return "vector3"
61+
if c_type in BASIC_NUMERIC_TYPES:
62+
return "number"
63+
if any(base_type in c_type for base_type in INTEGER_TYPES):
64+
return "boolean" if "FMOD_BOOL" in c_type else "number"
65+
if c_type.startswith("FMOD_"):
66+
return "number"
67+
return "number"
68+
69+
70+
def _convert_pointer_type(c_type: str) -> str:
71+
if "FMOD_VECTOR" in c_type:
72+
return "vector3"
73+
if "char" in c_type:
74+
return "string"
75+
return "userdata"
76+
77+
78+
def _convert_struct_type(c_type: str) -> str:
79+
if "FMOD_VECTOR" in c_type:
80+
return "vector3"
81+
type_lower = c_type.lower()
82+
if type_lower.startswith("fmod_studio_"):
83+
return type_lower.replace("fmod_studio_", "fmod.studio.")
84+
if type_lower.startswith("fmod_"):
85+
return type_lower.replace("fmod_", "fmod.")
86+
return "userdata"
87+
88+
89+
def convert_to_snake_case(text: str) -> str:
90+
valid_pattern = re.compile(r"^_*(IDs|[A-Z][a-z]+|[A-Z0-9]+(?![a-z]))")
91+
components = []
92+
remaining = text
93+
while True:
94+
match = valid_pattern.match(remaining)
95+
if match is None:
96+
break
97+
components.append(match.group(1).lower())
98+
remaining = remaining[match.end():]
99+
return "_".join(components)
100+
101+
102+
def convert_c_type_to_lua_type(c_type: str, type_enum: int) -> str:
103+
handler = TYPE_HANDLERS.get(type_enum)
104+
if handler:
105+
return handler(c_type)
106+
return "any"
107+
108+
109+
def generate_parameter_description(param_name: str, function_name: str) -> str:
110+
param_lower = param_name.lower()
111+
112+
if param_lower in PARAM_DESCRIPTIONS:
113+
return PARAM_DESCRIPTIONS[param_lower]
114+
115+
for key, description in PARAM_DESCRIPTIONS.items():
116+
if key in param_lower:
117+
return description
118+
119+
return param_name
120+
121+
122+
def get_input_args(method: ParsedMethod, skip_self: bool = False) -> List[Any]:
123+
args = method.args[1:] if skip_self else method.args
124+
return [arg for arg in args if arg.usage in INPUT_USAGES]
125+
126+
127+
def get_output_args(method: ParsedMethod) -> List[Any]:
128+
return [arg for arg in method.args if arg.usage in OUTPUT_USAGES]
129+
130+
131+
def get_arg_type_info(arg: Any) -> Tuple[str, int]:
132+
if arg.usage == "output_ptr":
133+
child = getattr(arg.type, "child", None)
134+
if child is not None:
135+
return child.c_type, child.type
136+
return arg.type.c_type, arg.type.type
137+
138+
139+
def write_script_api_file(
140+
output_path: str,
141+
enums: List[str],
142+
structs: List[ParsedStruct],
143+
global_functions: List[Tuple[int, str, ParsedMethod]]
144+
) -> None:
145+
env = Environment(
146+
loader=FileSystemLoader('.'),
147+
autoescape=False,
148+
trim_blocks=True,
149+
lstrip_blocks=True,
150+
)
151+
152+
env.globals['c_type_to_lua_type'] = convert_c_type_to_lua_type
153+
env.globals['get_param_description'] = generate_parameter_description
154+
env.globals['get_input_args'] = get_input_args
155+
env.globals['get_output_args'] = get_output_args
156+
env.globals['get_arg_type_info'] = get_arg_type_info
157+
158+
template = env.get_template('fmod_script_api_template.yaml')
159+
160+
rendered_output = template.render(
161+
enums=enums,
162+
structs=structs,
163+
global_functions=global_functions,
164+
)
165+
166+
with open(output_path, 'w') as output_file:
167+
output_file.write(rendered_output)
168+
169+
print(f"Generated {output_path}")

bridge/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ services:
1010
build_android:
1111
build: docker/build_android
1212
volumes:
13-
- "..:/repo"
13+
- "..:/repo"

0 commit comments

Comments
 (0)