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