1 /*
2  * Copyright (c) 2015 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 <math.h>
21 
22 #include <synthpod_lv2.h>
23 
24 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
25 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
26 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
27 #include <lv2/lv2plug.in/ns/ext/midi/midi.h>
28 #include <lv2/lv2plug.in/ns/ext/time/time.h>
29 
30 #include <props.h>
31 
32 #define MAX_NPROPS 1
33 
34 typedef struct _plugstate_t plugstate_t;
35 typedef struct _plughandle_t plughandle_t;
36 
37 struct _plugstate_t {
38 	int32_t alarm;
39 };
40 
41 struct _plughandle_t {
42 	LV2_URID_Map *map;
43 	LV2_Atom_Forge forge;
44 	LV2_Atom_Forge_Ref ref;
45 
46 	LV2_URID midi_MidiEvent;
47 	LV2_URID time_Position;
48 	LV2_URID time_speed;
49 
50 	PROPS_T(props, MAX_NPROPS);
51 
52 	const LV2_Atom_Sequence *event_in;
53 	LV2_Atom_Sequence *event_out;
54 
55 	plugstate_t state;
56 	plugstate_t stash;
57 
58 	struct {
59 		LV2_URID alarm;
60 	} urid;
61 
62 	bool rolling;
63 };
64 
65 static inline void
_controller(plughandle_t * handle,LV2_Atom_Forge * forge,int64_t frames,const uint8_t * m,uint32_t sz)66 _controller(plughandle_t *handle, LV2_Atom_Forge *forge, int64_t frames,
67 	const uint8_t *m, uint32_t sz)
68 {
69 	if(handle->ref)
70 		handle->ref = lv2_atom_forge_frame_time(forge, frames);
71 	if(handle->ref)
72 		handle->ref = lv2_atom_forge_atom(forge, sz, handle->midi_MidiEvent);
73 	if(handle->ref)
74 		handle->ref = lv2_atom_forge_write(forge, m, sz);
75 }
76 
77 static inline void
_trigger(plughandle_t * handle,LV2_Atom_Forge * forge,int64_t frames)78 _trigger(plughandle_t *handle, LV2_Atom_Forge *forge, int64_t frames)
79 {
80 	uint8_t m [3];
81 
82 	for(uint8_t i=0x0; i<0x10; i++)
83 	{
84 		// raise sustain pedal
85 		m[0] = LV2_MIDI_MSG_CONTROLLER | i;
86 		m[1] = LV2_MIDI_CTL_SUSTAIN;
87 		m[2] = 0x0;
88 
89 		_controller(handle, forge, frames, m, sizeof(m));
90 
91 		// all notes off
92 		m[0] = LV2_MIDI_MSG_CONTROLLER | i;
93 		m[1] = LV2_MIDI_CTL_ALL_NOTES_OFF;
94 		m[2] = 0x0;
95 
96 		_controller(handle, forge, frames, m, sizeof(m));
97 
98 		// all sounds off
99 		m[0] = LV2_MIDI_MSG_CONTROLLER | i;
100 		m[1] = LV2_MIDI_CTL_ALL_SOUNDS_OFF;
101 		m[2] = 0x0;
102 
103 		_controller(handle, forge, frames, m, sizeof(m));
104 	}
105 }
106 
107 static void
_intercept_alarm(void * data,int64_t frames,props_impl_t * impl)108 _intercept_alarm(void *data, int64_t frames, props_impl_t *impl)
109 {
110 	plughandle_t *handle = data;
111 
112 	if(handle->state.alarm)
113 	{
114 		handle->state.alarm = false;
115 		props_set(&handle->props, &handle->forge, frames, handle->urid.alarm, &handle->ref);
116 
117 		_trigger(handle, &handle->forge, frames);
118 	}
119 }
120 
121 static const props_def_t defs [MAX_NPROPS] = {
122 	{
123 		.property = SYNTHPOD_PANIC_URI"_alarm",
124 		.offset = offsetof(plugstate_t, alarm),
125 		.type = LV2_ATOM__Bool,
126 		.event_cb = _intercept_alarm
127 	}
128 };
129 
130 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)131 instantiate(const LV2_Descriptor* descriptor, double rate,
132 	const char *bundle_path, const LV2_Feature *const *features)
133 {
134 	plughandle_t *handle = calloc(1, sizeof(plughandle_t));
135 	if(!handle)
136 		return NULL;
137 
138 	for(unsigned i=0; features[i]; i++)
139 	{
140 		if(!strcmp(features[i]->URI, LV2_URID__map))
141 			handle->map = features[i]->data;
142 	}
143 
144 	if(!handle->map)
145 	{
146 		fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
147 		free(handle);
148 		return NULL;
149 	}
150 
151 	handle->midi_MidiEvent = handle->map->map(handle->map->handle, LV2_MIDI__MidiEvent);
152 	handle->time_Position = handle->map->map(handle->map->handle, LV2_TIME__Position);
153 	handle->time_speed = handle->map->map(handle->map->handle, LV2_TIME__speed);
154 
155 	lv2_atom_forge_init(&handle->forge, handle->map);
156 
157 	if(!props_init(&handle->props, descriptor->URI,
158 		defs, MAX_NPROPS, &handle->state, &handle->stash,
159 		handle->map, handle))
160 	{
161 		fprintf(stderr, "failed to allocate property structure\n");
162 		free(handle);
163 		return NULL;
164 	}
165 
166 	handle->urid.alarm = props_map(&handle->props, defs[0].property);
167 
168 	handle->rolling = true;
169 
170 	return handle;
171 }
172 
173 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)174 connect_port(LV2_Handle instance, uint32_t port, void *data)
175 {
176 	plughandle_t *handle = instance;
177 
178 	if(port == 0)
179 		handle->event_in = (const LV2_Atom_Sequence *)data;
180 	else if(port == 1)
181 		handle->event_out = (LV2_Atom_Sequence *)data;
182 }
183 
184 __realtime static void
run(LV2_Handle instance,uint32_t nsamples)185 run(LV2_Handle instance, uint32_t nsamples)
186 {
187 	plughandle_t *handle = instance;
188 
189 	const uint32_t capacity = handle->event_out->atom.size;
190 	LV2_Atom_Forge *forge = &handle->forge;
191 	lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->event_out, capacity);
192 	LV2_Atom_Forge_Frame frame;
193 	handle->ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
194 
195 	props_idle(&handle->props, &handle->forge, 0, &handle->ref);
196 
197 	LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
198 	{
199 		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
200 		const LV2_Atom *atom = (const LV2_Atom *)&ev->body;
201 		const int64_t frames = ev->time.frames;
202 
203 		if(lv2_atom_forge_is_object_type(forge, obj->atom.type))
204 		{
205 			if(!props_advance(&handle->props, forge, frames, obj, &handle->ref)
206 				&& (obj->body.otype == handle->time_Position) )
207 			{
208 				const LV2_Atom_Float *speed = NULL;
209 				lv2_atom_object_get(obj, handle->time_speed, &speed, 0);
210 				if(speed && (speed->atom.type == forge->Float) )
211 				{
212 					if(handle->rolling && (speed->body == 0.f) ) // do not retrigger when already stopped
213 						_trigger(handle, forge, frames);
214 					handle->rolling = speed->body != 0.f;
215 				}
216 			}
217 		}
218 	}
219 
220 	if(handle->ref)
221 		lv2_atom_forge_pop(forge, &frame);
222 	else
223 		lv2_atom_sequence_clear(handle->event_out);
224 }
225 
226 static void
cleanup(LV2_Handle instance)227 cleanup(LV2_Handle instance)
228 {
229 	plughandle_t *handle = instance;
230 
231 	if(handle)
232 		free(handle);
233 }
234 
235 static LV2_State_Status
_state_save(LV2_Handle instance,LV2_State_Store_Function store,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)236 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
237 	LV2_State_Handle state, uint32_t flags,
238 	const LV2_Feature *const *features)
239 {
240 	plughandle_t *handle = instance;
241 
242 	return props_save(&handle->props, store, state, flags, features);
243 }
244 
245 static LV2_State_Status
_state_restore(LV2_Handle instance,LV2_State_Retrieve_Function retrieve,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)246 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
247 	LV2_State_Handle state, uint32_t flags,
248 	const LV2_Feature *const *features)
249 {
250 	plughandle_t *handle = instance;
251 
252 	return props_restore(&handle->props, retrieve, state, flags, features);
253 }
254 
255 static const LV2_State_Interface state_iface = {
256 	.save = _state_save,
257 	.restore = _state_restore
258 };
259 
260 static const void *
extension_data(const char * uri)261 extension_data(const char *uri)
262 {
263 	if(!strcmp(uri, LV2_STATE__interface))
264 		return &state_iface;
265 
266 	return NULL;
267 }
268 
269 const LV2_Descriptor synthpod_panic = {
270 	.URI						= SYNTHPOD_PANIC_URI,
271 	.instantiate		= instantiate,
272 	.connect_port		= connect_port,
273 	.activate				= NULL,
274 	.run						= run,
275 	.deactivate			= NULL,
276 	.cleanup				= cleanup,
277 	.extension_data	= extension_data
278 };
279