41f466ef3b49 — vnorilo@siba.fi 5 years ago
SuperCollider support
4 files changed, 434 insertions(+), 0 deletions(-)

A => SuperCollider.py
A => SuperCollider/makedef.cpp
A => SuperCollider/supercollider.cpp
A => SuperCollider/supercollider.k
A => SuperCollider.py +123 -0
@@ 0,0 1,123 @@ 
+from __future__ import print_function
+
+import cmake
+import re
+
+from string import Template
+
+def banner():
+	print("[Vst3 builder]")
+
+k_template = Template("""
+set(${name}_OUT "${CMAKE_CURRENT_BINARY_DIR}/${name}${CMAKE_C_OUTPUT_EXTENSION}")
+
+set(${name}_SRC "${CMAKE_CURRENT_SOURCE_DIR}/source.k")
+
+add_custom_command(
+	OUTPUT ${${name}_OUT}
+	COMMENT "Generating object code for ${name}"
+	DEPENDS ${${name}_SRC} "${builder_assets}/supercollider.k"
+	COMMAND "${KC_EXECUTABLE}" ${${name}_SRC}
+			--main "${extern}"
+			-i "${builder_assets}/supercollider.k"
+			--prefix "${name}_" -O 3 --output ${${name}_OUT}
+			${KC_FLAGS}
+)
+
+set(ALL_OBJECTS "${ALL_OBJECTS};$${${name}_OUT}")
+set(ALL_CLASSES "${ALL_CLASSES} F(${name})")
+set(CLASS_LOOP "${CLASS_LOOP};${name}")
+
+""")
+
+cmake_template = Template("""
+cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
+
+${cmake_variables}
+
+include_directories(
+	"${SUPERCOLLIDER_DIR}/include/plugin_interface" 
+	"${SUPERCOLLIDER_DIR}/include/common" 
+	"${common_assets}")
+
+project(${package})
+
+set(CMAKE_CXX_STANDARD 14)
+
+${build_externs}
+
+set(SYNTHDEF "${CMAKE_SOURCE_DIR}/artifacts/${package}.sc")
+
+add_executable(
+	makesynthdef
+	"${builder_assets}/supercollider.cpp"
+	"source.k"
+	${ALL_OBJECTS})
+
+add_custom_command(
+	OUTPUT ${SYNTHDEF}
+	COMMENT "Generating synthdef for ${name}"
+	DEPENDS ${${name}_SRC} "${builder_assets}/supercollider.k" makesynthdef
+	COMMAND makesynthdef "${SYNTHDEF}")
+
+add_library(
+	sc_plugin SHARED
+	"${builder_assets}/supercollider.cpp"
+	"source.k"
+	"${SYNTHDEF}"
+	${ALL_OBJECTS})
+
+
+if(NOT SC_SUFFIX) 
+	if(APPLE)
+		set(SC_SUFFIX ".scx")
+	elseif(UNIX)
+		set(SC_SUFFIX ${CMAKE_SHARED_MODULE_SUFFIX})
+	elseif(WIN32)
+		set(SC_SUFFIX ".scx")
+	endif()
+	message(STATUS "Setting suffix ${SC_SUFFIX}")
+endif()
+
+set_target_properties(
+	makesynthdef PROPERTIES
+	COMPILE_DEFINITIONS
+	"DSP_CLASSES=${ALL_CLASSES};SUPERCOLLIDER_MAKEDEF=1"	
+)
+
+set_target_properties(
+	sc_plugin PROPERTIES 
+	COMPILE_DEFINITIONS 
+	"DSP_CLASSES=${ALL_CLASSES}"
+	PREFIX ""
+	SUFFIX 
+	${SC_SUFFIX}
+	OUTPUT_NAME 
+	"${package}"
+	OUTPUT_DIRECTORY
+	"${CMAKE_SOURCE_DIR}/artifacts")
+""")
+
+strip_extern_re = re.compile("^:Extern:(.*)$")
+
+def build(syscfg, job):
+	cfg = syscfg.copy()
+	cfg.update(job)
+
+	externs = ""
+	for ext, versions in job["externs"].items():
+		for version in versions:
+			ext_name = ext + version
+			local_cfg = cfg.copy()
+			local_cfg["name"] = cmake.sanitize(strip_extern_re.match(ext_name).group(1))
+			local_cfg["extern"] = ext_name
+			externs = externs + k_template.safe_substitute(local_cfg)
+
+	cfg["build_externs"] = externs
+	cfg["cmake_variables"] = cmake.dict_to_vars(cfg["cmake"])
+
+	log = cmake.configure_and_build(cmake_template.safe_substitute(cfg))
+
+	return True;
+
+	
  No newline at end of file

          
A => SuperCollider/makedef.cpp +0 -0

        
A => SuperCollider/supercollider.cpp +275 -0
@@ 0,0 1,275 @@ 
+#include <SC_PlugIn.h>
+#include "kreflect.h"
+
+#ifdef SUPERCOLLIDER_MAKEDEF
+#include <stdio.h>
+#undef SETCALC
+#define SETCALC(fn)
+#undef SAMPLERATE
+#define SAMPLERATE 44100
+#undef BUFLENGTH
+#define BUFLENGTH 1
+#undef RTAlloc
+#define RTAlloc(m,sz) malloc(sz)
+#endif
+
+static InterfaceTable *ft = nullptr;
+
+using namespace KReflect;
+
+class Class;
+
+static bool SymbolIsVisible(krt_sym const & sym) {
+    return sym.size > 0 && (sym.flags & KRT_FLAG_NO_DEFAULT) == 0;
+}
+
+struct Instance : public Unit {
+	krt_class *k;
+	float* outBuf;
+	float* params;
+	void* instance;
+    krt_process_call audioClock = nullptr;
+
+	void Clear(int numSamples) {
+		Unit* unit = this;
+		for(int c=0;c<mNumOutputs;c++) {
+			for(int f=0;f<numSamples;f++) {
+				OUT(c)[f] = 0.f;
+			}
+		}
+	}
+
+	void Next(int numSamples) {
+		Unit* unit = this;
+		char *ptr = (char*)params;
+		int scIn = 0;
+
+        void *scratch = alloca(k->result_type_size);
+		for(int i=0;i<k->num_symbols;++i) {
+            auto& sym{k->symbols[i]};
+            if (SymbolIsVisible(sym)) {
+                if ((sym.flags & KRT_FLAG_STREAMING) != 0) {
+                    *k->var(instance, sym.slot_index) = IN(scIn++);
+                } else {
+                    float pv = IN0(scIn++);
+                    if (pv != *(float*)ptr) {
+                        *(float*)ptr = pv;
+                        *k->var(instance, sym.slot_index) = ptr;
+                        if (sym.process) {
+                            sym.process(instance, scratch, 1);
+                        }
+                    }
+                }
+            }
+
+			ptr += sym.size;
+		}
+        
+        if (audioClock) {
+            if (mNumOutputs > 1) {
+                audioClock(instance, outBuf, numSamples);
+                Deinterleave(mOutBuf, outBuf, numSamples, mNumOutputs);
+            } else {
+                audioClock(instance, mOutBuf[0], numSamples);
+            }
+        } else {
+            Clear(numSamples);
+        }
+	}
+};
+
+static float SampleRate = 44100.f;
+
+class Class {
+	krt_class *k;
+	size_t paramSize, unitSize;
+	const char *name;
+public:
+	Class(const char *name, krt_class *k):k(k),name(name) {
+		paramSize = SizeOfParams();
+		unitSize = k->get_size();
+	}
+
+	size_t SizeOfParams() {
+		size_t psz = 0;
+		for(int i=0;i<k->num_symbols;i++) {
+			psz += k->symbols[i].size;
+		}
+		psz = (psz + 15) & -16;
+		return psz;
+	}
+
+
+	static void NextProc(Instance* unit, int numSamples) {
+		unit->Next(numSamples);
+	}
+
+	static void NextFailure(Instance* unit, int numSamples) {
+		unit->Clear(numSamples);
+	}
+    
+    int GetNumOutputs() {
+        return GetFloatTypeDim(k->result_type_descriptor);
+    }
+
+	void Construct(Instance* unit) {
+		unit->k = k;
+        
+        char *bytes = (char*)RTAlloc(unit->mWorld, paramSize + unitSize);
+        unit->params = (float*)bytes;
+        memset(unit->params, 0, paramSize);
+        unit->instance = bytes + paramSize;
+        unit->audioClock = nullptr;
+        
+        memset(unit->instance, 0, k->get_size());
+		SampleRate = SAMPLERATE;
+
+        char *ptr = (char *)unit->params;
+		for(int i=0;i<k->num_symbols;++i) {
+			auto& sym{k->symbols[i]};
+            
+            if (!strcmp(sym.sym, "#Rate{audio}")) {
+                k->configure(sym.slot_index, &SampleRate);
+			} else if ((KRT_FLAG_NO_DEFAULT & sym.flags) != 0) {
+				Print("Configuration variable %s[%s] not supported\n", name, sym.sym);
+				SETCALC(NextFailure);
+				return;
+            } else if (!strcmp(sym.sym, "audio")) {
+                unit->audioClock = sym.process;
+            }            
+		}
+
+		k->construct(unit->instance, nullptr);
+		SETCALC(NextProc);
+                
+        if (unit->mNumOutputs > 1) {
+            unit->outBuf = (float*)RTAlloc(unit->mWorld, k->result_type_size * BUFLENGTH);
+        } else {
+            unit->outBuf = nullptr;
+        }
+	}
+
+	void Destruct(Instance* unit) {
+        if (unit->outBuf) {
+            RTFree(unit->mWorld, unit->params);
+            RTFree(unit->mWorld, unit->outBuf);
+            unit->params = nullptr;
+            unit->instance = nullptr;
+            unit->outBuf = nullptr;
+        }
+	}
+
+#ifdef SUPERCOLLIDER_MAKEDEF
+	void WriteSynthDef(FILE* sdef) {
+		Instance *instance = (Instance *)malloc(sizeof(Instance));
+		Construct(instance);
+
+		fprintf(sdef, "%s : %s {\n\t*ar { arg ", Sanitize(name).c_str(), GetNumOutputs() == 1 ? "UGen" : "MultiOutUGen");
+		bool first = true;
+		std::vector<std::string> symNames;
+        std::map<std::string, float> defaultVals;
+		for(int i=0;i<k->num_symbols;++i) {
+			auto& sym{k->symbols[i]};
+			if (SymbolIsVisible(sym)) {
+                std::string symName { sym.sym };
+                float **slot = (float**)k->var(instance->instance, sym.slot_index);
+                defaultVals[sym.sym] = (*slot ? **slot : 0.f);
+                symNames.push_back(Sanitize(StripOrdinal(symName)));
+			}
+		}
+        
+        for(auto &dv : defaultVals) {
+            if (first) {
+                first = false;
+            } else {
+                fprintf(sdef, ", ");
+            }
+            std::string sym = dv.first;
+            fprintf(sdef, "%s = %f", Sanitize(StripOrdinal(dv.first)).c_str(), dv.second);
+            
+        }
+        
+        fprintf(sdef, ";\n");
+        
+		for(int i=0;i<k->num_symbols;++i) {
+			auto& sym{k->symbols[i]};
+			if (sym.size > 0 && (sym.flags & KRT_FLAG_NO_DEFAULT) == 0) {
+				if ((sym.flags & KRT_FLAG_STREAMING) != 0) {
+                    auto nm = Sanitize(StripOrdinal(sym.sym));
+					fprintf(sdef, "\t\t(%s.rate != 'audio').if {\n", nm.c_str());
+					fprintf(sdef, "\t\t\t^\"<%s> must be an audio signal\".throw;\n", nm.c_str());
+					fprintf(sdef, "\t\t};\n");
+				}
+			}
+		}
+
+		fprintf(sdef, "\t\t^this.multiNew('audio'");
+		for(auto &s:symNames) {
+			fprintf(sdef, ", %s", s.c_str());
+		}		
+        fprintf(sdef, ");\n\t}");
+        
+        if (GetNumOutputs() > 1) {
+            fprintf(sdef,
+                    "\n\n\tinit { arg ... theInputs;\n"
+                    "\t\tinputs = theInputs;\n"
+                    "\t\t^this.initOutputs(%i, rate);\n"
+                    "\t}", GetNumOutputs());
+        }
+        
+        fprintf(sdef, "\n}\n\n");
+		free(instance);
+	}
+#endif
+};
+
+#define F(CLASS) \
+extern "C" krt_class* CLASS ## _GetClassData(); \
+static Class Wrap ## CLASS { #CLASS, CLASS ## _GetClassData() }; \
+void Construct ## CLASS(Unit* i) { Wrap ## CLASS . Construct((Instance*)i); } \
+void Destruct ## CLASS(Unit* i) { Wrap ## CLASS . Destruct((Instance*)i); }
+DSP_CLASSES
+#undef F
+
+void Export(const char *className, Class *c, UnitCtorFunc ctor, UnitDtorFunc dtor) {
+	(*ft->fDefineUnit)(
+		className,
+		sizeof(Instance),
+		ctor,
+		dtor,
+		kUnitDef_CantAliasInputsToOutputs
+	);
+}
+
+extern "C" {
+#ifdef WIN32
+#define EXPORT __declspec(dllexport)
+#else 
+#define EXPORT __attribute__((visibility("default")))
+#endif
+
+	EXPORT void load(InterfaceTable* SCHost) {
+		ft = SCHost;
+		#define F(CLASS) Export(#CLASS, &Wrap ## CLASS, Construct ## CLASS, Destruct ## CLASS);
+			DSP_CLASSES
+		#undef F
+	}
+
+    EXPORT int api_version(void) { return sc_api_version; }
+}
+
+#ifdef SUPERCOLLIDER_MAKEDEF
+int main(int n, char *argv[]) {
+	FILE *def = stdout;
+	if (n>1) {
+		def = fopen(argv[1],"wt");
+	} 
+#define F(CLASS) Wrap ## CLASS . WriteSynthDef(def);
+	DSP_CLASSES
+#undef F
+	if (def != stdout) {
+		fclose(def);
+	}
+	return 0;
+}
+#endif

          
A => SuperCollider/supercollider.k +36 -0
@@ 0,0 1,36 @@ 
+Import main
+Import IO
+
+Package Extern-Interface {
+	Control(param default type curve low high) {
+		Control = Control:Param(param 
+			When(curve ==t "linear"
+				   low + default * (high - low)
+				 curve ==t "exponential"
+				   low * Math:Pow(high / low default)
+				 curve ==t "integer"
+				   Coerce(Int32 Round(low + default * (high - low)))
+				 Otherwise 
+				   default))
+
+		nums = Algorithm:Expand(Arity(default) (+ #1) #1)
+
+		suffices = Algorithm:Map(n => String:Convert(n) nums)
+
+		Control = Algorithm:Zip-With( 
+					(k d) => Control(String:Append(key k) d type curve low high)
+					suffices 
+					Constraints:Sequence!(default))
+	}
+
+	Audio-Input(bus default) {
+		Audio-Input = External-Stream(bus default #10 "audio")
+
+		nums = Algorithm:Expand(Arity(default) (+ #1) #1)
+		Audio-Input = Algorithm:Zip-With( (b d) => :Extern-Interface:Audio-Input(String:Concat(bus b) d) 
+									nums Constraints:Sequence!(default) )
+
+		ty = Class-Of(default)
+		Audio-Input = Make(ty Recur(bus Break(ty default)))
+	}
+}
  No newline at end of file