1 /*
2  * Copyright (c) 2015-2021 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 <api_midi.h>
19 #include <api_atom.h>
20 #include <api_forge.h>
21 
22 __realtime static int
_lmidiresponder__call(lua_State * L)23 _lmidiresponder__call(lua_State *L)
24 {
25 	moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
26 	const bool *through = lua_touserdata(L, 1);
27 
28 	lua_settop(L, 4); // discard superfluous arguments
29 	// 1: self
30 	// 2: frames
31 	// 3: forge
32 	// 4: atom
33 
34 	latom_t *latom = NULL;
35 	if(luaL_testudata(L, 4, "latom"))
36 		latom = lua_touserdata(L, 4);
37 	lua_pop(L, 1); // atom
38 
39 	// check for valid atom and event type
40 	if(  !latom
41 		|| (latom->atom->type != moony->uris.midi_event) )
42 	{
43 		lua_pushboolean(L, 0); // not handled
44 		return 1;
45 	}
46 
47 	const uint8_t *midi = latom->body.raw;
48 	const uint8_t status = midi[0];
49 	const uint8_t command = status & 0xf0;
50 	const bool is_system = command == 0xf0;
51 
52 	// replace self with its uservalue
53 	lua_getuservalue(L, 1);
54 	lua_replace(L, 1);
55 
56 	if(lua_geti(L, 1, is_system ? status : command) != LUA_TNIL)
57 	{
58 		lua_insert(L, 1);
59 		if(is_system)
60 			lua_pushnil(L); // system messages have no channel
61 		else
62 			lua_pushinteger(L, status & 0x0f); // 4: channel
63 
64 		for(unsigned i=1; i<latom->atom->size; i++)
65 			lua_pushinteger(L, midi[i]);
66 
67 		lua_call(L, 4 + latom->atom->size - 1, 0);
68 	}
69 	else if(*through) // through
70 	{
71 		const int64_t frames = luaL_checkinteger(L, 2);
72 		lforge_t *lforge = luaL_checkudata(L, 3, "lforge");
73 
74 		if(frames < lforge->last.frames)
75 			luaL_error(L, "invalid frame time, must not decrease");
76 		lforge->last.frames = frames;
77 
78 		if(  !lv2_atom_forge_frame_time(lforge->forge, frames)
79 			|| !lv2_atom_forge_atom(lforge->forge, latom->atom->size, latom->atom->type)
80 			|| !lv2_atom_forge_write(lforge->forge, latom->body.raw, latom->atom->size) )
81 			luaL_error(L, forge_buffer_overflow);
82 	}
83 
84 	lua_pushboolean(L, 1); // handled
85 	return 1;
86 }
87 
88 __realtime static int
_lmidiresponder__index(lua_State * L)89 _lmidiresponder__index(lua_State *L)
90 {
91 	const bool *through = lua_touserdata(L, 1);
92 	const char *key = luaL_checkstring(L, 2);
93 	// 1: self
94 	// 2: key
95 
96 	if(!strcmp(key, "through"))
97 	{
98 		lua_pushboolean(L, *through);
99 	}
100 	else
101 	{
102 		lua_pushnil(L);
103 	}
104 
105 	return 1;
106 }
107 
108 __realtime static int
_lmidiresponder__newindex(lua_State * L)109 _lmidiresponder__newindex(lua_State *L)
110 {
111 	// 1: self
112 	// 2: key
113 	// 3: val
114 	bool *through = lua_touserdata(L, 1);
115 	const char *key = luaL_checkstring(L, 2);
116 
117 	if(!strcmp(key, "through"))
118 	{
119 		*through = lua_toboolean(L, 3);
120 	}
121 
122 	return 0;
123 }
124 
125 __realtime int
_lmidiresponder(lua_State * L)126 _lmidiresponder(lua_State *L)
127 {
128 	//moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
129 
130 	lua_settop(L, 2); // discard superfluous arguments
131 
132 	const bool _through = lua_toboolean(L, 2);
133 	lua_pop(L, 1); // bool
134 
135 	// o = new
136 	bool *through = lua_newuserdata(L, sizeof(bool));
137 	*through= _through;
138 
139 	// o.uservalue = uservalue
140 	lua_insert(L, 1);
141 	lua_setuservalue(L, -2);
142 
143 	// setmetatable(o, self)
144 	luaL_getmetatable(L, "lmidiresponder");
145 	lua_setmetatable(L, -2);
146 
147 	// return o
148 	return 1;
149 }
150 
151 const luaL_Reg lmidiresponder_mt [] = {
152 	{"__call", _lmidiresponder__call},
153 	{"__index", _lmidiresponder__index},
154 	{"__newindex", _lmidiresponder__newindex},
155 	{NULL, NULL}
156 };
157 
158 const midi_msg_t midi_msgs [] = {
159 	{ LV2_MIDI_MSG_NOTE_OFF							, "NoteOff" },
160 	{ LV2_MIDI_MSG_NOTE_ON							, "NoteOn" },
161 	{ LV2_MIDI_MSG_NOTE_PRESSURE				, "NotePressure" },
162 	{ LV2_MIDI_MSG_CONTROLLER						, "Controller" },
163 	{ LV2_MIDI_MSG_PGM_CHANGE						, "ProgramChange" },
164 	{ LV2_MIDI_MSG_CHANNEL_PRESSURE			, "ChannelPressure" },
165 	{ LV2_MIDI_MSG_BENDER								, "Bender" },
166 	{ LV2_MIDI_MSG_SYSTEM_EXCLUSIVE			, "SystemExclusive" },
167 	{ LV2_MIDI_MSG_MTC_QUARTER					, "QuarterFrame" },
168 	{ LV2_MIDI_MSG_SONG_POS							, "SongPosition" },
169 	{ LV2_MIDI_MSG_SONG_SELECT					, "SongSelect" },
170 	{ LV2_MIDI_MSG_TUNE_REQUEST					, "TuneRequest" },
171 	{ LV2_MIDI_MSG_CLOCK								, "Clock" },
172 	{ LV2_MIDI_MSG_START								, "Start" },
173 	{ LV2_MIDI_MSG_CONTINUE							, "Continue" },
174 	{ LV2_MIDI_MSG_STOP									, "Stop" },
175 	{ LV2_MIDI_MSG_ACTIVE_SENSE					, "ActiveSense" },
176 	{ LV2_MIDI_MSG_RESET								, "Reset" },
177 	{ 0xf7															, "EndOfExclusive" },
178 
179 	{ 0, NULL} // sentinel
180 };
181 
182 const midi_msg_t controllers [] = {
183 	{ LV2_MIDI_CTL_MSB_BANK             , "BankSelection_MSB" },
184 	{ LV2_MIDI_CTL_MSB_MODWHEEL         , "Modulation_MSB" },
185 	{ LV2_MIDI_CTL_MSB_BREATH           , "Breath_MSB" },
186 	{ LV2_MIDI_CTL_MSB_FOOT             , "Foot_MSB" },
187 	{ LV2_MIDI_CTL_MSB_PORTAMENTO_TIME  , "PortamentoTime_MSB" },
188 	{ LV2_MIDI_CTL_MSB_DATA_ENTRY       , "DataEntry_MSB" },
189 	{ LV2_MIDI_CTL_MSB_MAIN_VOLUME      , "MainVolume_MSB" },
190 	{ LV2_MIDI_CTL_MSB_BALANCE          , "Balance_MSB" },
191 	{ LV2_MIDI_CTL_MSB_PAN              , "Panpot_MSB" },
192 	{ LV2_MIDI_CTL_MSB_EXPRESSION       , "Expression_MSB" },
193 	{ LV2_MIDI_CTL_MSB_EFFECT1          , "Effect1_MSB" },
194 	{ LV2_MIDI_CTL_MSB_EFFECT2          , "Effect2_MSB" },
195 	{ LV2_MIDI_CTL_MSB_GENERAL_PURPOSE1 , "GeneralPurpose1_MSB" },
196 	{ LV2_MIDI_CTL_MSB_GENERAL_PURPOSE2 , "GeneralPurpose2_MSB" },
197 	{ LV2_MIDI_CTL_MSB_GENERAL_PURPOSE3 , "GeneralPurpose3_MSB" },
198 	{ LV2_MIDI_CTL_MSB_GENERAL_PURPOSE4 , "GeneralPurpose4_MSB" },
199 
200 	{ LV2_MIDI_CTL_LSB_BANK             , "BankSelection_LSB" },
201 	{ LV2_MIDI_CTL_LSB_MODWHEEL         , "Modulation_LSB" },
202 	{ LV2_MIDI_CTL_LSB_BREATH           , "Breath_LSB" },
203 	{ LV2_MIDI_CTL_LSB_FOOT             , "Foot_LSB" },
204 	{ LV2_MIDI_CTL_LSB_PORTAMENTO_TIME  , "PortamentoTime_LSB" },
205 	{ LV2_MIDI_CTL_LSB_DATA_ENTRY       , "DataEntry_LSB" },
206 	{ LV2_MIDI_CTL_LSB_MAIN_VOLUME      , "MainVolume_LSB" },
207 	{ LV2_MIDI_CTL_LSB_BALANCE          , "Balance_LSB" },
208 	{ LV2_MIDI_CTL_LSB_PAN              , "Panpot_LSB" },
209 	{ LV2_MIDI_CTL_LSB_EXPRESSION       , "Expression_LSB" },
210 	{ LV2_MIDI_CTL_LSB_EFFECT1          , "Effect1_LSB" },
211 	{ LV2_MIDI_CTL_LSB_EFFECT2          , "Effect2_LSB" },
212 	{ LV2_MIDI_CTL_LSB_GENERAL_PURPOSE1 , "GeneralPurpose1_LSB" },
213 	{ LV2_MIDI_CTL_LSB_GENERAL_PURPOSE2 , "GeneralPurpose2_LSB" },
214 	{ LV2_MIDI_CTL_LSB_GENERAL_PURPOSE3 , "GeneralPurpose3_LSB" },
215 	{ LV2_MIDI_CTL_LSB_GENERAL_PURPOSE4 , "GeneralPurpose4_LSB" },
216 
217 	{ LV2_MIDI_CTL_SUSTAIN              , "SustainPedal" },
218 	{ LV2_MIDI_CTL_PORTAMENTO           , "Portamento" },
219 	{ LV2_MIDI_CTL_SOSTENUTO            , "Sostenuto" },
220 	{ LV2_MIDI_CTL_SOFT_PEDAL           , "SoftPedal" },
221 	{ LV2_MIDI_CTL_LEGATO_FOOTSWITCH    , "LegatoFootSwitch" },
222 	{ LV2_MIDI_CTL_HOLD2                , "Hold2" },
223 
224 	{ LV2_MIDI_CTL_SC1_SOUND_VARIATION  , "SoundVariation" },
225 	{ LV2_MIDI_CTL_SC3_RELEASE_TIME     , "ReleaseTime" },
226 	{ LV2_MIDI_CTL_SC2_TIMBRE           , "Timbre" },
227 	{ LV2_MIDI_CTL_SC4_ATTACK_TIME      , "AttackTime" },
228 	{ LV2_MIDI_CTL_SC5_BRIGHTNESS       , "Brightness" },
229 	{ LV2_MIDI_CTL_SC1_SOUND_VARIATION  , "SC1" },
230 	{ LV2_MIDI_CTL_SC2_TIMBRE           , "SC2" },
231 	{ LV2_MIDI_CTL_SC3_RELEASE_TIME     , "SC3" },
232 	{ LV2_MIDI_CTL_SC4_ATTACK_TIME      , "SC4" },
233 	{ LV2_MIDI_CTL_SC5_BRIGHTNESS       , "SC5" },
234 	{ LV2_MIDI_CTL_SC6                  , "SC6" },
235 	{ LV2_MIDI_CTL_SC7                  , "SC7" },
236 	{ LV2_MIDI_CTL_SC8                  , "SC8" },
237 	{ LV2_MIDI_CTL_SC9                  , "SC9" },
238 	{ LV2_MIDI_CTL_SC10                 , "SC10" },
239 
240 	{ LV2_MIDI_CTL_GENERAL_PURPOSE5     , "GeneralPurpose5" },
241 	{ LV2_MIDI_CTL_GENERAL_PURPOSE6     , "GeneralPurpose6" },
242 	{ LV2_MIDI_CTL_GENERAL_PURPOSE7     , "GeneralPurpose7" },
243 	{ LV2_MIDI_CTL_GENERAL_PURPOSE8     , "GeneralPurpose8" },
244 	{ LV2_MIDI_CTL_PORTAMENTO_CONTROL   , "PortamentoControl" },
245 
246 	{ LV2_MIDI_CTL_E1_REVERB_DEPTH      , "ReverbDepth" },
247 	{ LV2_MIDI_CTL_E2_TREMOLO_DEPTH     , "TremoloDepth" },
248 	{ LV2_MIDI_CTL_E3_CHORUS_DEPTH      , "ChorusDepth" },
249 	{ LV2_MIDI_CTL_E4_DETUNE_DEPTH      , "DetuneDepth" },
250 	{ LV2_MIDI_CTL_E5_PHASER_DEPTH      , "PhaserDepth" },
251 	{ LV2_MIDI_CTL_E1_REVERB_DEPTH      , "E1" },
252 	{ LV2_MIDI_CTL_E2_TREMOLO_DEPTH     , "E2" },
253 	{ LV2_MIDI_CTL_E3_CHORUS_DEPTH      , "E3" },
254 	{ LV2_MIDI_CTL_E4_DETUNE_DEPTH      , "E4" },
255 	{ LV2_MIDI_CTL_E5_PHASER_DEPTH      , "E5" },
256 
257 	{ LV2_MIDI_CTL_DATA_INCREMENT       , "DataIncrement" },
258 	{ LV2_MIDI_CTL_DATA_DECREMENT       , "DataDecrement" },
259 
260 	{ LV2_MIDI_CTL_NRPN_LSB             , "NRPN_LSB" },
261 	{ LV2_MIDI_CTL_NRPN_MSB             , "NRPN_MSB" },
262 
263 	{ LV2_MIDI_CTL_RPN_LSB              , "RPN_LSB" },
264 	{ LV2_MIDI_CTL_RPN_MSB              , "RPN_MSB" },
265 
266 	{ LV2_MIDI_CTL_ALL_SOUNDS_OFF       , "AllSoundsOff" },
267 	{ LV2_MIDI_CTL_RESET_CONTROLLERS    , "ResetControllers" },
268 	{ LV2_MIDI_CTL_LOCAL_CONTROL_SWITCH , "LocalControlSwitch" },
269 	{ LV2_MIDI_CTL_ALL_NOTES_OFF        , "AllNotesOff" },
270 	{ LV2_MIDI_CTL_OMNI_OFF             , "OmniOff" },
271 	{ LV2_MIDI_CTL_OMNI_ON              , "OmniOn" },
272 	{ LV2_MIDI_CTL_MONO1                , "Mono1" },
273 	{ LV2_MIDI_CTL_MONO2                , "Mono2" },
274 
275 	{ 0, NULL} // sentinel
276 };
277