Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #81 from Astrabit-ST/fmod-bindings
Browse files Browse the repository at this point in the history
[feat] FMOD Studio bindings
  • Loading branch information
Lily Lyons authored Jul 19, 2022
2 parents bb6bcbc + a95c535 commit b728fd1
Show file tree
Hide file tree
Showing 59 changed files with 6,257 additions and 509 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,5 @@ assets/icon.ico

.vscode
out/
/fmod/
.gdb_history
37 changes: 0 additions & 37 deletions Dockerfile-linux

This file was deleted.

29 changes: 0 additions & 29 deletions Dockerfile-windows

This file was deleted.

62 changes: 57 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,59 @@ The main upshot of this, of course, is remaining on par with ruby in terms of ge
Previously, C extensions were very jank with ModShot, **however** now you can use a C extension right from your own Ruby install!
(Provided the version is the same, and the msys2 evironment is the same. I'll get back to this later.)

### Build options
## Using clang

Unfortunately because of the way ModShot is set up, you will need to pass build options to Make **and** Meson, if it pertains to dependencies. (ruby ver, opt level, etc.)
//TODO

# FMOD

FMOD support is very... interesting. It involves a lot of licensing, legalese, etc.
If you want to enable FMOD support, download FMOD and extract the zip into a folder named `fmod`.
You will then need to pass `-Dfmod=true` as an option.

ModShot should handle everything from there, it's up to you to follow the FMOD license.

FMOD bindings also do not supply the `Audio` module. You may create wrapper functions for that if you wish.
AL Effect bndings are not supplied as well for obvious reasons.

Functions will always return an array of values (usually two) in the order of `[result, values...]`.
This means you can do `result, value = FMOD.some_func()`, which is pretty neat, right? If a result is not `FMOD_OK` (0) there will be no return value. Keep that in mind!

The FMOD bindings won't hold your hands either- You will need to clean up after yourself.
Because of the way the bindings work as well calling the same function twice will **NOT** return the "same" object. Fundamentally, it is the same object, as the C++ side object is the same, but it is a brand new object as far as ruby is concerned.

Because of this behavior, you can quite easily cause a memory leak by repeatedly storing an object returned in an array somewhere constantly loaded such that ruby will not garbage collect it.
i.e
```rb
array = []
100000.times do
array << bank.get_event_list # Bad will memory leak!
end
```
So, be mindful of what you write! Luckily instances of objects from these bindings are very small so it's not a big deal if your code isn't perfect, but **PLEASE** do be mindful of this! RMXP already has a similar problem with Bitmaps, so if you've dealt with them you should know what to do.
There is an `==` operator provided that will check if an object is the same for you as well. You can usually assign a value `nil` to get it garbage collected.

A feature is provided to automatically release **some** FMOD objects (Like System, Bank, etc) but it is disabled by default.
This is because some FMOD functions will allow you to "get back" an object after it has been garbage collected from ruby, and this feature messes with that.
I would not suggest using it because if you are, you're likely doing something wrong and are relying on a bandaid fix to avoid it being an issue.

The bindings should generally line up with what's documented in the latest FMOD docs- although things like `FMOD_Studio_System_Create` are hidden away under `FMOD::Studio::System.new` instead. The bindings are closest to the `C#` bindings for FMOD.

Plugins are potentially supported via C extensions but if you really want to add support for one you may hardcode it in.
Something like ResonanceAudio would be simple enough, since all you would need to do is wrap a struct like mkxp wraps classes, and the FMOD bindings here should do the rest. It'll pull a void pointer to your wrapped data by using `getPrivateData<void*>`. Getting data back like this will either convert it to a string, or the user will pass in a class that the bindings will set the data of. I still haven't decided on how to do this just yet, though, but I am leaning towards the string method.

One other thing to note is that with structs you need to be mindful of method chaining.
You can chain methods on the struct (like `struct.position.y = 15`) but if chained on the return vaue from a method it won't work.
```rb
# will not work!
eventinstance.get_3d_attributes[1].up.x = 15
# will work!
struct = eventinstance.get_3d_attributes[1]
struct.up.x = 15
eventinstance.set_3d_attributes(struct)
```

I hope that's enough info to get you started! There will be some direct conversions of FMOD examples in the `scripts` folder when I get to it. (TODO)

### Options

Expand All @@ -43,11 +93,13 @@ RUBY_VER && -Dmri_version (default 3.1) sets the ruby version.
-Dsteam (default false) sets the build to use steam.
--build-type (default Release) sets the build type.
-Dbuild_static (default true) sets the build to be static. (True is faster, but with longer startup times.)
-Dfmod (default false) toggles FMOD instead of OpenAL.
-Dauto_clean_fmod (default false) toggles auto releasing SOME FMOD objects when garbage collected.
```

## Building on Windows

First, you'll need to download [msys2](https://www.msys2.org/) and install it.
First, you'll need to download [msys2](https://www.msys2.org/) and install it.
Then, you'll want to determine what Ruby version you're using, as this will determine what build environment you'll be using.
As is, ModShot is set up to use Ruby 3.1, so keep that in mind. Please refer to this table to determine the environment.
(You *can* use the wrong environment and it will work fine, just not with C extensions.)
Expand Down Expand Up @@ -96,7 +148,7 @@ This should create a folder called `out` with your build of ModShot all ready to

## Configuration

*ModShot* reads configuration data from the file "oneshot.conf". The format is ini-style. Do *not* use quotes around file paths (spaces won't break). Lines starting with '#' are comments. See 'oneshot.conf.sample' for a list of accepted entries. Note that this feature appears to be brpken for the moment until we get around to fixing it. Using command line options does work, however.
*ModShot* reads configuration data from the file "oneshot.conf". The format is ini-style. Do *not* use quotes around file paths (spaces won't break). Lines starting with '#' are comments. See 'oneshot.conf.sample' for a list of accepted entries.

All option entries can alternatively be specified as command line options. Any options that are not arrays (eg. preloaded scripts) specified as command line options will override entries in oneshot.conf. Note that you will have to wrap values containing spaces in quotes (unlike in oneshot.conf).

Expand All @@ -108,4 +160,4 @@ Example: `./oneshot --gameFolder="oneshot" --vsync=true`

Modshot builds come pre-packaged with the ruby standard library in `/lib/ruby/`. You can require gems from this folder at any point by using `require '<gem>'`.

You can ship your own gems by finding the gem install location (Typically `C:\Ruby27-x64\lib\ruby\gems\2.7.0\gems`), going inside the gem, and copying over all the files inside lib.
You can ship your own gems by finding the gem install location (Typically `C:\Ruby27-x64\lib\ruby\gems\2.7.0\gems`), going inside the gem, and copying over all the files inside lib.
File renamed without changes.
35 changes: 23 additions & 12 deletions binding-mri/binding-mri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
#include "sdl-util.h"
#include "debugwriter.h"
#include "graphics.h"
#ifndef USE_FMOD
#include "audio.h"
#endif
#include "boost-hash.h"
#include "version.h"
#include "oneshot.h"
Expand All @@ -48,6 +50,9 @@
#include <SDL2/SDL_filesystem.h>

extern const char module_rpg1[];
#ifdef USE_FMOD
extern const char fmod_enums[];
#endif

static void mriBindingExecute();
static void mriBindingTerminate();
Expand Down Expand Up @@ -75,7 +80,6 @@ void windowVXBindingInit();
void tilemapVXBindingInit();

void inputBindingInit();
void audioBindingInit();
void graphicsBindingInit();

void fileIntBindingInit();
Expand All @@ -89,7 +93,13 @@ void nikoBindingInit();
void oneshotBindingInit();
void modshotBindingInit();
void steamBindingInit();
#ifndef USE_FMOD
void audioBindingInit();
void aleffectBindingInit();
#else
void fmodCoreBindingInit();
void fmodStudioBindingInit();
#endif
void screenBindingInit();
RB_METHOD(mriPrint);
RB_METHOD(mriP);
Expand Down Expand Up @@ -117,7 +127,6 @@ static void mriBindingInit()
tilemapBindingInit();

inputBindingInit();
audioBindingInit();
graphicsBindingInit();

fileIntBindingInit();
Expand All @@ -127,7 +136,13 @@ static void mriBindingInit()
oneshotBindingInit();
modshotBindingInit();
steamBindingInit();
#ifndef USE_FMOD
audioBindingInit();
aleffectBindingInit();
#else
fmodCoreBindingInit();
fmodStudioBindingInit();
#endif
screenBindingInit();
rb_define_global_const("MODSHOT_VERSION", rb_str_new_cstr(MODSHOT_VERSION));
if (rgssVer >= 3)
Expand All @@ -151,6 +166,10 @@ static void mriBindingInit()

rb_eval_string(module_rpg1);

#ifdef USE_FMOD
rb_eval_string(fmod_enums);
#endif

VALUE mod = rb_define_module("MKXP");
_rb_define_module_function(mod, "data_directory", mkxpDataDirectory);
_rb_define_module_function(mod, "puts", mkxpPuts);
Expand Down Expand Up @@ -296,7 +315,9 @@ static VALUE rgssMainRescue(VALUE arg, VALUE exc)
static void processReset()
{
shState->graphics().reset();
#ifndef USE_FMOD
shState->audio().reset();
#endif

shState->rtData().rqReset.clear();
shState->graphics().repaintWait(shState->rtData().rqResetFinish,
Expand Down Expand Up @@ -665,16 +686,6 @@ static void mriBindingExecute()
rubyArgsC.push_back(greedyVersioning.c_str());
}

if (conf.jitEnabled) {
std::string verboseLevel("-jit-verbose="); verboseLevel += std::to_string(conf.jitVerbosity);
std::string maxCache("--jit-max-cache="); maxCache += std::to_string(conf.jitMaxCache);
std::string minCalls("--jit-min-calls="); minCalls += std::to_string(conf.jitMinCalls);
rubyArgsC.push_back("--jit");
rubyArgsC.push_back(verboseLevel.c_str());
rubyArgsC.push_back(maxCache.c_str());
rubyArgsC.push_back(minCalls.c_str());
}

node = ruby_options(rubyArgsC.size(), const_cast<char**>(rubyArgsC.data()));

int state;
Expand Down
Loading

0 comments on commit b728fd1

Please sign in to comment.