Cabbage Logo
Back to Cabbage Site

Cabbage FMOD Android .so support

Exactly I’m testing with Unity. I have a basic setup. It calls for an lib .so file. This is already working. I didn’t know you have a .dll, this was stupid ^^’. But what name should the csd file be? fmod_csound64.csd?

The .so and csd file is in the right spot and it builds to the quest without failure, but I’m working with FMODStudio to make events and not directly with the api in Unity. So FMOD Studio asks for a .dll. (And unity too for playtesting)
This worked all fine with your custom csound snippet.

It works! I had to change the form caption or plugin id inside of the csd file to what I assumed is the one of the dll.
The UI is now also updated and fully working.
Now comes the most interesting part.

Would have been nice, but there’s an appcrash with following fmod errors:

[FMOD] assert : assertion: ‘cont’ failed
[FMOD] assert : assertion: ‘isEmpty()’ failed

This is my .cpp file for .so building.

/*
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"
#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 = false;

//===========================================================
// 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. */
};


//===========================================================
// 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(".");
		std::string fullFilename = fileName.substr(0, lastindex);
		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 = 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(csdFilename);
		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();
	csoundCreateMessageBuffer(csound->GetCsound(), 0);
#endif
	/*
	std::string csdText = "<CsoundSynthesizer>\n"
		"<CsOptions>\n"
		"csound -+rtaudio=jack -odac -B4096\n"
		"</CsOptions>\n"
		"<CsInstruments>\n"
		"sr = 44100\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(csdText.c_str());

	//csound->Start();
#endif

	
	char* args[2];
	args[0] = "csound";
	char fileName[1024];
	sprintf(fileName, "%s", csdFile.c_str());
	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)
	{
		ksmps = csoundGetKsmps(csound->GetCsound());
		csoundPerformKsmps(csound->GetCsound());
		cs_scale = csoundGet0dBFS(csound->GetCsound());
		csoundInput = csoundGetSpin(csound->GetCsound());
		csoundOutput = csoundGetSpout(csound->GetCsound());
	}
	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--)
		{

#ifdef WIN64
			if (ksmpsIndex >= ksmps)
			{
				csoundPerformKsmps(csound);
				ksmpsIndex = 0;
			}
#else
			if (ksmpsIndex >= ksmps)
			{
				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);
	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()));
			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;
}

Maybe you know what’s wrong. If no I’ll try to search a solution later.

But it works nicely inside fmod and the editor.

It’s really impossible for me to know what the problem is. You would need to run this through a debugger somehow. We don’t know if the error is to do with your code or FMOD. Does it give any lines numbers? Are you creating a debug build or a release build?

Yes, I just thought that you maybe ran into similar issues before.
It’s in a deep profiling build which connects to Unity. It’s unfortunately just those lines, which are relevant.
I’ll try to find a solution.

I think if I was doing this I would use the CMake system they recommend and generate an Android Studio project with it. Then take it from there to be honest. I’m not sure I would have the patience to create an new build system.

I now found out that the error is, that .csd file isn’t found. I can recreate it in the editor by renaming the csd file…
Btw, I also realised that csoundandroid isn’t needed, so everything is much simpler than anticipated.

When looking at the build apk, the csd file isn’t included indeed, so I moved the file to the Streaming Assets folder where all data types are transfered.
In the c++ I changed the function to this and after it didn’t work because I just wrote this blindly, I explicitly wrote the path, but that’s also not working…

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 lastslash = fileName.find_last_of("/");
		std::string fullFilename = fileName.substr(lastslash + 1, lastindex);
		fullFilename.append(".csd");
		fullFilename = "assets/" + fullFilename;
		
		//return fullFilename;
		return "assets/CsoundFMOD.csd";
	}
}

I’ve been trying now quite a while, how to determine the right path. Maybe this isn’t even possible inside in apk, I have no idea. The closest I got to a solution is answered here, but this handles urls. stackoverflow.com/a/22254488/385478
Do you have an idea?

On Android you need to use resource folders. This is why my first suggestion was to simply emded the csd text into the source.

How can you run your Csound orchestra without the Csound library?

Right, this makes sense. I tried now writing the whole cs file into the .so, which looks like this.

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";

The app crashes upon loading and there aren’t any error messages… the debugs from android debug are also very ambiguous.
I think this is an end for now. I know that I won’t be able to stop trying it out. But for now I have no idea what to try out else now.
Thank you very much for your time nevertheless!

And I just meant the standard csound library builds also fine, when using your original cpp file. I tried it with both variants, csound and androidcsound and both give the same result without any direct unity errors and the same android error which says
07-15 17:33:20.088: W/InputDispatcher(992): channel '21f3786 com.DefaultCompany.FMODPlugTestAndr/com.unity3d.player.UnityPlayerActivity (server)' ~ Consumer closed input channel or an error occurred. events=0x9
07-15 17:33:20.088: E/InputDispatcher(992): channel '21f3786 com.DefaultCompany.FMODPlugTestAndr/com.unity3d.player.UnityPlayerActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
Which would require some good crystal-wizardry to trace back to the code…

How are you calling the Csound compile? The first thing to do is make sure you have it working in the desktop version with a csd string. No point in moving to Android until you know that works.

Like this:

csoundReturnCode = csound->CompileCsdText(csdPlugText.c_str());
csound->Start();

But you’re right, when trying it for desktop with:

csoundReturnCode = csoundCompileCsdText(csound, csdPlugText.c_str());
csoundStart(csound);

It doesn’t work inside FMOD, but at least, this is better debugable. :+1:

Does it crash the desktop build when you try to run it?

Yes, that’s what I just found out. It crashes the editor without any log.
Running it through a debugger says Unity exited with code 0.

Can you post the entire code again?

Btw, are you running this through a debugger? I mean do you launch Unity through VS?

Sure:

/*
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"
#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 = false;

//===========================================================
// 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. */
};


//===========================================================
// 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(".");
		std::string fullFilename = fileName.substr(0, lastindex);
		fullFilename.append(".csd");
		return fullFilename;

		//fullFilename = "jar:file://" + Application.dataPath + "!/assets/";
	}
}
*/
#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 = 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(csdFilename);
		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; }

	CSOUND* csound;


	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);

	csound = csoundCreate(NULL);
	csoundCreateMessageBuffer(csound, 0);



	std::string csdText = "<CsoundSynthesizer>\n"
		"<CsOptions>\n"
		"csound -+rtaudio=jack -odac -B4096\n"
		"</CsOptions>\n"
		"<CsInstruments>\n"
		"sr = 44100\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";

	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";




	csoundReturnCode = csoundCompileCsdText(csound, csdPlugText.c_str());

	csoundStart(csound);


	/*
	char* args[2];
	args[0] = "csound";
	char fileName[1024];
	sprintf(fileName, "%s", csdFile.c_str());
	args[1] = fileName;
	*/
	//csoundReturnCode = csoundCompile(csound, 2, (const char**)args);



	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
	}


	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--)
		{

			if (ksmpsIndex >= ksmps)
			{
				csoundPerformKsmps(csound);
				ksmpsIndex = 0;
			}



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

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

	//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, 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;
}
#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;
}

Yes, I connected the Unity Debugger through VS.

None of those widgets will be parsed because that element of CsoundFMOD must read a file from disk. What happens when you use the csdText code above it? The one I suggested you use earlier on in this process?

Btw, replace this CsOptions in taht string, ie.,

"csound -+rtaudio=jack -odac -B4096\n"

with

"-n -d"

You really need to get the simplest of examples working before you look at something with widgets. :+1: Also, I have to leave this now, but I’ll check in again tomorrow.

That makes a lot of sense too, but I didn’t think of that.

This builds and the sound is playing in FMOD and the editor. With that change to -n -d, everthing works perfectly fine.

However on the Quest I still have no audio. I tried with both androidcsound and csound and both give the same output, all is running well except the audio and after a few (10ish) seconds in the scene it crashes… I also saw that I’m in 48000 in FMOD but 41000 in csound but I changed that and it also has no effect. I tried a separate event to see, wether fmod gets initialised properly. The other event plays fine until it crashes. Unfortunately without any log.
In the editor I ran it for 3-4 mins without any problems and with audio output from csound.