1 /*
2  * Carla Native Plugins
3  * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 #include "CarlaNative.h"
19 #include "CarlaMIDI.h"
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 // -----------------------------------------------------------------------
25 
26 typedef enum {
27     PARAM_GAIN = 0,
28     PARAM_APPLY_NOTES,
29     PARAM_APPLY_AFTERTOUCH,
30     PARAM_APPLY_CC,
31     PARAM_COUNT
32 } MidiGainParams;
33 
34 typedef struct {
35     const NativeHostDescriptor* host;
36     float gain;
37     bool applyNotes;
38     bool applyAftertouch;
39     bool applyCC;
40 } MidiGainHandle;
41 
42 // -----------------------------------------------------------------------
43 
midigain_instantiate(const NativeHostDescriptor * host)44 static NativePluginHandle midigain_instantiate(const NativeHostDescriptor* host)
45 {
46     MidiGainHandle* const handle = (MidiGainHandle*)malloc(sizeof(MidiGainHandle));
47 
48     if (handle == NULL)
49         return NULL;
50 
51     handle->host = host;
52     handle->gain = 1.0f;
53     handle->applyNotes = true;
54     handle->applyAftertouch = true;
55     handle->applyCC = false;
56     return handle;
57 }
58 
59 #define handlePtr ((MidiGainHandle*)handle)
60 
midigain_cleanup(NativePluginHandle handle)61 static void midigain_cleanup(NativePluginHandle handle)
62 {
63     free(handlePtr);
64 }
65 
midigain_get_parameter_count(NativePluginHandle handle)66 static uint32_t midigain_get_parameter_count(NativePluginHandle handle)
67 {
68     return PARAM_COUNT;
69 
70     // unused
71     (void)handle;
72 }
73 
midigain_get_parameter_info(NativePluginHandle handle,uint32_t index)74 static const NativeParameter* midigain_get_parameter_info(NativePluginHandle handle, uint32_t index)
75 {
76     if (index > PARAM_COUNT)
77         return NULL;
78 
79     static NativeParameter param;
80 
81     param.hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
82     param.unit  = NULL;
83     param.scalePointCount = 0;
84     param.scalePoints     = NULL;
85 
86     switch (index)
87     {
88     case PARAM_GAIN:
89         param.name = "Gain";
90         param.ranges.def = 1.0f;
91         param.ranges.min = 0.001f;
92         param.ranges.max = 4.0f;
93         param.ranges.step = PARAMETER_RANGES_DEFAULT_STEP;
94         param.ranges.stepSmall = PARAMETER_RANGES_DEFAULT_STEP_SMALL;
95         param.ranges.stepLarge = PARAMETER_RANGES_DEFAULT_STEP_LARGE;
96         break;
97     case PARAM_APPLY_NOTES:
98         param.name   = "Apply Notes";
99         param.hints |= NATIVE_PARAMETER_IS_BOOLEAN;
100         param.ranges.def = 1.0f;
101         param.ranges.min = 0.0f;
102         param.ranges.max = 1.0f;
103         param.ranges.step = 1.0f;
104         param.ranges.stepSmall = 1.0f;
105         param.ranges.stepLarge = 1.0f;
106         break;
107     case PARAM_APPLY_AFTERTOUCH:
108         param.name   = "Apply Aftertouch";
109         param.hints |= NATIVE_PARAMETER_IS_BOOLEAN;
110         param.ranges.def = 1.0f;
111         param.ranges.min = 0.0f;
112         param.ranges.max = 1.0f;
113         param.ranges.step = 1.0f;
114         param.ranges.stepSmall = 1.0f;
115         param.ranges.stepLarge = 1.0f;
116         break;
117     case PARAM_APPLY_CC:
118         param.name   = "Apply CC";
119         param.hints |= NATIVE_PARAMETER_IS_BOOLEAN;
120         param.ranges.def = 0.0f;
121         param.ranges.min = 0.0f;
122         param.ranges.max = 1.0f;
123         param.ranges.step = 1.0f;
124         param.ranges.stepSmall = 1.0f;
125         param.ranges.stepLarge = 1.0f;
126         break;
127     }
128 
129     return &param;
130 
131     // unused
132     (void)handle;
133 }
134 
midigain_get_parameter_value(NativePluginHandle handle,uint32_t index)135 static float midigain_get_parameter_value(NativePluginHandle handle, uint32_t index)
136 {
137     switch (index)
138     {
139     case PARAM_GAIN:
140         return handlePtr->gain;
141     case PARAM_APPLY_NOTES:
142         return handlePtr->applyNotes ? 1.0f : 0.0f;
143     case PARAM_APPLY_AFTERTOUCH:
144         return handlePtr->applyAftertouch ? 1.0f : 0.0f;
145     case PARAM_APPLY_CC:
146         return handlePtr->applyCC ? 1.0f : 0.0f;
147     default:
148         return 0.0f;
149     }
150 }
151 
midigain_set_parameter_value(NativePluginHandle handle,uint32_t index,float value)152 static void midigain_set_parameter_value(NativePluginHandle handle, uint32_t index, float value)
153 {
154     switch (index)
155     {
156     case PARAM_GAIN:
157         handlePtr->gain = value;
158         break;
159     case PARAM_APPLY_NOTES:
160         handlePtr->applyNotes = (value >= 0.5f);
161         break;
162     case PARAM_APPLY_AFTERTOUCH:
163         handlePtr->applyAftertouch = (value >= 0.5f);
164         break;
165     case PARAM_APPLY_CC:
166         handlePtr->applyCC = (value >= 0.5f);
167         break;
168     }
169 }
170 
171 // FIXME for v3.0, use const for the input buffer
midigain_process(NativePluginHandle handle,float ** inBuffer,float ** outBuffer,uint32_t frames,const NativeMidiEvent * midiEvents,uint32_t midiEventCount)172 static void midigain_process(NativePluginHandle handle,
173                              float** inBuffer, float** outBuffer, uint32_t frames,
174                              const NativeMidiEvent* midiEvents, uint32_t midiEventCount)
175 {
176     const NativeHostDescriptor* const host = handlePtr->host;
177     const float gain           = handlePtr->gain;
178     const bool applyNotes      = handlePtr->applyNotes;
179     const bool applyAftertouch = handlePtr->applyAftertouch;
180     const bool applyCC         = handlePtr->applyCC;
181     NativeMidiEvent tmpEvent;
182 
183     for (uint32_t i=0; i < midiEventCount; ++i)
184     {
185         const NativeMidiEvent* const midiEvent = &midiEvents[i];
186 
187         const uint8_t status = (uint8_t)MIDI_GET_STATUS_FROM_DATA(midiEvent->data);
188 
189         if (midiEvent->size == 3 && ((applyNotes      && (status == MIDI_STATUS_NOTE_OFF || status == MIDI_STATUS_NOTE_ON)) ||
190                                      (applyAftertouch &&  status == MIDI_STATUS_POLYPHONIC_AFTERTOUCH) ||
191                                      (applyCC         &&  status == MIDI_STATUS_CONTROL_CHANGE)))
192         {
193             memcpy(&tmpEvent, midiEvent, sizeof(NativeMidiEvent));
194 
195             float value = (float)midiEvent->data[2] * gain;
196 
197             if (value <= 0.0f)
198                 tmpEvent.data[2] = 0;
199             else if (value >= 127.0f)
200                 tmpEvent.data[2] = 127;
201             else
202                 tmpEvent.data[2] = (uint8_t)value;
203 
204             host->write_midi_event(host->handle, &tmpEvent);
205         }
206         else
207             host->write_midi_event(host->handle, midiEvent);
208     }
209 
210     return;
211 
212     // unused
213     (void)inBuffer;
214     (void)outBuffer;
215     (void)frames;
216 }
217 
218 // -----------------------------------------------------------------------
219 
220 static const NativePluginDescriptor midigainDesc = {
221     .category  = NATIVE_PLUGIN_CATEGORY_UTILITY,
222     .hints     = NATIVE_PLUGIN_IS_RTSAFE,
223     .supports  = NATIVE_PLUGIN_SUPPORTS_EVERYTHING,
224     .audioIns  = 0,
225     .audioOuts = 0,
226     .cvIns     = 0,
227     .cvOuts    = 0,
228     .midiIns   = 1,
229     .midiOuts  = 1,
230     .paramIns  = 0,
231     .paramOuts = 0,
232     .name      = "MIDI Gain",
233     .label     = "midigain",
234     .maker     = "falkTX",
235     .copyright = "GNU GPL v2+",
236 
237     .instantiate = midigain_instantiate,
238     .cleanup     = midigain_cleanup,
239 
240     .get_parameter_count = midigain_get_parameter_count,
241     .get_parameter_info  = midigain_get_parameter_info,
242     .get_parameter_value = midigain_get_parameter_value,
243 
244     .get_midi_program_count = NULL,
245     .get_midi_program_info  = NULL,
246 
247     .set_parameter_value = midigain_set_parameter_value,
248     .set_midi_program    = NULL,
249     .set_custom_data     = NULL,
250 
251     .ui_show = NULL,
252     .ui_idle = NULL,
253 
254     .ui_set_parameter_value = NULL,
255     .ui_set_midi_program    = NULL,
256     .ui_set_custom_data     = NULL,
257 
258     .activate   = NULL,
259     .deactivate = NULL,
260     .process    = midigain_process,
261 
262     .get_state = NULL,
263     .set_state = NULL,
264 
265     .dispatcher = NULL
266 };
267 
268 // -----------------------------------------------------------------------
269 
270 void carla_register_native_plugin_midigain(void);
271 
carla_register_native_plugin_midigain(void)272 void carla_register_native_plugin_midigain(void)
273 {
274     carla_register_native_plugin(&midigainDesc);
275 }
276 
277 // -----------------------------------------------------------------------
278