1 /*
2  * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
3  *
4  * This is free software: you can redistribute it and/or modify
5  * it under the terms of the Artistic License 2.0 as published by
6  * The Perl Foundation.
7  *
8  * This source is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License 2.0 for more details.
12  *
13  * You should have received a copy of the Artistic License 2.0
14  * along the source as a COPYING file. If not, obtain it from
15  * http://www.perlfoundation.org/artistic_license_2_0.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <ctype.h>
22 #include <math.h>
23 
24 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
25 #	include <pthread_np.h>
26 typedef cpuset_t cpu_set_t;
27 #endif
28 
29 #include <synthpod_bin.h>
30 
31 #define NANO_SECONDS 1000000000
32 
33 typedef struct _prog_t prog_t;
34 
35 struct _prog_t {
36 	bin_t bin;
37 
38 	LV2_Atom_Forge forge;
39 
40 	save_state_t save_state;
41 	atomic_int kill;
42 	pthread_t thread;
43 
44 	uint32_t srate;
45 	uint32_t frsize;
46 	uint32_t nfrags;
47 	uint32_t seq_size;
48 
49 	LV2_OSC_Schedule osc_sched;
50 	struct timespec cur_ntp;
51 	struct timespec nxt_ntp;
52 	struct {
53 		uint64_t cur_frames;
54 		uint64_t ref_frames;
55 		double dT;
56 		double dTm1;
57 	} cycle;
58 };
59 
60 static inline void
_ntp_now(cross_clock_t * clk,struct timespec * ntp)61 _ntp_now(cross_clock_t *clk, struct timespec *ntp)
62 {
63 	cross_clock_gettime(clk, ntp);
64 	ntp->tv_sec += JAN_1970; // convert NTP to OSC time
65 }
66 
67 static inline void
_ntp_clone(struct timespec * dst,struct timespec * src)68 _ntp_clone(struct timespec *dst, struct timespec *src)
69 {
70 	dst->tv_sec = src->tv_sec;
71 	dst->tv_nsec = src->tv_nsec;
72 }
73 
74 static inline void
_ntp_add_nanos(struct timespec * ntp,uint64_t nanos)75 _ntp_add_nanos(struct timespec *ntp, uint64_t nanos)
76 {
77 	ntp->tv_nsec += nanos;
78 	while(ntp->tv_nsec >= NANO_SECONDS) // has overflowed
79 	{
80 		ntp->tv_sec += 1;
81 		ntp->tv_nsec -= NANO_SECONDS;
82 	}
83 }
84 
85 static inline double
_ntp_diff(struct timespec * from,struct timespec * to)86 _ntp_diff(struct timespec *from, struct timespec *to)
87 {
88 	double diff = to->tv_sec;
89 	diff -= from->tv_sec;
90 	diff += 1e-9 * to->tv_nsec;
91 	diff -= 1e-9 * from->tv_nsec;
92 
93 	return diff;
94 }
95 
96 __realtime static inline void
_process(prog_t * handle)97 _process(prog_t *handle)
98 {
99 	bin_t *bin = &handle->bin;
100 	sp_app_t *app = bin->app;
101 
102 	const uint32_t nsamples = handle->frsize;
103 
104 	const uint64_t nanos_per_period = (uint64_t)nsamples * NANO_SECONDS / handle->srate;
105 	handle->cycle.cur_frames = 0; // initialize frame counter
106 	_ntp_now(&bin->clk_real, &handle->nxt_ntp);
107 
108 	const unsigned n_period = handle->nfrags;
109 
110 	struct timespec sleep_to;
111 	cross_clock_gettime(&bin->clk_mono, &sleep_to);
112 
113 	while(!atomic_load_explicit(&handle->kill, memory_order_relaxed))
114 	{
115 		cross_clock_nanosleep(&bin->clk_mono, true, &sleep_to);
116 		_ntp_add_nanos(&sleep_to, nanos_per_period * n_period);
117 
118 		uint32_t na = nsamples * n_period;
119 
120 		// current time is next time from last cycle
121 		_ntp_clone(&handle->cur_ntp, &handle->nxt_ntp);
122 
123 		// extrapolate new nxt_ntp
124 		struct timespec nxt_ntp;
125 		_ntp_now(&bin->clk_real, &nxt_ntp);
126 		_ntp_clone(&handle->nxt_ntp, &nxt_ntp);
127 
128 		// reset ref_frames
129 		handle->cycle.ref_frames = handle->cycle.cur_frames;
130 
131 		// calculate apparent period
132 		_ntp_add_nanos(&nxt_ntp, nanos_per_period);
133 		double diff = _ntp_diff(&handle->cur_ntp, &nxt_ntp);
134 
135 		// calculate apparent samples per period
136 		handle->cycle.dT = nsamples / diff;
137 		handle->cycle.dTm1 = 1.0 / handle->cycle.dT;
138 
139 		for( ; na >= nsamples;
140 				na -= nsamples,
141 				handle->cycle.ref_frames += nsamples,
142 				_ntp_add_nanos(&handle->nxt_ntp, nanos_per_period) )
143 		{
144 			const sp_app_system_source_t *sources = sp_app_get_system_sources(app);
145 
146 			if(sp_app_bypassed(app))
147 			{
148 				//const sp_app_system_sink_t *sinks = sp_app_get_system_sinks(app);
149 
150 				//fprintf(stderr, "app is bypassed\n");
151 
152 				bin_process_pre(bin, nsamples, true);
153 				bin_process_post(bin);
154 
155 				continue;
156 			}
157 
158 			// fill input buffers
159 			for(const sp_app_system_source_t *source=sources;
160 				source->type != SYSTEM_PORT_NONE;
161 				source++)
162 			{
163 				switch(source->type)
164 				{
165 					case SYSTEM_PORT_NONE:
166 					case SYSTEM_PORT_AUDIO:
167 					case SYSTEM_PORT_CONTROL:
168 					case SYSTEM_PORT_CV:
169 					case SYSTEM_PORT_OSC:
170 					case SYSTEM_PORT_MIDI:
171 						break;
172 
173 					case SYSTEM_PORT_COM:
174 					{
175 						void *seq_in = source->buf;
176 
177 						LV2_Atom_Forge *forge = &handle->forge;
178 						LV2_Atom_Forge_Frame frame;
179 						lv2_atom_forge_set_buffer(forge, seq_in, SEQ_SIZE);
180 						LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
181 
182 						const LV2_Atom_Object *obj;
183 						size_t size;
184 						while((obj = varchunk_read_request(bin->app_from_com, &size)))
185 						{
186 							if(ref)
187 								ref = lv2_atom_forge_frame_time(forge, 0);
188 							if(ref)
189 								ref = lv2_atom_forge_raw(forge, obj, size);
190 							if(ref)
191 								lv2_atom_forge_pad(forge, size);
192 
193 							varchunk_read_advance(bin->app_from_com);
194 						}
195 						if(ref)
196 							lv2_atom_forge_pop(forge, &frame);
197 						else
198 							lv2_atom_sequence_clear(seq_in);
199 
200 						break;
201 					}
202 				}
203 			}
204 
205 			bin_process_pre(bin, nsamples, false);
206 
207 			const sp_app_system_sink_t *sinks = sp_app_get_system_sinks(app);
208 
209 			// fill output buffers
210 			for(const sp_app_system_sink_t *sink=sinks;
211 				sink->type != SYSTEM_PORT_NONE;
212 				sink++)
213 			{
214 				switch(sink->type)
215 				{
216 					case SYSTEM_PORT_NONE:
217 					case SYSTEM_PORT_CONTROL:
218 					case SYSTEM_PORT_CV:
219 					case SYSTEM_PORT_OSC:
220 					case SYSTEM_PORT_AUDIO:
221 					case SYSTEM_PORT_MIDI:
222 						break;
223 					case SYSTEM_PORT_COM:
224 					{
225 						const LV2_Atom_Sequence *seq_out = sink->buf;
226 
227 						LV2_ATOM_SEQUENCE_FOREACH(seq_out, ev)
228 						{
229 							const LV2_Atom *atom = &ev->body;
230 
231 							// try do process events directly
232 							bin->advance_ui = sp_app_from_ui(bin->app, atom);
233 							if(!bin->advance_ui) // queue event in ringbuffer instead
234 							{
235 								//fprintf(stderr, "plugin ui direct is blocked\n");
236 
237 								void *ptr;
238 								size_t size = lv2_atom_total_size(atom);
239 								if((ptr = varchunk_write_request(bin->app_from_app, size)))
240 								{
241 									memcpy(ptr, atom, size);
242 									varchunk_write_advance(bin->app_from_app, size);
243 								}
244 								else
245 								{
246 									bin_log_trace(bin, "%s: app_from_app ringbuffer full\n", __func__);
247 									//FIXME
248 								}
249 							}
250 						}
251 						break;
252 					}
253 				}
254 			}
255 
256 			bin_process_post(bin);
257 		}
258 
259 		// increase cur_frames
260 		handle->cycle.cur_frames = handle->cycle.ref_frames;
261 		//sched_yield();
262 	}
263 }
264 
265 __non_realtime static void *
_rt_thread(void * data)266 _rt_thread(void *data)
267 {
268 	prog_t *handle = data;
269 	bin_t *bin = &handle->bin;
270 
271 	bin->dsp_thread = pthread_self();
272 
273 	if(handle->bin.audio_prio)
274 	{
275 		struct sched_param schedp;
276 		memset(&schedp, 0, sizeof(struct sched_param));
277 		schedp.sched_priority = handle->bin.audio_prio;
278 
279 		if(schedp.sched_priority)
280 		{
281 			if(pthread_setschedparam(bin->dsp_thread, SCHED_FIFO, &schedp))
282 				bin_log_error(bin, "%s: pthread_setschedparam error\n", __func__);
283 		}
284 	}
285 
286 	if(handle->bin.cpu_affinity)
287 	{
288 		cpu_set_t cpuset;
289 		CPU_ZERO(&cpuset);
290 		CPU_SET(0, &cpuset);
291 		if(pthread_setaffinity_np(bin->dsp_thread, sizeof(cpu_set_t), &cpuset))
292 			bin_log_error(bin, "%s: pthread_setaffinity_np error\n", __func__);
293 	}
294 
295 	_process(handle);
296 
297 	return NULL;
298 }
299 
300 __non_realtime static void *
_system_port_add(void * data,system_port_t type,const char * short_name,const char * pretty_name,const char * designation,bool input,uint32_t order)301 _system_port_add(void *data, system_port_t type, const char *short_name,
302 	const char *pretty_name, const char *designation, bool input, uint32_t order)
303 {
304 	bin_t *bin = data;
305 	prog_t *handle = (void *)bin - offsetof(prog_t, bin);
306 	(void)handle;
307 
308 	switch(type)
309 	{
310 		case SYSTEM_PORT_NONE:
311 		case SYSTEM_PORT_CONTROL:
312 		case SYSTEM_PORT_AUDIO:
313 		case SYSTEM_PORT_CV:
314 		case SYSTEM_PORT_MIDI:
315 		case SYSTEM_PORT_OSC:
316 		case SYSTEM_PORT_COM:
317 			// unsupported, skip
318 			break;
319 	}
320 
321 	return NULL;
322 }
323 
324 __non_realtime static void
_system_port_del(void * data,void * sys_port)325 _system_port_del(void *data, void *sys_port)
326 {
327 	bin_t *bin = data;
328 	prog_t *handle = (void *)bin - offsetof(prog_t, bin);
329 	(void)handle;
330 
331 	// unsupported, skip
332 }
333 
334 __non_realtime static void
_saved(bin_t * bin,int status)335 _saved(bin_t *bin, int status)
336 {
337 	prog_t *handle = (void *)bin - offsetof(prog_t, bin);
338 
339 	if(handle->save_state == SAVE_STATE_NSM)
340 	{
341 		nsmc_saved(bin->nsm, status);
342 	}
343 	handle->save_state = SAVE_STATE_INTERNAL;
344 
345 	if(atomic_load_explicit(&handle->kill, memory_order_relaxed))
346 	{
347 		bin_quit(bin);
348 	}
349 }
350 
351 __non_realtime static int
_open(const char * path,const char * name,const char * id,void * data)352 _open(const char *path, const char *name, const char *id, void *data)
353 {
354 	bin_t *bin = data;
355 	prog_t *handle = (void *)bin - offsetof(prog_t, bin);
356 	(void)name;
357 
358 	if(bin->path)
359 		free(bin->path);
360 	bin->path = strdup(path);
361 
362 	// synthpod init
363 	bin->app_driver.sample_rate = handle->srate;
364 	bin->app_driver.update_rate = handle->bin.update_rate;
365 	bin->app_driver.max_block_size = handle->frsize;
366 	bin->app_driver.min_block_size = 1;
367 	bin->app_driver.seq_size = handle->seq_size;
368 	bin->app_driver.num_periods = handle->nfrags;
369 
370 	// app init
371 	bin->app = sp_app_new(NULL, &bin->app_driver, bin);
372 
373 	// alsa activate
374 	atomic_init(&handle->kill, 0);
375 	if(pthread_create(&handle->thread, NULL, _rt_thread, handle))
376 		bin_log_error(bin, "%s: creation of realtime thread failed\n", __func__);
377 
378 	bin_bundle_load(bin, bin->path);
379 	nsmc_opened(bin->nsm, 0);
380 
381 	return 0; // success
382 }
383 
384 __non_realtime static int
_save(void * data)385 _save(void *data)
386 {
387 	bin_t *bin = data;
388 	prog_t *handle = (void *)bin - offsetof(prog_t, bin);
389 
390 	handle->save_state = SAVE_STATE_NSM;
391 	bin_bundle_save(bin, bin->path);
392 	_saved(bin, 0);
393 
394 	return 0; // success
395 }
396 
397 __non_realtime static int
_show(void * data)398 _show(void *data)
399 {
400 	bin_t *bin = data;
401 
402 	return bin_show(bin);
403 }
404 
405 __non_realtime static int
_hide(void * data)406 _hide(void *data)
407 {
408 	bin_t *bin = data;
409 
410 	return bin_hide(bin);
411 }
412 
413 static const nsmc_driver_t nsm_driver = {
414 	.open = _open,
415 	.save = _save,
416 	.show = _show,
417 	.hide = _hide
418 };
419 
420 // rt
421 __realtime static double
_osc_schedule_osc2frames(LV2_OSC_Schedule_Handle instance,uint64_t timestamp)422 _osc_schedule_osc2frames(LV2_OSC_Schedule_Handle instance, uint64_t timestamp)
423 {
424 	prog_t *handle = instance;
425 
426 	if(timestamp == 1ULL)
427 		return 0; // inject at start of period
428 
429 	const uint64_t time_sec = timestamp >> 32;
430 	const uint64_t time_frac = timestamp & 0xffffffff;
431 
432 	const double diff = (time_sec - handle->cur_ntp.tv_sec)
433 		+ time_frac * 0x1p-32
434 		- handle->cur_ntp.tv_nsec * 1e-9;
435 
436 	const double frames = diff * handle->cycle.dT
437 		- handle->cycle.ref_frames
438 		+ handle->cycle.cur_frames;
439 
440 	return frames;
441 }
442 
443 // rt
444 __realtime static uint64_t
_osc_schedule_frames2osc(LV2_OSC_Schedule_Handle instance,double frames)445 _osc_schedule_frames2osc(LV2_OSC_Schedule_Handle instance, double frames)
446 {
447 	prog_t *handle = instance;
448 
449 	double diff = (frames - handle->cycle.cur_frames + handle->cycle.ref_frames)
450 		* handle->cycle.dTm1;
451 	diff += handle->cur_ntp.tv_nsec * 1e-9;
452 	diff += handle->cur_ntp.tv_sec;
453 
454 	double time_sec_d;
455 	double time_frac_d = modf(diff, &time_sec_d);
456 
457 	uint64_t time_sec = time_sec_d;
458 	uint64_t time_frac = time_frac_d * 0x1p32;
459 	if(time_frac >= 0x100000000ULL) // illegal overflow
460 		time_frac = 0xffffffffULL;
461 
462 	uint64_t timestamp = (time_sec << 32) | time_frac;
463 
464 	return timestamp;
465 }
466 
467 int
main(int argc,char ** argv)468 main(int argc, char **argv)
469 {
470 	mlockall(MCL_CURRENT | MCL_FUTURE);
471 
472 	static prog_t handle;
473 	bin_t *bin = &handle.bin;
474 
475 	handle.srate = 48000;
476 	handle.frsize = 1024;
477 	handle.nfrags = 3; //TODO make this configurable
478 	handle.seq_size = SEQ_SIZE;
479 
480 	bin->audio_prio = 70;
481 	bin->worker_prio = 60;
482 	bin->num_slaves = sysconf(_SC_NPROCESSORS_ONLN) - 1;
483 	bin->bad_plugins = false;
484 	bin->has_gui = false;
485 	bin->kill_gui = false;
486 	snprintf(bin->socket_path, sizeof(bin->socket_path), "shm:///synthpod-%i", getpid());
487 	bin->update_rate = 25;
488 	bin->cpu_affinity = false;
489 
490 	fprintf(stderr,
491 		"Synthpod "SYNTHPOD_VERSION"\n"
492 		"Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)\n"
493 		"Released under Artistic License 2.0 by Open Music Kontrollers\n");
494 
495 	int c;
496 	while((c = getopt(argc, argv, "vhgGkKbBaAy:Yw:Wl:r:p:s:c:f:")) != -1)
497 	{
498 		switch(c)
499 		{
500 			case 'v':
501 				fprintf(stderr,
502 					"--------------------------------------------------------------------\n"
503 					"This is free software: you can redistribute it and/or modify\n"
504 					"it under the terms of the Artistic License 2.0 as published by\n"
505 					"The Perl Foundation.\n"
506 					"\n"
507 					"This source is distributed in the hope that it will be useful,\n"
508 					"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
509 					"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
510 					"Artistic License 2.0 for more details.\n"
511 					"\n"
512 					"You should have received a copy of the Artistic License 2.0\n"
513 					"along the source as a COPYING file. If not, obtain it from\n"
514 					"http://www.perlfoundation.org/artistic_license_2_0.\n\n");
515 				return 0;
516 			case 'h':
517 				fprintf(stderr,
518 					"--------------------------------------------------------------------\n"
519 					"USAGE\n"
520 					"   %s [OPTIONS] [BUNDLE_PATH]\n"
521 					"\n"
522 					"OPTIONS\n"
523 					"   [-v]                 print version and full license information\n"
524 					"   [-h]                 print usage information\n"
525 					"   [-g]                 load GUI\n"
526 					"   [-G]                 do NOT load GUI (default)\n"
527 					"   [-k]                 kill DSP with GUI\n"
528 					"   [-K]                 do NOT kill DSP with GUI (default)\n"
529 					"   [-b]                 enable bad plugins\n"
530 					"   [-B]                 disable bad plugins (default)\n"
531 					"   [-a]                 enable CPU affinity\n"
532 					"   [-A]                 disable CPU affinity (default)\n"
533 					"   [-y] audio-priority  audio thread realtime priority (70)\n"
534 					"   [-Y]                 do NOT use audio thread realtime priority\n"
535 					"   [-w] worker-priority worker thread realtime priority (60)\n"
536 					"   [-W]                 do NOT use worker thread realtime priority\n"
537 					"   [-l] link-path       socket link path (shm:///synthpod)\n"
538 					"   [-r] sample-rate     sample rate (48000)\n"
539 					"   [-p] sample-period   frames per period (1024)\n"
540 					"   [-s] sequence-size   minimum sequence size (8192)\n"
541 					"   [-c] slave-cores     number of slave cores (auto)\n"
542 					"   [-f] update-rate     GUI update rate (25)\n\n"
543 					, argv[0]);
544 				return 0;
545 			case 'g':
546 				bin->has_gui = true;
547 				break;
548 			case 'G':
549 				bin->has_gui = false;
550 				break;
551 			case 'k':
552 				bin->kill_gui = true;
553 				break;
554 			case 'K':
555 				bin->kill_gui = false;
556 				break;
557 			case 'b':
558 				bin->bad_plugins = true;
559 				break;
560 			case 'B':
561 				bin->bad_plugins = false;
562 				break;
563 			case 'a':
564 				bin->cpu_affinity = true;
565 				break;
566 			case 'A':
567 				bin->cpu_affinity = false;
568 				break;
569 			case 'y':
570 				bin->audio_prio = atoi(optarg);
571 				break;
572 			case 'Y':
573 				bin->audio_prio = 0;
574 				break;
575 			case 'w':
576 				bin->worker_prio = atoi(optarg);
577 				break;
578 			case 'W':
579 				bin->worker_prio = 0;
580 				break;
581 			case 'l':
582 				snprintf(bin->socket_path, sizeof(bin->socket_path), "%s", optarg);
583 				break;
584 			case 'r':
585 				handle.srate = atoi(optarg);
586 				break;
587 			case 'p':
588 				handle.frsize = atoi(optarg);
589 				break;
590 			case 's':
591 				handle.seq_size = MAX(SEQ_SIZE, atoi(optarg));
592 				break;
593 			case 'c':
594 				if(atoi(optarg) < bin->num_slaves)
595 					bin->num_slaves = atoi(optarg);
596 				break;
597 			case 'f':
598 				bin->update_rate = atoi(optarg);
599 				break;
600 			case '?':
601 				if(  (optopt == 'r') || (optopt == 'p') || (optopt == 's') || (optopt == 'c')
602 					|| (optopt == 'l') || (optopt == 'f') )
603 					fprintf(stderr, "Option `-%c' requires an argument.\n", optopt);
604 				else if(isprint(optopt))
605 					fprintf(stderr, "Unknown option `-%c'.\n", optopt);
606 				else
607 					fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
608 				return -1;
609 			default:
610 				return -1;
611 		}
612 	}
613 
614 	bin_init(bin, handle.srate);
615 
616 	LV2_URID_Map *map = bin->map;
617 
618 	lv2_atom_forge_init(&handle.forge, map);
619 
620 	bin->app_driver.system_port_add = _system_port_add;
621 	bin->app_driver.system_port_del = _system_port_del;
622 
623 	handle.osc_sched.osc2frames = _osc_schedule_osc2frames;
624 	handle.osc_sched.frames2osc = _osc_schedule_frames2osc;
625 	handle.osc_sched.handle = &handle;
626 	bin->app_driver.osc_sched = &handle.osc_sched;
627 	bin->app_driver.features = SP_APP_FEATURE_FIXED_BLOCK_LENGTH; // always true for DUMMY
628   if(handle.frsize && !(handle.frsize & (handle.frsize - 1))) // check for powerOf2
629 		bin->app_driver.features |= SP_APP_FEATURE_POWER_OF_2_BLOCK_LENGTH;
630 
631 	// run
632 	bin_run(bin, argv, &nsm_driver, NULL, NULL);
633 
634 	// stop
635 	bin_stop(bin);
636 
637 	// stop rt thread
638 	if(handle.thread)
639 	{
640 		atomic_store_explicit(&handle.kill, 1, memory_order_relaxed);
641 		pthread_join(handle.thread, NULL);
642 	}
643 
644 	// deinit
645 	bin_deinit(bin);
646 
647 	munlockall();
648 
649 	return 0;
650 }
651