Cabbage Logo
Back to Cabbage Site

Cabbage FMOD Android .so support

I think, I have to stop here because I have no ideas left and have to concentrate on other things.
I hope, it’ll be possible in the future in some way or another. I’m just going with the no GC version at the moment.

Thank you so much for helping me along all this! Even though the solution is not very elegant right now, I could learn a lot along the way.

I have a small unrelated question though. Now if I want to have multiple plugins, I have to change the dsp description (second in FMOD_Csound_Desc) and this name has to be the same as the caption in the .csd? My first try wasn’t successful but maybe I did something somewhere wrong.
So I could use the cabbage export for the windows plugin and modify the android_fmod_csound script for the android part.

Well, on desktop platform you only need to change the plugin name and provide a corresponding csd file of the same name. Will this not work in this instance?

Ah! Then I haven’t found the part, where the dsp name is set to the filename or caption.
But good to know, that it’s setup like that. Thank you!

It’s here:

How I understand this part is, that you’re getting the filepath and creating one for the csd file. This is then used in “csdcompile” and also in csoundchannelvectors where you read out the sliders and other widgets. But also the caption. I just wondered if you change the plugin name (set to “FMOD Csound”) which is also shown in FMOD studio with said caption or how it is handled.

I only rename the csound_fmod.so/.dylib/.dll library. When FMOD scans for new plugins it uses the name of the plugin library as the name that appear in FMOD Studio. :+1:

This is interesting. Sorry for maybe being slow again, but changing the name of the .dll doesn’t change anything inside Fl Studio. It always uses the name “FMOD Csound” from the plugin description. So I thought you modify this string withing cabbage somehow.
Maybe I’m missing something here though…

Hmm, maybe I do, I’ll have to check the source code. Leave it with me, I’m not at my PC right now…

I just checked now and I don’t see anything other than a simple renaming of the csound_fmod library when exporting from Cabbage. :thinking:

When I tried renaming it, I forgot to change the caption. It’s taking the caption as plugin name. The name of the library and csd don’t matter as long as they are the same of course.
All clear now apart from the GC stuff.
But thank you for everything!

1 Like

I’m late to the party (was on holiday) but it looks like you’ve found the issue :wink:
What’s the state of this now?
Great to have such a tool!
Keep up the hard work! :wink:

Hi Giovanni! :blush:
Yes, the issue is found but I just had to move on with the rest of the project.
The temporary solution is now to just disable the Garbage Collection which works for my small project.
As I’m a complete c++ noob I also made the workaround to copy the cs file into the persistent data of the app via csharp in a separate unity script and then passing the pathname to the plugin.
This should of course all happen inside the library itself I believe (or some other android filemanagement I don’t know about. Maybe direct webrequest or how this works, why is this so complicated ^^)

This aren’t really solutions. I just had to give up searching for now how to pin the csoundandroid library that it doesn’t get removed by GC (what I’m assuming is happening). (I’m now just having the spontaneous idea to load also csoundandroid via script, maybe that works). I’d have to try that out but It’ll be in a few weeks…
Maybe you know more? I’d be happy to provide those code(monstrosities) I’m working with now, if it helps.

Why do you need to copy the cs (csharp or csound?) file to the persistent folder?
In the case of a csd file, it could be enough to save its content as a string like we did in CsoundUnity.

Yes on Android you have to use a WebRequest to get a file on the disk, since the apk is like an archive.

I know nothing of FMOD but if you want I’m happy to have a look at the code, maybe I can suggest something (though I’m not a C++ master) :wink:

Yes, that would totally work too with Csdcompiletext and passing the whole file as text. I just didn’t yet know the issue of the crash and wanted to use csoundcompile like it’s used in the desktop version. For the final thing it would be surely a lot smarter to just go with the text variant.

I’ll post the code this evening when I’m back home. Thank you!

/*
Copyright (C) 2016 Rory Walsh

CsoundFMOD is free software; you can redistribute it
and/or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Csound; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA
*/
#define _CRT_SECURE_NO_WARNINGS

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <cstring>
#include <vector>
#include "dirent.h"
#include "fmod.hpp"
#include "float-version.h"



#ifdef WIN32
#include "windows.h"
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#else
#include <dlfcn.h>
// #include <CoreFoundation/CoreFoundation.h>
#endif
#include <fstream>
#include <sstream>


#define MAXPLUGINS 512

extern "C" {
	F_EXPORT FMOD_DSP_DESCRIPTION* F_CALL FMODGetDSPDescription();
}

const float FMOD_CSOUND_PARAM_GAIN_MIN = -80.0f;
const float FMOD_CSOUND_PARAM_GAIN_MAX = 10.0f;
const float FMOD_CSOUND_PARAM_GAIN_DEFAULT = 0.0f;

#include "csound.h"
#include "AndroidCsound.hpp"


//===========================================================
// simple class for holding information about Csound instruments and channels
//===========================================================
#define MIN 0
#define MAX 1
#define VALUE 2


struct CsoundChannel
{
	float range[3];
	std::string name, text, label, caption, type;
};

std::vector<CsoundChannel> csoundChannels;

//============================================================
enum FMOD_CSOUND_FORMAT
{
	FMOD_CSOUND_FORMAT_MONO = 0,
	FMOD_CSOUND_FORMAT_STEREO,
	FMOD_CSOUND_FORMAT_5POINT1
};

#define DECIBELS_TO_LINEAR(__dbval__)  ((__dbval__ <= FMOD_CSOUND_PARAM_GAIN_MIN) ? 0.0f : powf(10.0f, __dbval__ / 20.0f))
#define LINEAR_TO_DECIBELS(__linval__) ((__linval__ <= 0.0f) ? FMOD_CSOUND_PARAM_GAIN_MIN : 20.0f * log10f((float)__linval__))

FMOD_RESULT F_CALLBACK FMOD_Csound_dspcreate(FMOD_DSP_STATE* dsp);
FMOD_RESULT F_CALLBACK FMOD_Csound_dsprelease(FMOD_DSP_STATE* dsp);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspreset(FMOD_DSP_STATE* dsp);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspprocess(FMOD_DSP_STATE* dsp, unsigned int length, const FMOD_DSP_BUFFER_ARRAY* inbufferarray, FMOD_DSP_BUFFER_ARRAY* outbufferarray, FMOD_BOOL inputsidle, FMOD_DSP_PROCESS_OPERATION op);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparamfloat(FMOD_DSP_STATE* dsp, int index, float value);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparamint(FMOD_DSP_STATE* dsp, int index, int value);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparambool(FMOD_DSP_STATE* dsp, int index, bool value);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparamdata(FMOD_DSP_STATE* dsp, int index, void* data, unsigned int length);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspgetparamfloat(FMOD_DSP_STATE* dsp, int index, float* value, char* valuestr);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspgetparamint(FMOD_DSP_STATE* dsp, int index, int* value, char* valuestr);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspgetparambool(FMOD_DSP_STATE* dsp, int index, bool* value, char* valuestr);
FMOD_RESULT F_CALLBACK FMOD_Csound_dspgetparamdata(FMOD_DSP_STATE* dsp, int index, void** value, unsigned int* length, char* valuestr);

//each plugin can use 1000 parameters
#define MAX_PARAMETERS 1000

static FMOD_DSP_PARAMETER_DESC csoundParameters[MAX_PARAMETERS];
std::string csdFilename;
FMOD_DSP_PARAMETER_DESC* FMOD_Csound_dspparam[MAX_PARAMETERS] = {};
bool debugMode = true;

//===========================================================
// generic descriptor for plguins. Members are updated once 
// the .csd file has been read. 
//===========================================================
FMOD_DSP_DESCRIPTION FMOD_Csound_Desc =
{
	FMOD_PLUGIN_SDK_VERSION,        /* [w] The plugin SDK version this plugin is built for.  set to this to FMOD_PLUGIN_SDK_VERSION defined above. */
	"FMOD Csound",                  /* [w] The identifier of the DSP. This will also be used as the name of DSP and shouldn't change between versions. */
	0x00010000,                     /* [w] Plugin writer's version number. */
	1,                              /* [w] Number of input buffers to process.  Use 0 for DSPs that only generate sound and 1 for effects that process incoming sound. */
	1,                              /* [w] Number of audio output buffers.  Only one output buffer is currently supported. */
	FMOD_Csound_dspcreate,          /* [w] Create callback.  This is called when DSP unit is created.  Can be null. */
	FMOD_Csound_dsprelease,         /* [w] Release callback.  This is called just before the unit is freed so the user can do any cleanup needed for the unit.  Can be null. */
	FMOD_Csound_dspreset,           /* [w] Reset callback.  This is called by the user to reset any history buffers that may need resetting for a filter, when it is to be used or re-used for the first time to its initial clean state.  Use to avoid clicks or artifacts. */
	0,                              /* [w] Read callback.  Processing is done here.  Can be null. */
	FMOD_Csound_dspprocess,         /* [w] Process callback.  Can be specified instead of the read callback if any channel format changes occur between input and output.  This also replaces shouldiprocess and should return an error if the effect is to be bypassed.  Can be null. */
	0,                              /* [w] Set position callback.  This is called if the unit wants to update its position info but not process data, or reset a cursor position internally if it is reading data from a certain source.  Can be null. */

	MAX_PARAMETERS,      /* [w] Number of parameters used in this filter.  The user finds this with DSP::getNumParameters */
	FMOD_Csound_dspparam,           /* [w] Variable number of parameter structures. */
	FMOD_Csound_dspsetparamfloat,   /* [w] This is called when the user calls DSP::setParameterFloat. Can be null. */
	FMOD_Csound_dspsetparamint,     /* [w] This is called when the user calls DSP::setParameterInt.   Can be null. */
	0,    /* [w] This is called when the user calls DSP::setParameterBool.  Can be null. */
	0,                              /* [w] This is called when the user calls DSP::setParameterData.  Can be null. */
	FMOD_Csound_dspgetparamfloat,   /* [w] This is called when the user calls DSP::getParameterFloat. Can be null. */
	FMOD_Csound_dspgetparamint,     /* [w] This is called when the user calls DSP::getParameterInt.   Can be null. */
	0,    /* [w] This is called when the user calls DSP::getParameterBool.  Can be null. */
	0,                              /* [w] This is called when the user calls DSP::getParameterData.  Can be null. */
	0,                              /* [w] This is called before processing.  You can detect if inputs are idle and return FMOD_OK to process, or any other error code to avoid processing the effect.  Use a count down timer to allow effect tails to process before idling! */
	0,                              /* [w] Optional. Specify 0 to ignore. This is user data to be attached to the DSP unit during creation.  Access via DSP::getUserData. */

	0,                              /* [w] Register callback.  This is called when DSP unit is loaded/registered.  Useful for 'global'/per system object init for plugin.  Can be null. */
	0,                              /* [w] Deregister callback.  This is called when DSP unit is unloaded/deregistered.  Useful as 'global'/per system object shutdown for plugin.  Can be null. */
	0,                              /* [w] System mix stage callback.  This is called when the mixer starts to execute or is just finishing executing.  Useful for 'global'/per system object once a mix update calls for a plugin.  Can be null. */
};

#include <android/log.h>


extern "C" {
	extern int androidplayopen_(CSOUND* csound, const csRtAudioParams* parm);
	extern int androidrecopen_(CSOUND* csound, const csRtAudioParams* parm);
	extern void androidrtplay_(CSOUND* csound, const MYFLT* buffer, int nbytes);
	extern int androidrtrecord_(CSOUND* csound, MYFLT* buffer, int nbytes);
	extern void androidrtclose_(CSOUND* csound);

	static void androidMessageCallback(CSOUND*, int attr, const char* format, va_list valist)
	{
		char message[1024];
		::vsnprintf(message, 1024, format, valist);
		__android_log_print(ANDROID_LOG_INFO,"AndroidCsound", "%s", message);
	}
}


void AndroidCsound::setOpenSlCallbacks()
{

	__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "setOpenSlCallbacks");

	if (csoundQueryGlobalVariable(csound, "::async::") == NULL)
		if (this->CreateGlobalVariable("::async::", sizeof(int)) == 0)
		{
			int* p = ((int*)csoundQueryGlobalVariable(csound, "::async::"));
			*p = asyncProcess;
			__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "==set callbacks");
			csoundSetPlayopenCallback(csound, androidplayopen_);
			csoundSetRecopenCallback(csound, androidrecopen_);
			csoundSetRtplayCallback(csound, androidrtplay_);
			csoundSetRtrecordCallback(csound, androidrtrecord_);
			csoundSetRtcloseCallback(csound, androidrtclose_);
			csoundSetMessageCallback(csound, androidMessageCallback);
			__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "==callbacks set");
			//__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%s", csdFilename.c_str());
		}

	if (csoundQueryGlobalVariable(csound, "::paused::") == NULL)
	{
		if (this->CreateGlobalVariable("::paused::", sizeof(int)) == 0)
		{
			int* p = ((int*)csoundQueryGlobalVariable(csound, "::paused::"));
			*p = 0;
		}
	}


};

int AndroidCsound::SetGlobalEnv(const char* name, const char* variable)
{
	return csoundSetGlobalEnv(name, variable);
}

void AndroidCsound::Pause(bool pause)
{
	int* p = ((int*)csoundQueryGlobalVariable(csound, "::paused::"));
	*p = pause ? 1 : 0;
}

unsigned long AndroidCsound::getStreamTime()
{
	return *((__uint64_t*)csoundQueryGlobalVariable(csound, "::streamtime::"));
}


extern "C"
{
	void PassText(const char *str)
	{
		//std::string csdFilename = txt;
		csdFilename = str;

		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_ReceivedText", "%s", csdFilename.c_str());
		
	}
}

//===========================================================
// utility function to get name of library that was loaded
//===========================================================
#ifdef WIN32
std::string GetCsdFilename()
{
	char   DllPath[MAX_PATH] = { 0 };
#ifdef WIN32
	GetModuleFileName((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
	string fileName = DllPath;
	size_t lastindex = fileName.find_last_of(".");
	string fullFilename = fileName.substr(0, lastindex);
	fullFilename.append(".csd");
	return fullFilename;
#endif

}
#else
/*
std::string GetCsdFilename(void)
{
	Dl_info info;
	if (dladdr((void*)"GetCsdFilename", &info))
	{
		std::string fileName = info.dli_fname;
		size_t lastindex = fileName.find_last_of(".");
		size_t lastSlashInd = fileName.find_last_of("/");
		std::string stringtoFind = "/lib/arm64/";
		size_t libIndex = fileName.find(stringtoFind);
		std::string fullFilename = fileName.substr(0, lastindex);
		fullFilename.replace(libIndex, stringtoFind.length() , "/assets/");
		fullFilename = fullFilename.substr(0, fullFilename.size());
		fullFilename.append(".csd");
		
		return fullFilename;
	}
}
*/
#endif
/*
//===========================================================
// to remove leading and trailing spaces from strings....
//===========================================================
std::string Trim(std::string s)
{
	//trim spaces at start
	s.erase(0, s.find_first_not_of(" \t\n"));
	//trim spaces at end
	s.erase(s.find_last_not_of(" \t\n") + 1);
	return s;
}
*/
//===========================================================
// get csd files. This file must reside in the same directory
// as the plugin library - this is not implemented for now due
// to way FMOD organses plugins..
//===========================================================
/*
std::vector<std::string> GetCsdFiles()
{
	//    std::vector<std::string> csdnames;
	//    DIR             *dip = NULL;
	//    struct dirent   *dit;
	//    std::string          temp, name, path;
	//    int             i = 0;
	//    size_t    indx = 0;
	//    char csd_path[1024];
	//    sprintf(csd_path, "%s", GetCsdFilename());
	//    char *src = NULL;
	//
	//    if (strlen(csd_path) == 0) dip = opendir(".");
	//    else {
	//        path = csd_path;
	//#ifdef WIN32
	//        indx = path.find(";");
	//#else
	//        indx = path.find(":");
	//#endif
	//        if (indx != std::string::npos) {
	//            dip = opendir(path.substr(0, indx).c_str());
	//            strncpy(csd_path, path.substr(0, indx).c_str(), 1023);
	//            csd_path[1023] = '\0';
	//        }
	//        else dip = opendir(csd_path);
	//    }
	//    if (dip == NULL) {
	//        free(src);
	//        return csdnames;
	//    }
	//    while ((dit = readdir(dip)) != NULL)
	//    {
	//        temp = dit->d_name;
	//        indx = temp.find(".csd", 0);
	//        std::string validExt = Trim(temp.substr(indx + 1));
	//        if (!validExt.compare("csd"))
	//        {
	//            if (strlen(csd_path) != 0) {
	//                name = csd_path;
	//                name.append("/");
	//                name.append(temp);
	//            }
	//            else name = temp;
	//            if (i < MAXPLUGINS) {
	//                csdnames.push_back(name);
	//                i++;
	//            }
	//        }
	//    }
	//    closedir(dip);
	//    free(src);
	//    return csdnames;
}
*/
//================================================================
// simple function for loading information about controls 
// and Csound channels to vector
//================================================================
static std::vector<CsoundChannel> GetCsoundChannelVector(std::string csdFile)
{
	std::vector<CsoundChannel> csndChannels;

	std::ifstream input(csdFile.c_str());

	std::string line;
	while (std::getline(input, line))
	{
		if (line.find("</") != std::string::npos)
			break;

		std::string newLine = line;
		std::string control = line.substr(0, line.find(" ") != std::string::npos ? line.find(" ") : 0);
		std::string::size_type i = newLine.find(control);

		if (i != std::string::npos)
			newLine.erase(i, control.length());

		if (control.find("slider") != std::string::npos ||
			control.find("button") != std::string::npos ||
			control.find("checkbox") != std::string::npos ||
			control.find("groupbox") != std::string::npos ||
			control.find("form") != std::string::npos)
		{
			CsoundChannel csndChannel;
			csndChannel.type = control;
			//init range
			csndChannel.range[MIN] = 0;
			csndChannel.range[MAX] = 1;
			csndChannel.range[VALUE] = 0;

			if (line.find("debug") != std::string::npos)
			{
				debugMode = true;
			}

			if (line.find("caption(") != std::string::npos)
			{
				std::string infoText = line.substr(line.find("caption(") + 9);
				infoText = infoText.substr(0, infoText.find(")") - 1);
				csndChannel.caption = infoText;
			}

			if (line.find("text(") != std::string::npos)
			{
				std::string text = line.substr(line.find("text(") + 6);
				text = text.substr(0, text.find(")") - 1);
				csndChannel.text = text;
			}

			if (line.find("channel(") != std::string::npos)
			{
				std::string channel = line.substr(line.find("channel(") + 9);
				channel = channel.substr(0, channel.find(")") - 1);
				csndChannel.name = channel;
			}

			if (line.find("range(") != std::string::npos)
			{
				std::string range = line.substr(line.find("range(") + 6);
				range = range.substr(0, range.find(")"));
				char* p = strtok(&range[0u], ",");
				int argCount = 0;
				while (p)
				{
					csndChannel.range[argCount] = atof(p);
					argCount++;
					//not handling increment or log sliders yet
					if (argCount == 3)
						break;
					p = strtok(NULL, ",");
				}
			}

			if (line.find("value(") != std::string::npos)
			{
				std::string value = line.substr(line.find("value(") + 6);
				value = value.substr(0, value.find(")"));
				csndChannel.range[VALUE] = value.length() > 0 ? atof(value.c_str()) : 0;
			}
			csndChannels.push_back(csndChannel);
		}
	}

	return csndChannels;
}


extern "C"
{
	//================================================================
	// this function is called when FMOD loads first
	//================================================================
	F_EXPORT FMOD_DSP_DESCRIPTION* F_CALL FMODGetDSPDescription()
	{
		//csdFilename = csdFilename;
		//        char filestring[1000];
		//        sprintf(filestring, "%s", csdFilename.c_str());
		//        CFStringRef ref = CFStringCreateWithCString(NULL, filestring, kCFStringEncodingUTF8);
		//        CFUserNotificationDisplayNotice(0, kCFUserNotificationPlainAlertLevel,
		//                                        NULL, NULL, NULL, CFSTR("Result"),
		//                                        ref, CFSTR("OK"));


		csoundChannels = GetCsoundChannelVector(csdFilename);
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_Senttovector", "%s", csdFilename.c_str());
		int params = csoundChannels.size();
		CsoundChannel csndChannel;

		for (int i = 0; i < params; i++)
		{
			if (csoundChannels[i].type == "form")
			{
				sprintf(FMOD_Csound_Desc.name, "%s", csoundChannels[i].caption.c_str());
				//remove form from control array as it does not control anything...
				csoundChannels.erase(csoundChannels.begin() + i);
				params = csoundChannels.size();
			}
		}

		params = csoundChannels.size();
		FMOD_Csound_Desc.numparameters = params;

		for (int i = 0; i < params; i++)
		{
			FMOD_Csound_dspparam[i] = &csoundParameters[i];
			// FMOD only allows automation of float parameters?!

			//if (csoundChannels[i].type == "button" || csoundChannels[i].type == "checkbox")
			//{
			//	FMOD_DSP_INIT_PARAMDESC_INT(
			//		csoundParameters[i],
			//		csoundChannels[i].name.c_str(),
			//		"",
			//		csoundChannels[i].text.c_str(),
			//		0,
			//		1,
			//		csoundChannels[i].range[VALUE],
			//		0,
			//		0);
			//}
			//else
			{
				FMOD_DSP_INIT_PARAMDESC_FLOAT(
					csoundParameters[i],
					csoundChannels[i].name.c_str(),
					"",
					csoundChannels[i].text.c_str(),
					csoundChannels[i].range[MIN],
					csoundChannels[i].range[MAX],
					csoundChannels[i].range[VALUE]);
			}
			//FMOD_DSP_INIT_PARAMDESC_FLOAT(csoundParameters[i], csoundChannels[i].name.c_str(), "", csoundChannels[i].text.c_str(), csoundChannels[i].range[Range::MIN], csoundChannels[i].range[Range::MAX], csoundChannels[i].range[Range::VALUE]);
		}

		return &FMOD_Csound_Desc;
	}

}

//========================================================================
// Simple Csound class that handles compiling of Csound and generating audio
//========================================================================
class FMODCsound
{
public:
	FMODCsound();

	void generate(float* outbuffer, float* inbuffer, unsigned int length, int channels);
	void setFormat(FMOD_CSOUND_FORMAT format) { m_format = format; }
	FMOD_CSOUND_FORMAT format() const { return m_format; }
#ifdef WIN64
	CSOUND* csound;

#else
	AndroidCsound* csound;
#endif
	int csoundReturnCode;
	int ksmpsIndex, ksmps;
	MYFLT cs_scale;
	MYFLT* csoundInput, *csoundOutput;
	
	int sampleIndex = 0;
	int CompileCsound(std::string csdFile);

	bool csoundCompileOk()
	{
		if (csoundReturnCode == 0)
			return true;
		else
			return false;
	}

private:
	float m_target_level;
	float m_current_level;
	int m_ramp_samples_left;
	FMOD_CSOUND_FORMAT m_format;
};

FMODCsound::FMODCsound()
{

}

int FMODCsound::CompileCsound(std::string csdFile)
{
	csoundInitialize(CSOUNDINIT_NO_ATEXIT);


#ifdef WIN64
	csound = csoundCreate(NULL);
	csoundCreateMessageBuffer(csound, 0);
#else 
	csound = new AndroidCsound();
	csound->setOpenSlCallbacks();
	csoundCreateMessageBuffer(csound->GetCsound(), 0);
	csound->SetHostImplementedAudioIO(1, 0);
#endif
	/*
	std::string csdText = "<CsoundSynthesizer>\n"
		"<CsOptions>\n"
		"-n -d\n"
		"</CsOptions>\n"
		"<CsInstruments>\n"
		"sr = 22050\n"
		"ksmps = 64\n    "
		"nchnls = 2\n"
		"0dbfs = 1\n"
		"instr 1\n"
		"a1 oscili .3, 300, 1\n"
		"outs a1, a1\n"
		"endin\n"
		"</CsInstruments>\n"
		"<CsScore>\n"
		"f1 0 1024 10 1\n"
		"i1 0 1000\n"
		"</CsScore>\n"
		"</CsoundSynthesizer>\n";
		*/
	


#ifdef WIN64
	csoundReturnCode = csoundCompileCsdText(csound, csdText.c_str());

	csoundStart(csound);
#else 
	//csoundReturnCode = csound->CompileCsdText(csdFileText.c_str());

	
#endif

	
	char* args[2];
	args[0] = (char*)"csound";
	char fileName[1024];
	sprintf(fileName, "%s", csdFile.c_str());
	__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_argsFilename", "%s", fileName);
	args[1] = fileName;
	
#ifdef WIN64
	csoundReturnCode = csoundCompile(csound, 2, (const char**)args);
	
#else 
	csoundReturnCode = csound->Compile(2, (const char**)args);
#endif

#ifdef WIN64
	if (csoundReturnCode == 0)
	{
		ksmps = csoundGetKsmps(csound);
		csoundPerformKsmps(csound);
		cs_scale = csoundGet0dBFS(csound);
		csoundInput = csoundGetSpin(csound);
		csoundOutput = csoundGetSpout(csound);
		
	}
	else
	{
		// fmod doesn't allow logging but if it does in the future, this should print information to the user
		// about possible problems in their instruments
	}
#else
	if (csoundReturnCode == 0)
	{
		//csound->Start();
		ksmps = csoundGetKsmps(csound->GetCsound());
		csoundPerformKsmps(csound->GetCsound());
		cs_scale = csoundGet0dBFS(csound->GetCsound());
		csoundInput = csoundGetSpin(csound->GetCsound());
		csoundOutput = csoundGetSpout(csound->GetCsound());
		
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_API", "%i", csound->GetAPIVersion());
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_Version", "%i", csound->GetVersion());
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_Ksmps", "%i", csound->GetKsmps());
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_SR", "%f", csound->GetSr());
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_MYFLT", "%i", csoundGetSizeOfMYFLT());
	}
	else
	{
		// fmod doesn't allow logging but if it does in the future, this should print information to the user
		// about possible problems in their instruments
	}

#endif // __ANDROID__


	return csoundReturnCode;

}

void FMODCsound::generate(float* outbuffer, float* inbuffer, unsigned int length, int channels)
{
	if (csoundCompileOk())
	{
		unsigned int samples = length;
		unsigned int position = 0;
		while (samples--)
		{
			//__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%i", (int)ksmpsIndex);
#ifdef WIN64
			if (ksmpsIndex >= ksmps)
			{
				csoundPerformKsmps(csound);
				ksmpsIndex = 0;
			}
#else
			if (ksmpsIndex >= ksmps)
			{
				csoundPerformKsmps(csound->GetCsound());
				//__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%i", csoundPerformKsmps(csound->GetCsound()));
				ksmpsIndex = 0;
			}

#endif // WIN64


			for (int chans = 0; chans < channels; chans++)
			{
				position = ksmpsIndex * channels;
				csoundInput[chans + position] = *inbuffer++;
				*outbuffer++ = csoundOutput[chans + position];
			}

			ksmpsIndex++;
		}

	}
}

FMOD_RESULT F_CALLBACK FMOD_Csound_dspcreate(FMOD_DSP_STATE* dsp_state)
{
	dsp_state->plugindata = (FMODCsound*)FMOD_DSP_ALLOC(dsp_state, sizeof(FMODCsound));
	//std::string test = csdFilename;
	int result = ((FMODCsound*)dsp_state->plugindata)->CompileCsound(csdFilename);
	__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_sentToCompile", "%s", csdFilename.c_str());
	if (!dsp_state->plugindata   || result != 0)
	{
		return FMOD_ERR_MEMORY;
	}
	
	return FMOD_OK;
}

FMOD_RESULT F_CALLBACK FMOD_Csound_dsprelease(FMOD_DSP_STATE* dsp)
{
	FMODCsound* state = (FMODCsound*)dsp->plugindata;
	FMOD_DSP_FREE(dsp, state);
	return FMOD_OK;
}

FMOD_RESULT F_CALLBACK FMOD_Csound_dspprocess(FMOD_DSP_STATE* dsp, unsigned int length, const FMOD_DSP_BUFFER_ARRAY* inbufferarray, FMOD_DSP_BUFFER_ARRAY* outbufferarray, FMOD_BOOL inputsidle, FMOD_DSP_PROCESS_OPERATION op)
{
	FMODCsound* state = (FMODCsound*)dsp->plugindata;

	if (op == FMOD_DSP_PROCESS_QUERY)
	{
		FMOD_SPEAKERMODE outmode = FMOD_SPEAKERMODE_DEFAULT;
		int outchannels = 0;

		// fixed at stereo for now....
		outmode = FMOD_SPEAKERMODE_STEREO;
		outchannels = 2;

		if (outbufferarray)
		{
			outbufferarray->speakermode = outmode;
			outbufferarray->buffernumchannels[0] = outchannels;
			outbufferarray->bufferchannelmask[0] = 0;
		}

		return FMOD_OK;
	}
#ifdef WIN64
	if (debugMode == true)
	{
		const int messageCnt = csoundGetMessageCnt(state->csound);
		for (int i = 0; i < messageCnt; i++)
		{
			printf("%s", csoundGetFirstMessage(state->csound));
			csoundPopFirstMessage(state->csound);
		}
	}
#else

	if (debugMode == true)
	{
		const int messageCnt = csoundGetMessageCnt(state->csound->GetCsound());
		
		for (int i = 0; i < messageCnt; i++)
		{
			printf("%s", csoundGetFirstMessage(state->csound->GetCsound()));
			__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%s", csoundGetFirstMessage(state->csound->GetCsound()));
			csoundPopFirstMessage(state->csound->GetCsound());
		}
		
	}

#endif
	//number of inputs has to mathc number of outputs...
	state->generate(outbufferarray->buffers[0], inbufferarray->buffers[0], length, outbufferarray->buffernumchannels[0]);
	return FMOD_OK;
}

#ifdef WIN64
FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparamfloat(FMOD_DSP_STATE* dsp_state, int index, float value)
{
	FMODCsound* state = (FMODCsound*)dsp_state->plugindata;

	if (index < csoundChannels.size())
	{
		std::string channelName = csoundChannels[index].name;
		csoundSetControlChannel(state->csound, csoundChannels[index].name.c_str(), value);
		return FMOD_OK;
	}

	return FMOD_ERR_INVALID_PARAM;

}


FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparamint(FMOD_DSP_STATE* dsp_state, int index, int value)
{
	FMODCsound* state = (FMODCsound*)dsp_state->plugindata;
	// this function, and the setbool one are not being used becase fmod doesn't allow automation of anything 
	// other than float parameters...
	if (index < csoundChannels.size())
	{
		std::string channelName = csoundChannels[index].name;
		csoundSetControlChannel(state->csound, csoundChannels[index].name.c_str(), value);
		return FMOD_OK;
	}

	return FMOD_ERR_INVALID_PARAM;
}
#else
FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparamfloat(FMOD_DSP_STATE* dsp_state, int index, float value)
{
	FMODCsound* state = (FMODCsound*)dsp_state->plugindata;

	if (index < csoundChannels.size())
	{
		std::string channelName = csoundChannels[index].name;
		csoundSetControlChannel(state->csound->GetCsound(), csoundChannels[index].name.c_str(), value);
		return FMOD_OK;
	}

	return FMOD_ERR_INVALID_PARAM;

}


FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparamint(FMOD_DSP_STATE* dsp_state, int index, int value)
{
	FMODCsound* state = (FMODCsound*)dsp_state->plugindata;
	// this function, and the setbool one are not being used becase fmod doesn't allow automation of anything 
	// other than float parameters...
	if (index < csoundChannels.size())
	{
		std::string channelName = csoundChannels[index].name;
		csoundSetControlChannel(state->csound->GetCsound(), csoundChannels[index].name.c_str(), value);
		return FMOD_OK;
	}

	return FMOD_ERR_INVALID_PARAM;
}
#endif

FMOD_RESULT F_CALLBACK FMOD_Csound_dspreset(FMOD_DSP_STATE* dsp)
{
	return FMOD_OK;
}


FMOD_RESULT F_CALLBACK FMOD_Csound_dspgetparamfloat(FMOD_DSP_STATE* dsp, int index, float* value, char* valuestr)
{
	return FMOD_OK;
}

FMOD_RESULT F_CALLBACK FMOD_Csound_dspsetparambool(FMOD_DSP_STATE* dsp, int index, FMOD_BOOL value)
{
	return FMOD_OK;
}


FMOD_RESULT F_CALLBACK FMOD_Csound_dspgetparamint(FMOD_DSP_STATE* dsp, int index, int* value, char* valuestr)
{
	return FMOD_OK;
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using UnityEngine.Scripting;

public class CallPlugin : MonoBehaviour
{
    [DllImport("CsoundFMOD")]
    static extern void PassText(string text);
    //unsafe private static extern bool PostprocessNative(int width, int height, short* data_out);

    public static string filename =  "libCsoundFMOD.csd";
    public static string pathtofile { get { return Path.Combine(Application.streamingAssetsPath, filename); } }

    private void Awake()
    {

        GarbageCollector.GCMode = GarbageCollector.Mode.Disabled;

        var splithPath = pathtofile.Split('!');
        var dataPath = Path.Combine(Application.persistentDataPath, splithPath[1].Substring(1));

        if (File.Exists(dataPath))
        {
            Debug.Log("File exists");
            Debug.Log(dataPath);
            PassText(dataPath);
        }
        else
        {
            Debug.Log("File doesn't exist, writing to persistent data...");
            // read the file out of the apk and save it to persistent data.
            var request = UnityWebRequest.Get(pathtofile).SendWebRequest();

            request.completed += delegate (AsyncOperation operation)
            {
                var asyncWebOp = operation as UnityWebRequestAsyncOperation;

                if (asyncWebOp.webRequest.result == UnityWebRequest.Result.Success)
                {
                    var directory = Path.GetDirectoryName(dataPath);

                    Directory.CreateDirectory(directory);

                    File.WriteAllBytes(dataPath, asyncWebOp.webRequest.downloadHandler.data);

                    Debug.Log(dataPath);
                    PassText(dataPath);
                }
                else Debug.Log("Error");
            };
        }

    }

   

}

This are the two scripts. It’s probably overcomplicated on the Unity side. It’s just something Frankensteined ^^’

And I couldn’t hold back to just put in a script Dllimport of the other dependant libraries and test it. It doesn’t work. Crash on GC.

I had a look at the code.
It is not clear to me why it crashes. I haven’t had the chance to try it since I have no FMOD installed.
Are you using the latest version of FMOD?
I think that first we should understand which address is changing, and then producing a crash.
First thing I’d try is to pin the string you send from C# (even if it seems to be used only once).
Declare the argument as IntPtr and use Marshal.StringToHGlobalAnsi() to create the pointer value, something like:

C++

void* csdContent;

extern "C"
{
    void PassText(void* str)
    {
        csdContent = str;      
    }
}  

C#

[DllImport("CsoundFMOD", CharSet = CharSet.Ansi)] // add the CharSet.Ansi, it could help with strings!
static extern void PassText(IntPtr stringPtr);

In the Awake function, if the file doesn’t exist (the Android section):

...
if (asyncWebOp.webRequest.result == UnityWebRequest.Result.Success)
{
    var directory = Path.GetDirectoryName(dataPath);
    Directory.CreateDirectory(directory);
    File.WriteAllBytes(dataPath, asyncWebOp.webRequest.downloadHandler.data);
    Debug.Log(dataPath);
    IntPtr ptr = Marshal.StringToHGlobalAnsi(dataPath);
    PassText(ptr);
}
...

This won’t probably solve the issue, but I think you’re right, something has to be pinned to avoid the GC to collect it.
And again, try not to pass the path of the file: pass its string content instead!

Thank you very much for looking into it and I’m very sorry for this ginormous late reply.
My project still isn’t finished and I’m very limited in time.
In a bit more than a month everything will be finished and I’ll be able to hopefully find a proper solution to the whole Android-FMOD-Unity-GC problem with passing the string directly.

Sorry again for my silence.

I just wrote here another problem which I’ve misinterpretated. mtof seems to be not supported on csoundandroid or something along the line. This isn’t related to the core problem.