caxca.c 8.77 KB
Newer Older
Hanspeter Portner's avatar
Hanspeter Portner committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
 *
 * This is free software: you can redistribute it and/or modify
 * it under the terms of the Artistic License 2.0 as published by
 * The Perl Foundation.
 *
 * This source 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
 * Artistic License 2.0 for more details.
 *
 * You should have received a copy of the Artistic License 2.0
 * along the source as a COPYING file. If not, obtain it from
 * http://www.perlfoundation.org/artistic_license_2_0.
 */

Hanspeter Portner's avatar
Hanspeter Portner committed
18
#include <moony.h>
Hanspeter Portner's avatar
Hanspeter Portner committed
19
#include <api_atom.h>
20
#include <api_forge.h>
21
#include <lock_stash.h>
Hanspeter Portner's avatar
Hanspeter Portner committed
22 23 24 25 26 27

#include <lauxlib.h>

typedef struct _Handle Handle;

struct _Handle {
Hanspeter Portner's avatar
Hanspeter Portner committed
28
	moony_t moony;
29
	bool once;
Hanspeter Portner's avatar
Hanspeter Portner committed
30

31
	unsigned max_val;
Hanspeter Portner's avatar
Hanspeter Portner committed
32

33
	uint32_t sample_count;
Hanspeter Portner's avatar
Hanspeter Portner committed
34 35 36 37 38 39 40 41 42
	const LV2_Atom_Sequence *event_in;
	LV2_Atom_Sequence *event_out;
	const float *val_in [4];
	float *val_out [4];

	const LV2_Atom_Sequence *control;
	LV2_Atom_Sequence *notify;
	
	LV2_Atom_Forge forge;
43 44 45 46

	lock_stash_t stash [2];
	bool stashed;
	uint32_t stash_nsamples;
Hanspeter Portner's avatar
Hanspeter Portner committed
47 48
};

49
__non_realtime static LV2_Handle
Hanspeter Portner's avatar
Hanspeter Portner committed
50 51 52 53 54
instantiate(const LV2_Descriptor* descriptor, double rate, const char *bundle_path, const LV2_Feature *const *features)
{
	Handle *handle = (Handle *)calloc(1, sizeof(Handle));
	if(!handle)
		return NULL;
Hanspeter Portner's avatar
Hanspeter Portner committed
55
	mlock(handle, sizeof(Handle));
Hanspeter Portner's avatar
Hanspeter Portner committed
56

57
	if(moony_init(&handle->moony, descriptor->URI, rate, features, 0x80000, false)) // 512KB initial memory
Hanspeter Portner's avatar
Hanspeter Portner committed
58 59 60 61
	{
		free(handle);
		return NULL;
	}
62 63 64
	moony_vm_nrt_enter(handle->moony.vm);
	moony_open(&handle->moony, handle->moony.vm, moony_current(&handle->moony));
	moony_vm_nrt_leave(handle->moony.vm);
Hanspeter Portner's avatar
Hanspeter Portner committed
65
	
Hanspeter Portner's avatar
Hanspeter Portner committed
66
	if(!strcmp(descriptor->URI, MOONY_C1A1XC1A1_URI))
Hanspeter Portner's avatar
Hanspeter Portner committed
67
		handle->max_val = 1;
Hanspeter Portner's avatar
Hanspeter Portner committed
68
	else if(!strcmp(descriptor->URI, MOONY_C2A1XC2A1_URI))
Hanspeter Portner's avatar
Hanspeter Portner committed
69
		handle->max_val = 2;
Hanspeter Portner's avatar
Hanspeter Portner committed
70
	else if(!strcmp(descriptor->URI, MOONY_C4A1XC4A1_URI))
Hanspeter Portner's avatar
Hanspeter Portner committed
71 72
		handle->max_val = 4;
	else
73
		handle->max_val = 1;
74

Hanspeter Portner's avatar
Hanspeter Portner committed
75
	lv2_atom_forge_init(&handle->forge, handle->moony.map);
76 77 78 79 80 81
	
	for(unsigned i=0; i<2; i++)
	{
		_stash_init(&handle->stash[i], handle->moony.map);
		_stash_reset(&handle->stash[i]);
	}
Hanspeter Portner's avatar
Hanspeter Portner committed
82 83 84 85

	return handle;
}

86
__realtime static void
Hanspeter Portner's avatar
Hanspeter Portner committed
87 88 89 90 91 92
connect_port(LV2_Handle instance, uint32_t port, void *data)
{
	Handle *handle = (Handle *)instance;

	if(port == 0)
		handle->event_in = (const LV2_Atom_Sequence *)data;
93
	else if(port == 1)
Hanspeter Portner's avatar
Hanspeter Portner committed
94
		handle->event_out = (LV2_Atom_Sequence *)data;
95 96 97 98 99 100 101 102
	else if( (port - 2) < handle->max_val)
		handle->val_in[port - 2] = (const float *)data;
	else if( (port - 2) < handle->max_val*2)
		handle->val_out[port - 2 - handle->max_val] = (float *)data;
	else if(port == handle->max_val*2 + 2)
		handle->control = (const LV2_Atom_Sequence *)data;
	else if(port == handle->max_val*2 + 3)
		handle->notify = (LV2_Atom_Sequence *)data;
Hanspeter Portner's avatar
Hanspeter Portner committed
103 104
}

105
__realtime static inline void
106
_run_period(lua_State *L, const char *cmd, Handle *handle, uint32_t nsamples,
107
	const LV2_Atom_Sequence *event_in, const LV2_Atom_Sequence *control)
108 109
{
	int top = lua_gettop(L);
110
	if(lua_getglobal(L, cmd) != LUA_TNIL)
111
	{
112
		lua_pushinteger(L, nsamples);
113

114
		// push control / notify pair
115
		{
116
			latom_t *latom = moony_newuserdata(L, &handle->moony, MOONY_UDATA_ATOM, true);
117
			latom->atom = (const LV2_Atom *)control;
118
			latom->body.raw = LV2_ATOM_BODY_CONST(latom->atom);
119

120
			lforge_t *lforge = moony_newuserdata(L, &handle->moony, MOONY_UDATA_FORGE, true);
121 122
			lforge->depth = 0;
			lforge->last.frames = 0;
123
			lforge->forge = &handle->moony.notify_forge;
124
		}
125

126
		// push sequence / forge pair
127
		{
128
			latom_t *latom = moony_newuserdata(L, &handle->moony, MOONY_UDATA_ATOM, true);
129
			latom->atom = (const LV2_Atom *)event_in;
130
			latom->body.raw = LV2_ATOM_BODY_CONST(latom->atom);
131

132
			lforge_t *lforge = moony_newuserdata(L, &handle->moony, MOONY_UDATA_FORGE, true);
133 134
			lforge->depth = 0;
			lforge->last.frames = 0;
135
			lforge->forge = &handle->forge;
136 137
		}

138 139 140 141
		// push values
		for(unsigned i=0; i<handle->max_val; i++)
			lua_pushnumber(L, *handle->val_in[i]);

142
		lua_call(L, 3 + handle->max_val + 2, LUA_MULTRET);
143

144 145 146
		unsigned ret = lua_gettop(L) - top;
		unsigned max = ret > handle->max_val ? handle->max_val : ret; // discard superfluous returns
		for(unsigned i=0; i<max; i++)
147
			*handle->val_out[i] = luaL_optnumber(L, i+1, 0.f);
148
		for(unsigned i=ret; i<handle->max_val; i++) // fill in missing returns with 0.f
149 150
			*handle->val_out[i] = 0.f;
	}
151 152
}

153
__realtime static int
154 155 156 157 158 159 160 161 162 163
_run(lua_State *L)
{
	Handle *handle = lua_touserdata(L, lua_upvalueindex(1));

	// apply stash, if any
	if(handle->stashed)
	{
		const LV2_Atom_Sequence *event_in = &handle->stash[0].seq;
		const LV2_Atom_Sequence *control = &handle->stash[1].seq;

164
		_run_period(L, "run", handle, handle->stash_nsamples, event_in, control);
165 166 167 168 169 170 171

		LV2_ATOM_SEQUENCE_FOREACH(handle->event_out, ev)
			ev->time.frames = 0; // overwrite time stamps
		LV2_ATOM_SEQUENCE_FOREACH(handle->notify, ev)
			ev->time.frames = 0; // overwrite time stamps
	}

172 173 174 175 176 177 178
	if(handle->once)
	{
		_run_period(L, "once", handle, handle->sample_count, handle->event_in, handle->control);
		handle->once = false;
	}

	_run_period(L, "run", handle, handle->sample_count, handle->event_in, handle->control);
179 180 181 182

	return 0;
}

183
__realtime static void
Hanspeter Portner's avatar
Hanspeter Portner committed
184 185 186 187
run(LV2_Handle instance, uint32_t nsamples)
{
	Handle *handle = (Handle *)instance;

188 189
	handle->sample_count = nsamples;

Hanspeter Portner's avatar
Hanspeter Portner committed
190 191 192 193 194 195
	// prepare event_out sequence
	LV2_Atom_Forge_Frame frame;
	uint32_t capacity = handle->event_out->atom.size;
	lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->event_out, capacity);
	lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);

196 197
	moony_pre(&handle->moony, handle->notify);

Hanspeter Portner's avatar
Hanspeter Portner committed
198
	if(_try_lock(&handle->moony.state_lock))
Hanspeter Portner's avatar
Hanspeter Portner committed
199
	{
200 201
		// apply stash, if any
		if(handle->stashed)
202
		{
203
			lock_stash_t *stash = &handle->stash[1];
204
			handle->once = moony_in(&handle->moony, &stash->seq, handle->notify);
205 206 207 208 209 210

			LV2_ATOM_SEQUENCE_FOREACH(handle->notify, ev)
				ev->time.frames = 0; // overwrite time stamps
		}

		// handle UI comm
211 212
		handle->once = moony_in(&handle->moony, handle->control, handle->notify)
			|| handle->once;
213 214 215 216

		// run
		if(!moony_bypass(&handle->moony))
		{
217 218
			lua_State *L = moony_current(&handle->moony);

219 220 221 222 223 224 225 226 227 228 229 230
			if(lua_gettop(L) != 1)
			{
				// cache for reuse
				lua_settop(L, 0);
				lua_pushlightuserdata(L, handle);
				lua_pushcclosure(L, _run, 1);
			}
			lua_pushvalue(L, 1); // _run with upvalue
			if(lua_pcall(L, 0, 0, 0))
				moony_error(&handle->moony);

			moony_freeuserdata(&handle->moony);
231
#ifdef USE_MANUAL_GC
232
			lua_gc(L, LUA_GCSTEP, 0);
233
#endif
234
		}
Hanspeter Portner's avatar
Hanspeter Portner committed
235

236 237 238 239 240 241 242 243
		if(handle->stashed)
		{
			for(unsigned i=0; i<2; i++)
				_stash_reset(&handle->stash[i]);

			handle->stash_nsamples = 0;
			handle->stashed = false;
		}
244

Hanspeter Portner's avatar
Hanspeter Portner committed
245
		_unlock(&handle->moony.state_lock);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
	}
	else
	{
		// stash incoming events to later apply

		lock_stash_t *stash = &handle->stash[0];
		LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
		{
			if(stash->ref)
				stash->ref = lv2_atom_forge_frame_time(&stash->forge, handle->stash_nsamples + ev->time.frames);
			if(stash->ref)
				stash->ref = lv2_atom_forge_write(&stash->forge, &ev->body, lv2_atom_total_size(&ev->body));
		}

		stash = &handle->stash[1];
		LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
		{
			if(stash->ref)
				stash->ref = lv2_atom_forge_frame_time(&stash->forge, handle->stash_nsamples + ev->time.frames);
			if(stash->ref)
				stash->ref = lv2_atom_forge_write(&stash->forge, &ev->body, lv2_atom_total_size(&ev->body));
		}

		handle->stash_nsamples += nsamples;
		handle->stashed = true;
	}
Hanspeter Portner's avatar
Hanspeter Portner committed
272

273 274 275
	if(&frame != handle->forge.stack) // intercept assert
		moony_err(&handle->moony, "forge frame mismatch");

276 277
	// clear output sequence buffer upon error
	if(moony_bypass(&handle->moony))
278 279 280
		lv2_atom_sequence_clear(handle->event_out);
	else
		lv2_atom_forge_pop(&handle->forge, &frame);
Hanspeter Portner's avatar
Hanspeter Portner committed
281

282 283 284 285 286
	// clear output ports upon error
	if(moony_bypass(&handle->moony))
		for(unsigned i=0; i<handle->max_val; i++)
			*handle->val_out[i] = 0.f;

Hanspeter Portner's avatar
Hanspeter Portner committed
287
	// handle UI comm
Hanspeter Portner's avatar
Hanspeter Portner committed
288
	moony_out(&handle->moony, handle->notify, nsamples - 1);
Hanspeter Portner's avatar
Hanspeter Portner committed
289 290
}

291
__non_realtime static void
Hanspeter Portner's avatar
Hanspeter Portner committed
292 293 294 295
cleanup(LV2_Handle instance)
{
	Handle *handle = (Handle *)instance;

Hanspeter Portner's avatar
Hanspeter Portner committed
296
	moony_deinit(&handle->moony);
Hanspeter Portner's avatar
Hanspeter Portner committed
297
	munlock(handle, sizeof(Handle));
Hanspeter Portner's avatar
Hanspeter Portner committed
298 299 300 301
	free(handle);
}

const LV2_Descriptor c1a1xc1a1 = {
Hanspeter Portner's avatar
Hanspeter Portner committed
302
	.URI						= MOONY_C1A1XC1A1_URI,
Hanspeter Portner's avatar
Hanspeter Portner committed
303 304
	.instantiate		= instantiate,
	.connect_port		= connect_port,
305
	.activate				= NULL,
Hanspeter Portner's avatar
Hanspeter Portner committed
306
	.run						= run,
307
	.deactivate			= NULL,
Hanspeter Portner's avatar
Hanspeter Portner committed
308 309 310 311 312
	.cleanup				= cleanup,
	.extension_data	= extension_data
};

const LV2_Descriptor c2a1xc2a1 = {
Hanspeter Portner's avatar
Hanspeter Portner committed
313
	.URI						= MOONY_C2A1XC2A1_URI,
Hanspeter Portner's avatar
Hanspeter Portner committed
314 315
	.instantiate		= instantiate,
	.connect_port		= connect_port,
316
	.activate				= NULL,
Hanspeter Portner's avatar
Hanspeter Portner committed
317
	.run						= run,
318
	.deactivate			= NULL,
Hanspeter Portner's avatar
Hanspeter Portner committed
319 320 321 322 323
	.cleanup				= cleanup,
	.extension_data	= extension_data
};

const LV2_Descriptor c4a1xc4a1 = {
Hanspeter Portner's avatar
Hanspeter Portner committed
324
	.URI						= MOONY_C4A1XC4A1_URI,
Hanspeter Portner's avatar
Hanspeter Portner committed
325 326
	.instantiate		= instantiate,
	.connect_port		= connect_port,
327
	.activate				= NULL,
Hanspeter Portner's avatar
Hanspeter Portner committed
328
	.run						= run,
329
	.deactivate			= NULL,
Hanspeter Portner's avatar
Hanspeter Portner committed
330 331 332
	.cleanup				= cleanup,
	.extension_data	= extension_data
};