Cabbage Logo
Back to Cabbage Site

Cabbage FMOD Android .so support

I’ve done a few little games for Android with Unity. I know my phone can work with it. :+1: I’ll try it when I get a chance. But I’ve never used FMOD…

Great! That’s no problem. I have everything setup in the project concerning fmod. You just have to adjust the architecture to il2cpp and 64 bit in the playersettings, I forgot that…

Today I tried with a mono arm build which behaved similarly. Most of the time it played longer though until it crashed.
I also found a forum post in which you were also involved where it was stated that the MYFLT should be turned into floats instead of doubles. But that’s probably more of a performance issue and maybe totally outdated and changed nothing anyways.

I made changes to the project as I found out that it wasn’t playing correctly and couldn’t debug. I found out that’s possible to debug non VR Apps on Oculus which is handy. But the Csound Plugin just gets completely ignored now while testing, which is interesting…
I have the updated project here if you want to give it a try.

FMOD wise there is just important to know, that if you have a custom Plugin in an FMOD project, you have to specifiy it in the Unity FMOD settings (FMOD->Edit Settings). And those plugins have to also be present inside the Assets Folder inside the FMOD Plugin folder (\Assets\Plugins\FMOD\lib).
Interestingly enough FMOD asks for libsndfile and libc++_shared and csoundandroid if they’re not also present in the folder.
There is also a bug withing the Unity version that I’m using that the plugins don’t get correctly imported. So if the plugin isn’t found even though it’s there, that could be the issue. The architecture has to be selected again and applied in the inspector when having the plugin selected.
Just a few experiences I’ve made if you venture to tackle it yourself :grinning:.

No, that’s because it should be floats on Android as far as I know. Worth a shot.

I haven’t had time to return to this since. Apologies. It’s on my todo list…

No problem!
I just can’t get my hands of it with trying for a solution.
I think, you’ll have more luck than me.
And no stress. I’m just writing stuff here that I found out, I could also keep myself shorter maybe ^^’.
But one last thing I wanted to add is, when targetting both architectures in il2cpp, it plays for a very long time before crashing (around 30-40 seconds) but still without sound. But it influences the crashing.

Tbh, I’m quite addicted to solving this at this point… but I think I finally found the problem!
I was linking csound 6.15 with csoundandroid 6.16. Or at least that’s one change I’ve made and now and also defining MYFLT as float in the csound.h.
It plays the sound :partying_face:!
Completely at a wrong pitch.
But that’s also discussed here I saw:

For me it’s more like 22000Hz, as it plays the pitch way too low.

And it still crashes. But I’ve turned it into an instrument. When triggering it multiple times, you can kind of reset everything and it crashes later. But also when the FMOD event ends, it crashes. Everthing without an error…
But it’s nevertheless a good step. I’m ignoring now the crash and look into the effect, maybe by trying to get the filereading, which might not work, even though I have the path correctly set. I’ve no clue if this is also a thing with permissions, as I’m always getting a read permission warning, every time csound gets loaded.
I’ll update once again when I find sth out.

Nice work! I think it’s safest to avoid reading of any files on Android if you can. You’re most interested in generating effects? It’s a pity there is no proper debugging output when it crashes. Is there a log anywhere that you can check?

Yes exactly! The one I’ve made above is really simple but it’s using a few knobs. Those aren’t taken into account with compilecsdtext, you’re handling this with csoundChannelVector and using the channelinfos in the dsp description function. So the way would be to feed the same c_str into csoundChannelVector?
I’ll try that as soon as I have time again.

Yeah, instead of passing a file, just pass the entire string. something like:

static std::vector<CsoundChannel> GetCsoundChannelVector(std::string csdFileText)
{
	std::vector<CsoundChannel> csndChannels;

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

Somehow it doesn’t work yet.
After ifstream, it doesn’t enter the getline while loop… I thought it’s maybe because of a different lineending but the reference says it goes until “\n”. So I also tried to remove the .c_str but that makes no difference.

I’ll post the current code again here (I’m sorry for the messyness…): (separate post)

As you can see, I also check for the input-filelength to see, whether this works and it does. Even though there are about 120 characters less, than when I’m counting them with an online counting tool.

Do you have an idea what is wrong here? I’m not getting any error messages unfortunately just no audio…

/*
Copyright (C) 2016 Rory Walsh

*/
#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>



#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::"));
}

std::string csdPlugText = "<Cabbage>\n"
"form caption(\"FMOD Csound\") size(400, 300), colour(58, 110, 182), pluginId(\"defi\")\n"
"rslider bounds(276, 156, 100, 100), channel(\"gain\"), range(0, 1, 0, 1, 0.01), text(\"Gain\"), trackerColour(0, 255, 0, 255), outlineColour(0, 0, 0, 50), textColour(0, 0, 0, 255)\n"
"rslider bounds(30, 180, 60, 60) range(0, 1, 0, 1, 0.001) channel(\"rmidVol\") text(\"Mid\")\n"
"rslider bounds(122, 182, 60, 60) range(0, 1, 0, 1, 0.001) channel(\"rsideVol\") text(\"Side\")\n"
"</Cabbage>\n"
"<CsoundSynthesizer>\n"
"<CsOptions>\n"
"-n -d -+rtmidi=NULL -M0 -m0d\n"
"</CsOptions>\n"
"<CsInstruments>\n"
"; Initialize the global variables.\n"
"ksmps = 32\n"
"nchnls = 2\n"
"0dbfs = 1\n"


"instr 1\n"
"kGain chnget \"gain\"\n"
"kMidGain chnget \"rmidVol\"\n"
"kSideGain chnget \"rsideVol\"\n"

"a1 inch 1\n"
"a2 inch 2\n"


"amid = a1 + a2\n"
"aside = a1 - a2\n"



"amid2 = amid * kMidGain\n"
"aside2 = aside * kSideGain\n"

"aleft = amid2 + aside2\n"
"aright = amid2 + (aside2 * -1)\n"

"outs aleft * kGain, aright * kGain\n"
"endin\n"

"</CsInstruments> \n"
"<CsScore>\n"
"; causes Csound to run for about 7000 years...\n"
"f0 z\n"
"; starts instrument 1 and runs it for a week\n"
"i1 0[60 * 60 * 24 * 7]\n"
"</CsScore>\n"
"</CsoundSynthesizer> \n";




//===========================================================
// 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 csdFileText)
{
	std::vector<CsoundChannel> csndChannels;

	__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_Filelen", "%lu", csdFileText.length());

	std::ifstream input(csdFileText);
	
	
	std::string line;
	while (std::getline(input, line))
	{
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_Filelen", "%s", "WhileLoop");
		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);
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_Cntrl", "%s", control.c_str());
		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;
				__android_log_print(ANDROID_LOG_INFO, "AndroidCsound_ChnName", "%s", csndChannel.name.c_str());
			}

			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 = GetCsdFilename();
		//        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(csdPlugText);
		int params = csoundChannels.size();
		__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%i", params);
		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());
				__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%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 csdFileText);

	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 csdFileText)
{
	csoundInitialize(CSOUNDINIT_NO_ATEXIT);


#ifdef WIN64
	csound = csoundCreate(NULL);
	csoundCreateMessageBuffer(csound, 0);
#else 
	csound = new AndroidCsound();
	csound->setOpenSlCallbacks();
	csoundCreateMessageBuffer(csound->GetCsound(), 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", "%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_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];
				//__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%f", csound->GetSpoutSample(chans, position));
				//__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%f", csoundOutput[chans + position]);
				//__android_log_print(ANDROID_LOG_INFO, "AndroidCsound", "%f", *csoundOutput);
				//__android_log_print(ANDROID_LOG_INFO, "AndroidCsound Out", "%f", *outbuffer);
				//__android_log_print(ANDROID_LOG_INFO, "AndroidCsound In", "%f", *inbuffer);
			}

			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(csdPlugText);
	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;
}

Check std::ifstream for an error. Perhaps something is catching it.

Yes, that was it!
Solved it by using istringstream and #include sstream.
As always makes a lot of sense after finding it out ^^.

But the effect works flawlessly! Yay :grin:
Now there is just this crashing…

This is the logcat when the crash happens and no clue why this is happening…
Maybe this has also to do with the Unity Version I’m using, I don’t know. But it really just happens when the plugin is used. It now happens after more than a minute.
It doesn’t seem to be connected to a distinct process… something is just wrong or incopatible.

2021.07.26 17:38:18.897 838 16663 Warn Memory Allocation large allocation 4001056
2021.07.26 17:38:18.905 2474 2577 Info GuardianSystem Memory pressure changed to None
2021.07.26 17:38:18.912 1816 17321 Warn PipeUtils RuntimeIPC: ReadFromPipeInternal: read failed: 0, Socket operation on non-socket
2021.07.26 17:38:18.912 1816 17321 Warn PipeUtils RuntimeIPC: ReadFromPipe: ReadFromPipeInternal FAILED
2021.07.26 17:38:18.912 2474 17320 Warn PipeUtils RuntimeIPC: ReadFromPipeInternal: read failed: 0, Success
2021.07.26 17:38:18.912 1816 17321 Warn RuntimeIPCServerMgr RPCClientHandler::MainLoop: Error reading pipe: Client: com.DefaultCompany.FMODPlugTestAndr:com.DefaultCompany.FMODPlugTestAndr:17215, Server: com.oculus.systemdriver:com.oculus.vrruntimeservice (RuntimeServiceServer)
2021.07.26 17:38:18.912 2474 17320 Warn PipeUtils RuntimeIPC: ReadFromPipe: ReadFromPipeInternal FAILED
2021.07.26 17:38:18.912 2474 17320 Warn RuntimeIPCServerMgr RPCClientHandler::MainLoop: Error reading pipe: Client: com.DefaultCompany.FMODPlugTestAndr:com.DefaultCompany.FMODPlugTestAndr:17215, Server: com.oculus.guardian:com.oculus.vrguardianservice (guardianserver)
2021.07.26 17:38:18.912 1816 17321 Info VrRuntimeService RuntimeIPC: IPC_SYSTEM_EVENT_CLIENT_DISCONNECTED_EXT: com.DefaultCompany.FMODPlugTestAndr
2021.07.26 17:38:18.912 1816 17321 Info ClientMgr ClientMgr::DestroyClient: 17215:com.DefaultCompany.FMODPlugTestAndr
2021.07.26 17:38:18.912 1816 17321 Info ClientMgr ClientMgr::DestroyClient: 17215:com.DefaultCompany.FMODPlugTestAndr is enabled for rendering on destroy.
2021.07.26 17:38:18.912 1816 17321 Info ClientMgr ClientMgr::EnableRendering 17215:com.DefaultCompany.FMODPlugTestAndr : type 0 comp-id 54: disable
2021.07.26 17:38:18.912 1816 17321 Info VrRuntimeServer DisableCompositorClient: 54
2021.07.26 17:38:18.912 2474 17311 Warn PipeUtils RuntimeIPC: ReadFromPipeInternal: read failed: 0, Socket operation on non-socket
2021.07.26 17:38:18.912 1816 17321 Info TimeWarpClientMgr Disable client 54
2021.07.26 17:38:18.912 1816 17321 Info TimeWarpClientMgr DisableClient: frame flush required: 1
2021.07.26 17:38:18.912 2474 17311 Warn PipeUtils RuntimeIPC: ReadFromPipe: ReadFromPipeInternal FAILED
2021.07.26 17:38:18.913 2474 17311 Warn RPCServerHandlerHelper RPCServerHandlerHelper::MainLoop: Error reading pipe: ServerProcessConnection: com.DefaultCompany.FMODPlugTestAndr:com.DefaultCompany.FMODPlugTestAndr:17215
2021.07.26 17:38:18.913 1816 17313 Warn PipeUtils RuntimeIPC: ReadFromPipeInternal: read failed: 0, Socket operation on non-socket
2021.07.26 17:38:18.913 1816 17313 Warn PipeUtils RuntimeIPC: ReadFromPipe: ReadFromPipeInternal FAILED
2021.07.26 17:38:18.913 1816 17313 Warn RPCServerHandlerHelper RPCServerHandlerHelper::MainLoop: Error reading pipe: ServerProcessConnection: com.DefaultCompany.FMODPlugTestAndr:com.DefaultCompany.FMODPlugTestAndr:17215
2021.07.26 17:38:18.916 983 1070 Warn InputDispatcher channel '22df4b1 com.DefaultCompany.FMODPlugTestAndr/com.unity3d.player.UnityPlayerActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x9
2021.07.26 17:38:18.916 983 1070 Error InputDispatcher channel '22df4b1 com.DefaultCompany.FMODPlugTestAndr/com.unity3d.player.UnityPlayerActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
2021.07.26 17:38:18.916 1764 1792 Debug RuntimeIPCService binderDied: com.DefaultCompany.FMODPlugTestAndr
2021.07.26 17:38:18.916 1764 1792 Debug ThreadHelpers RuntimeIPC: ThreadBase: Shutdown
2021.07.26 17:38:18.916 1764 1792 Debug ThreadHelpers RuntimeIPC: ThreadBase: RequestShutdown
0001.01.01 00:00:00.000 -1 -1 Info  --------- beginning of tracking
2021.07.26 17:36:22.877 838 5189 Info chatty uid=1000(system) Binder:838_5 expire 3 lines
2021.07.26 17:36:22.877 838 5189 Info TrackingService TrackingServiceHost registered common client; there are 0 secure clients
2021.07.26 17:38:18.916 838 1744 Info TrackingService Unregistering client 'com.DefaultCompany.FMODPlugTestAndr#17215' by binder death
2021.07.26 17:38:18.916 838 1744 Info TrackingService TrackingServiceHost Unregistering client: com.DefaultCompany.FMODPlugTestAndr#17215
2021.07.26 17:38:18.919 983 1000 Info WindowManager WIN DEATH: Window{22df4b1 u0 com.DefaultCompany.FMODPlugTestAndr/com.unity3d.player.UnityPlayerActivity}
2021.07.26 17:38:18.919 2474 2577 Info GuardianSystem Memory pressure changed to None
2021.07.26 17:38:18.919 983 1000 Warn InputDispatcher Attempted to unregister already unregistered input channel '22df4b1 com.DefaultCompany.FMODPlugTestAndr/com.unity3d.player.UnityPlayerActivity (server)'
2021.07.26 17:38:18.919 983 1323 Info ActivityManager Process com.DefaultCompany.FMODPlugTestAndr (pid 17215) has died: fore TOP 
2021.07.26 17:38:18.920 983 1012 Info libprocessgroup Successfully killed process cgroup uid 10113 pid 17215 in 0ms
2021.07.26 17:38:18.920 1764 1792 Info RuntimeIPCService DestroyBrokerConnection: com.DefaultCompany.FMODPlugTestAndr:com.DefaultCompany.FMODPlugTestAndr:17215
2021.07.26 17:38:18.921 705 705 Info Zygote Process 17215 exited due to signal 11 (Segmentation fault)
2021.07.26 17:38:18.922 1157 1157 Verbose AvrcpMediaPlayerList onPlaybackConfigChanged(): Configs list size=26

Remove the -+rtmidi=NULL -M0 -m0d\n from your CsOptions since it is not needed. Also, I’m not sure how the score processor works, but it looks like you might need a whitespace between the start time and the duration? You could also add a

printk 1, 1 to instrument 1 to see if that gets logged. It would be good to know if you can access the Csound output messages…

Removed those arguments and made a whitespace, but it makes no difference to the crashing problem.

printk works fine :+1: :+1:
This is the last output before crash:

2021.07.27 09:50:53.561 21402 21513 Info AndroidCsound  i   1 
2021.07.27 09:50:53.561 21402 21513 Info AndroidCsound time    59.03746: 
2021.07.27 09:50:53.561 21402 21513 Info AndroidCsound     1.00000

It now happens always at this 60 mark. Sometimes 60 also gets printed. This isn’t the time displayed on the right though. Is this the time calculated by the ksamples somehow?

Here a bigger trace (with the infos about the quest which are also logged):

2021.07.27 10:05:54.539 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:05:54.539 22925 23059 Info AndroidCsound time    51.03238: 
2021.07.27 10:05:54.539 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:05:55.377 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1082MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=6.74ms,GD=0.45ms,CPU&GPU=8.60ms,LCnt=1,GPU%=0.61,CPU%=0.12(W0.15),DSF=1.00
2021.07.27 10:05:56.371 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:05:56.371 22925 23059 Info AndroidCsound time    52.03302: 
2021.07.27 10:05:56.371 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:05:56.376 22925 23045 Info Telemetry App memory usage: PSS=192MB DalvikPSS=0 MB
2021.07.27 10:05:56.379 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1081MB,PLS=0,Temp=36.0C/0.0C,TW=2.96ms,App=6.75ms,GD=0.45ms,CPU&GPU=8.75ms,LCnt=1,GPU%=0.61,CPU%=0.16(W0.19),DSF=1.00
2021.07.27 10:05:57.377 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1082MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=6.65ms,GD=0.45ms,CPU&GPU=8.62ms,LCnt=1,GPU%=0.59,CPU%=0.10(W0.19),DSF=1.00
2021.07.27 10:05:58.207 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:05:58.207 22925 23059 Info AndroidCsound time    53.03365: 
2021.07.27 10:05:58.207 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:05:58.379 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=71,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1081MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=7.00ms,GD=0.46ms,CPU&GPU=9.17ms,LCnt=1,GPU%=0.62,CPU%=0.13(W0.19),DSF=1.00
2021.07.27 10:05:59.381 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1081MB,PLS=0,Temp=36.0C/0.0C,TW=2.96ms,App=6.85ms,GD=0.46ms,CPU&GPU=8.91ms,LCnt=1,GPU%=0.62,CPU%=0.13(W0.23),DSF=1.00
2021.07.27 10:06:00.063 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:06:00.063 22925 23059 Info AndroidCsound time    54.03429: 
2021.07.27 10:06:00.063 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:06:00.378 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1081MB,PLS=0,Temp=36.0C/0.0C,TW=2.98ms,App=7.03ms,GD=0.49ms,CPU&GPU=8.97ms,LCnt=1,GPU%=0.61,CPU%=0.10(W0.19),DSF=1.00
2021.07.27 10:06:01.378 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1081MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=6.74ms,GD=0.45ms,CPU&GPU=8.81ms,LCnt=1,GPU%=0.61,CPU%=0.10(W0.15),DSF=1.00
2021.07.27 10:06:01.899 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:06:01.899 22925 23059 Info AndroidCsound time    55.03492: 
2021.07.27 10:06:01.899 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:06:02.378 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=71,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1080MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=7.17ms,GD=0.49ms,CPU&GPU=9.20ms,LCnt=1,GPU%=0.61,CPU%=0.11(W0.15),DSF=1.00
2021.07.27 10:06:03.378 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1081MB,PLS=0,Temp=36.0C/0.0C,TW=2.96ms,App=6.99ms,GD=0.49ms,CPU&GPU=9.00ms,LCnt=1,GPU%=0.62,CPU%=0.10(W0.15),DSF=1.00
2021.07.27 10:06:03.731 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:06:03.731 22925 23059 Info AndroidCsound time    56.03556: 
2021.07.27 10:06:03.731 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:06:04.380 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1080MB,PLS=0,Temp=36.0C/0.0C,TW=2.98ms,App=7.20ms,GD=0.45ms,CPU&GPU=9.09ms,LCnt=1,GPU%=0.60,CPU%=0.10(W0.15),DSF=1.00
2021.07.27 10:06:05.378 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1080MB,PLS=0,Temp=36.0C/0.0C,TW=2.96ms,App=6.87ms,GD=0.44ms,CPU&GPU=8.75ms,LCnt=1,GPU%=0.61,CPU%=0.17(W0.23),DSF=1.00
2021.07.27 10:06:05.567 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:06:05.567 22925 23059 Info AndroidCsound time    57.03619: 
2021.07.27 10:06:05.567 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:06:06.378 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1080MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=6.57ms,GD=0.44ms,CPU&GPU=8.68ms,LCnt=1,GPU%=0.62,CPU%=0.14(W0.19),DSF=1.00
2021.07.27 10:06:07.379 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1080MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=7.10ms,GD=0.44ms,CPU&GPU=8.95ms,LCnt=1,GPU%=0.58,CPU%=0.13(W0.19),DSF=1.00
2021.07.27 10:06:07.403 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:06:07.403 22925 23059 Info AndroidCsound time    58.03683: 
2021.07.27 10:06:07.403 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:06:08.378 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1080MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=6.87ms,GD=0.44ms,CPU&GPU=8.84ms,LCnt=1,GPU%=0.61,CPU%=0.13(W0.19),DSF=1.00
2021.07.27 10:06:09.259 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:06:09.259 22925 23059 Info AndroidCsound time    59.03746: 
2021.07.27 10:06:09.259 22925 23059 Info AndroidCsound     1.00000
2021.07.27 10:06:09.379 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1554MHz,Free=1080MB,PLS=0,Temp=36.0C/0.0C,TW=2.97ms,App=6.32ms,GD=0.44ms,CPU&GPU=8.37ms,LCnt=1,GPU%=0.60,CPU%=0.13(W0.19),DSF=1.00
2021.07.27 10:06:10.381 22925 23045 Info VrApi FPS=72/72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1295MHz,Free=1081MB,PLS=0,Temp=36.0C/0.0C,TW=3.05ms,App=7.08ms,GD=0.45ms,CPU&GPU=8.80ms,LCnt=1,GPU%=0.62,CPU%=0.15(W0.18),DSF=1.00
2021.07.27 10:06:11.091 22925 23059 Info AndroidCsound  i   1 
2021.07.27 10:06:11.091 22925 23059 Info AndroidCsound time    60.03810: 
2021.07.27 10:06:11.091 22925 23059 Info AndroidCsound     1.00000

Does it run as long if you change ksmps to say 16, or 64?

ksmps modification changes nothing whatsoever, but the sr does. I made the same discovery with the previous sine example.
I added sr=24000, which syncs it perfectly. 48000 makes it half speed. It crashes after exactly 110 seconds.

What do you mean by syncs it perfectly? Does it not crash when you have this SR?

No, sorry. Just the logging of the realtime and the ksample time are in sync.
It always crashes.
When I set the sr=22050 it crashed on 120 seconds (ksmpstime) and when I didn’t write anything it probably defaults to 44100 so it crashed at 110 (ksampstime).

Is there some way you can check what the default SR is for FMOD/Unity on Android?