/[svn]/gigedit/trunk/src/plugin/linuxsamplerplugin.cpp
ViewVC logotype

Contents of /gigedit/trunk/src/plugin/linuxsamplerplugin.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4008 - (show annotations) (download)
Wed Dec 22 14:40:54 2021 UTC (5 months ago) by schoenebeck
File size: 11924 byte(s)
* Live-editing: Show an error message on terminal if sampler's libgig
  version and Gigedit's libgig version are not binary compatible.

* Bumped version (1.2.0.svn3).

1 /*
2 * Copyright (C) 2007 - 2019 Andreas Persson
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with program; see the file COPYING. If not, write to the Free
16 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include "linuxsamplerplugin.h"
21
22 #ifdef LIBLINUXSAMPLER_HEADER_FILE
23 # include LIBLINUXSAMPLER_HEADER_FILE(plugins/InstrumentEditorFactory.h)
24 # include LIBLINUXSAMPLER_HEADER_FILE(engines/Engine.h)
25 # include LIBLINUXSAMPLER_HEADER_FILE(engines/EngineChannel.h)
26 #else
27 # include <linuxsampler/plugins/InstrumentEditorFactory.h>
28 # include <linuxsampler/engines/Engine.h>
29 # include <linuxsampler/engines/EngineChannel.h>
30 #endif
31
32 #include "../gigedit/gigedit.h"
33 #include "../gigedit/global.h"
34
35 #include <iostream>
36 #ifdef SIGCPP_HEADER_FILE
37 # include SIGCPP_HEADER_FILE(bind.h)
38 #else
39 # include <sigc++/bind.h>
40 #endif
41 #include <glibmm/main.h>
42 #include <set>
43
44 REGISTER_INSTRUMENT_EDITOR(LinuxSamplerPlugin)
45
46 struct LSPluginPrivate {
47 std::set<gig::Region*> debounceRegionChange;
48 bool debounceRegionChangedScheduled;
49
50 LSPluginPrivate() {
51 debounceRegionChangedScheduled = false;
52 }
53 };
54
55 LinuxSamplerPlugin::LinuxSamplerPlugin() {
56 pApp = new GigEdit;
57 priv = new LSPluginPrivate;
58 }
59
60 LinuxSamplerPlugin::~LinuxSamplerPlugin() {
61 if (pApp) delete static_cast<GigEdit*>(pApp);
62 if (priv) delete priv;
63 }
64
65 int LinuxSamplerPlugin::Main(void* pInstrument, String sTypeName, String sTypeVersion, void* /*pUserData*/) {
66 return Main(pInstrument, sTypeName, sTypeVersion);
67 }
68
69 int LinuxSamplerPlugin::Main(void* pInstrument, String sTypeName, String sTypeVersion) {
70 std::cout << "Entered Gigedit Main() loop :)\n" << std::flush;
71 gig::Instrument* pGigInstr = static_cast<gig::Instrument*>(pInstrument);
72 GigEdit* app = static_cast<GigEdit*>(pApp);
73
74 // connect notification signals
75 app->signal_file_structure_to_be_changed().connect(
76 sigc::bind(
77 sigc::mem_fun(
78 *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
79 ),
80 "gig::File"
81 )
82 );
83 app->signal_file_structure_changed().connect(
84 sigc::bind(
85 sigc::mem_fun(
86 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
87 ),
88 "gig::File"
89 )
90 );
91 app->signal_samples_to_be_removed().connect(
92 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onSamplesToBeRemoved)
93 );
94 app->signal_samples_removed().connect(
95 sigc::mem_fun(*this, &LinuxSamplerPlugin::NotifySamplesRemoved)
96 );
97 app->signal_region_to_be_changed().connect(
98 sigc::bind(
99 sigc::mem_fun(
100 *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
101 ),
102 "gig::Region"
103 )
104 );
105 app->signal_region_changed().connect(
106 sigc::bind(
107 sigc::mem_fun(
108 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
109 ),
110 "gig::Region"
111 )
112 );
113 app->signal_dimreg_to_be_changed().connect(
114 // not connected directly anymore ...
115 /*sigc::bind(
116 sigc::mem_fun(
117 *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
118 ),
119 "gig::DimensionRegion"
120 )*/
121 // ... because we are doing some event debouncing here :
122 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionToBeChanged)
123 );
124 app->signal_dimreg_changed().connect(
125 // not connected directly anymore ...
126 /*sigc::bind(
127 sigc::mem_fun(
128 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
129 ),
130 "gig::DimensionRegion"
131 )*/
132 // ... because we are doing some event debouncing here :
133 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionChanged)
134 );
135 app->signal_sample_changed().connect(
136 sigc::bind(
137 sigc::mem_fun(
138 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
139 ),
140 "gig::Sample"
141 )
142 );
143 app->signal_sample_ref_changed().connect(
144 sigc::mem_fun(*this, &LinuxSamplerPlugin::NotifySampleReferenceChanged)
145 );
146
147 app->signal_keyboard_key_hit().connect(
148 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onVirtualKeyboardKeyHit)
149 );
150 app->signal_keyboard_key_released().connect(
151 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onVirtualKeyboardKeyReleased)
152 );
153 app->signal_switch_sampler_instrument().connect(
154 sigc::mem_fun(*this, &LinuxSamplerPlugin::__requestSamplerToSwitchInstrument)
155 );
156 app->signal_script_to_be_changed.connect(
157 sigc::bind(
158 sigc::mem_fun(
159 *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
160 ),
161 "gig::Script"
162 )
163 );
164 app->signal_script_changed.connect(
165 sigc::bind(
166 sigc::mem_fun(
167 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
168 ),
169 "gig::Script"
170 )
171 );
172
173 // register a timeout job to gigedit's main loop, so we can poll the
174 // the sampler periodically for MIDI events (I HOPE it works on all
175 // archs, because gigedit is actually running in another thread than
176 // the one that is calling this timeout handler register code)
177 const Glib::RefPtr<Glib::TimeoutSource> timeout_source =
178 Glib::TimeoutSource::create(100); // poll every 100ms
179 timeout_source->connect(
180 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onPollPeriod)
181 );
182 timeout_source->attach(Glib::MainContext::get_default());
183
184 // run gigedit application
185 return app->run(pGigInstr);
186 }
187
188 void LinuxSamplerPlugin::__onDimRegionToBeChanged(gig::DimensionRegion* pDimRgn) {
189 // instead of sending this signal per dimregion ...
190 //NotifyDataStructureToBeChanged(pDimRgn, "gig::DimensionRegion");
191
192 // ... we are rather debouncing those dimregion to be changed events, and
193 // instead only send a region to be changed event, which is much faster when
194 // changing a very large amount of dimregions.
195 if (!pDimRgn) return;
196 gig::Region* pRegion = (gig::Region*) pDimRgn->GetParent();
197 const bool bIdle = priv->debounceRegionChange.empty();
198 bool bRegionLocked = priv->debounceRegionChange.count(pRegion);
199 if (!bRegionLocked) {
200 if (bIdle)
201 printf("DimRgn change event debounce BEGIN (%p)\n", pRegion);
202 priv->debounceRegionChange.insert(pRegion);
203 NotifyDataStructureToBeChanged(pRegion, "gig::Region");
204 }
205 }
206
207 void LinuxSamplerPlugin::__onDimRegionChanged(gig::DimensionRegion* pDimRgn) {
208 // like above, not sending this ...
209 //NotifyDataStructureChanged(pDimRgn, "gig::DimensionRegion");
210
211 // ... but rather aggressively debounce those dim region changed events and
212 // sending a debounced region changed event instead.
213 if (!pDimRgn) return;
214 if (!priv->debounceRegionChangedScheduled) {
215 priv->debounceRegionChangedScheduled = true;
216 Glib::signal_idle().connect_once(
217 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionChangedDebounced),
218 Glib::PRIORITY_HIGH_IDLE
219 );
220 }
221 }
222
223 void LinuxSamplerPlugin::__onDimRegionChangedDebounced() {
224 // Note that we are really aggressively unlocking the region here: we are
225 // not even bothering whether the amount "changed" events match with the
226 // previously sent amount of "to be changed" events, because this handler
227 // here is only called when the app's event loop is already idle for a
228 // while, which is not the case if the app is still changing instrument
229 // parameters (except if the app is i.e. currently showing an error dialog
230 // to the user).
231 priv->debounceRegionChangedScheduled = false;
232 for (std::set<gig::Region*>::const_iterator it = priv->debounceRegionChange.begin();
233 it != priv->debounceRegionChange.end(); ++it)
234 {
235 gig::Region* pRegion = *it;
236 NotifyDataStructureChanged(pRegion, "gig::Region");
237 }
238 priv->debounceRegionChange.clear();
239 printf("DimRgn change event debounce END\n");
240 }
241
242 bool LinuxSamplerPlugin::__onPollPeriod() {
243 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
244 GigEdit* app = static_cast<GigEdit*>(pApp);
245 if (!NotesChanged()) return true;
246 for (int iKey = 0; iKey < 128; iKey++)
247 if (NoteChanged(iKey))
248 NoteIsActive(iKey) ?
249 app->on_note_on_event(iKey, NoteOnVelocity(iKey)) :
250 app->on_note_off_event(iKey, NoteOffVelocity(iKey));
251 return true;
252 #else
253 return false;
254 #endif
255 }
256
257 void LinuxSamplerPlugin::__onSamplesToBeRemoved(std::list<gig::Sample*> lSamples) {
258 // we have to convert the gig::Sample* list to a void* list first
259 std::set<void*> samples;
260 for (
261 std::list<gig::Sample*>::iterator iter = lSamples.begin();
262 iter != lSamples.end(); ++iter
263 ) samples.insert((void*)*iter);
264 // finally send notification to sampler
265 NotifySamplesToBeRemoved(samples);
266 }
267
268 void LinuxSamplerPlugin::__onVirtualKeyboardKeyHit(int Key, int Velocity) {
269 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
270 SendNoteOnToSampler(Key, Velocity);
271 #endif
272 }
273
274 void LinuxSamplerPlugin::__onVirtualKeyboardKeyReleased(int Key, int Velocity) {
275 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
276 SendNoteOffToSampler(Key, Velocity);
277 #endif
278 }
279
280 void LinuxSamplerPlugin::__requestSamplerToSwitchInstrument(gig::Instrument* pInstrument) {
281 if (!pInstrument) return;
282
283 LinuxSampler::EngineChannel* pEngineChannel = GetEngineChannel();
284 if (!pEngineChannel) return;
285
286 LinuxSampler::Engine* pEngine = pEngineChannel->GetEngine();
287 if (!pEngine) return;
288
289 LinuxSampler::InstrumentManager* pInstrumentManager = pEngine->GetInstrumentManager();
290 if (!pInstrumentManager) return;
291
292 gig::File* pFile = (gig::File*) pInstrument->GetParent();
293
294 // resolve instrument's index number in its gig file
295 int index = -1;
296 for (int i = 0; pFile->GetInstrument(i); ++i) {
297 if (pFile->GetInstrument(i) == pInstrument) {
298 index = i;
299 break;
300 }
301 }
302 if (index < 0) return;
303
304 LinuxSampler::InstrumentManager::instrument_id_t id;
305 id.FileName = pFile->GetFileName();
306 id.Index = index;
307 pInstrumentManager->LoadInstrumentInBackground(id, pEngineChannel);
308 }
309
310 bool LinuxSamplerPlugin::IsTypeSupported(String sTypeName, String sTypeVersion) {
311 // does LS use libgig for the instrument, which basically means is the
312 // instrument loaded by the sampler a .gig file?
313 if (sTypeName != gig::libraryName()) return false;
314
315 // the libgig version used by sampler and the one used by gigedit need
316 // to be binary compatible for live-editing mode to work
317 if (sTypeVersion == gig::libraryVersion()) return true;
318 std::cerr <<
319 "ERROR: Installed Gigedit version is binary incompatible to "
320 "installed LinuxSampler version!\n"
321 "[Cause: Gigedit is using libgig " + gig::libraryVersion() +
322 " whereas LinuxSampler is using libgig " + sTypeVersion + "]\n";
323 return false;
324 }
325
326 String LinuxSamplerPlugin::Name() {
327 return "gigedit";
328 }
329
330 String LinuxSamplerPlugin::Version() {
331 return VERSION; // gigedit's version
332 }
333
334 String LinuxSamplerPlugin::Description() {
335 return "Gigedit is an instrument editor for gig files.";
336 }

  ViewVC Help
Powered by ViewVC