/[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 3177 - (show annotations) (download)
Thu May 11 20:59:46 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 11271 byte(s)
* Live mode performance optimization: fixed the editor to be non-responsive
  for a very long time, which happened if changing instrument parameters of
  a very large amount of dimension regions was requested.
* Bumped version (1.0.0.svn45).

1 /*
2 * Copyright (C) 2007 - 2017 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 #include <sigc++/bind.h>
37 #include <glibmm/main.h>
38 #include <set>
39
40 REGISTER_INSTRUMENT_EDITOR(LinuxSamplerPlugin)
41
42 struct LSPluginPrivate {
43 std::set<gig::Region*> debounceRegionChange;
44 bool debounceRegionChangedScheduled;
45
46 LSPluginPrivate() {
47 debounceRegionChangedScheduled = false;
48 }
49 };
50
51 LinuxSamplerPlugin::LinuxSamplerPlugin() {
52 pApp = new GigEdit;
53 priv = new LSPluginPrivate;
54 }
55
56 LinuxSamplerPlugin::~LinuxSamplerPlugin() {
57 if (pApp) delete static_cast<GigEdit*>(pApp);
58 if (priv) delete priv;
59 }
60
61 int LinuxSamplerPlugin::Main(void* pInstrument, String sTypeName, String sTypeVersion, void* /*pUserData*/) {
62 return Main(pInstrument, sTypeName, sTypeVersion);
63 }
64
65 int LinuxSamplerPlugin::Main(void* pInstrument, String sTypeName, String sTypeVersion) {
66 std::cout << "Entered Gigedit Main() loop :)\n" << std::flush;
67 gig::Instrument* pGigInstr = static_cast<gig::Instrument*>(pInstrument);
68 GigEdit* app = static_cast<GigEdit*>(pApp);
69
70 // connect notification signals
71 app->signal_file_structure_to_be_changed().connect(
72 sigc::bind(
73 sigc::mem_fun(
74 *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
75 ),
76 "gig::File"
77 )
78 );
79 app->signal_file_structure_changed().connect(
80 sigc::bind(
81 sigc::mem_fun(
82 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
83 ),
84 "gig::File"
85 )
86 );
87 app->signal_samples_to_be_removed().connect(
88 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onSamplesToBeRemoved)
89 );
90 app->signal_samples_removed().connect(
91 sigc::mem_fun(*this, &LinuxSamplerPlugin::NotifySamplesRemoved)
92 );
93 app->signal_region_to_be_changed().connect(
94 sigc::bind(
95 sigc::mem_fun(
96 *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
97 ),
98 "gig::Region"
99 )
100 );
101 app->signal_region_changed().connect(
102 sigc::bind(
103 sigc::mem_fun(
104 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
105 ),
106 "gig::Region"
107 )
108 );
109 app->signal_dimreg_to_be_changed().connect(
110 // not connected directly anymore ...
111 /*sigc::bind(
112 sigc::mem_fun(
113 *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
114 ),
115 "gig::DimensionRegion"
116 )*/
117 // ... because we are doing some event debouncing here :
118 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionToBeChanged)
119 );
120 app->signal_dimreg_changed().connect(
121 // not connected directly anymore ...
122 /*sigc::bind(
123 sigc::mem_fun(
124 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
125 ),
126 "gig::DimensionRegion"
127 )*/
128 // ... because we are doing some event debouncing here :
129 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionChanged)
130 );
131 app->signal_sample_changed().connect(
132 sigc::bind(
133 sigc::mem_fun(
134 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
135 ),
136 "gig::Sample"
137 )
138 );
139 app->signal_sample_ref_changed().connect(
140 sigc::mem_fun(*this, &LinuxSamplerPlugin::NotifySampleReferenceChanged)
141 );
142
143 app->signal_keyboard_key_hit().connect(
144 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onVirtualKeyboardKeyHit)
145 );
146 app->signal_keyboard_key_released().connect(
147 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onVirtualKeyboardKeyReleased)
148 );
149 app->signal_switch_sampler_instrument().connect(
150 sigc::mem_fun(*this, &LinuxSamplerPlugin::__requestSamplerToSwitchInstrument)
151 );
152 app->signal_script_to_be_changed.connect(
153 sigc::bind(
154 sigc::mem_fun(
155 *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
156 ),
157 "gig::Script"
158 )
159 );
160 app->signal_script_changed.connect(
161 sigc::bind(
162 sigc::mem_fun(
163 *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
164 ),
165 "gig::Script"
166 )
167 );
168
169 // register a timeout job to gigedit's main loop, so we can poll the
170 // the sampler periodically for MIDI events (I HOPE it works on all
171 // archs, because gigedit is actually running in another thread than
172 // the one that is calling this timeout handler register code)
173 const Glib::RefPtr<Glib::TimeoutSource> timeout_source =
174 Glib::TimeoutSource::create(100); // poll every 100ms
175 timeout_source->connect(
176 sigc::mem_fun(this, &LinuxSamplerPlugin::__onPollPeriod)
177 );
178 timeout_source->attach(Glib::MainContext::get_default());
179
180 // run gigedit application
181 return app->run(pGigInstr);
182 }
183
184 void LinuxSamplerPlugin::__onDimRegionToBeChanged(gig::DimensionRegion* pDimRgn) {
185 // instead of sending this signal per dimregion ...
186 //NotifyDataStructureToBeChanged(pDimRgn, "gig::DimensionRegion");
187
188 // ... we are rather debouncing those dimregion to be changed events, and
189 // instead only send a region to be changed event, which is much faster when
190 // changing a very large amount of dimregions.
191 if (!pDimRgn) return;
192 gig::Region* pRegion = (gig::Region*) pDimRgn->GetParent();
193 const bool bIdle = priv->debounceRegionChange.empty();
194 bool bRegionLocked = priv->debounceRegionChange.count(pRegion);
195 if (!bRegionLocked) {
196 if (bIdle)
197 printf("DimRgn change event debounce BEGIN (%p)\n", pRegion);
198 priv->debounceRegionChange.insert(pRegion);
199 NotifyDataStructureToBeChanged(pRegion, "gig::Region");
200 }
201 }
202
203 void LinuxSamplerPlugin::__onDimRegionChanged(gig::DimensionRegion* pDimRgn) {
204 // like above, not sending this ...
205 //NotifyDataStructureChanged(pDimRgn, "gig::DimensionRegion");
206
207 // ... but rather aggressively debounce those dim region changed events and
208 // sending a debounced region changed event instead.
209 if (!pDimRgn) return;
210 if (!priv->debounceRegionChangedScheduled) {
211 priv->debounceRegionChangedScheduled = true;
212 Glib::signal_idle().connect_once(
213 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionChangedDebounced),
214 Glib::PRIORITY_HIGH_IDLE
215 );
216 }
217 }
218
219 void LinuxSamplerPlugin::__onDimRegionChangedDebounced() {
220 // Note that we are really aggressively unlocking the region here: we are
221 // not even bothering whether the amount "changed" events match with the
222 // previously sent amount of "to be changed" events, because this handler
223 // here is only called when the app's event loop is already idle for a
224 // while, which is not the case if the app is still changing instrument
225 // parameters (except if the app is i.e. currently showing an error dialog
226 // to the user).
227 priv->debounceRegionChangedScheduled = false;
228 for (std::set<gig::Region*>::const_iterator it = priv->debounceRegionChange.begin();
229 it != priv->debounceRegionChange.end(); ++it)
230 {
231 gig::Region* pRegion = *it;
232 NotifyDataStructureChanged(pRegion, "gig::Region");
233 }
234 priv->debounceRegionChange.clear();
235 printf("DimRgn change event debounce END\n");
236 }
237
238 bool LinuxSamplerPlugin::__onPollPeriod() {
239 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
240 GigEdit* app = static_cast<GigEdit*>(pApp);
241 if (!NotesChanged()) return true;
242 for (int iKey = 0; iKey < 128; iKey++)
243 if (NoteChanged(iKey))
244 NoteIsActive(iKey) ?
245 app->on_note_on_event(iKey, NoteOnVelocity(iKey)) :
246 app->on_note_off_event(iKey, NoteOffVelocity(iKey));
247 return true;
248 #else
249 return false;
250 #endif
251 }
252
253 void LinuxSamplerPlugin::__onSamplesToBeRemoved(std::list<gig::Sample*> lSamples) {
254 // we have to convert the gig::Sample* list to a void* list first
255 std::set<void*> samples;
256 for (
257 std::list<gig::Sample*>::iterator iter = lSamples.begin();
258 iter != lSamples.end(); ++iter
259 ) samples.insert((void*)*iter);
260 // finally send notification to sampler
261 NotifySamplesToBeRemoved(samples);
262 }
263
264 void LinuxSamplerPlugin::__onVirtualKeyboardKeyHit(int Key, int Velocity) {
265 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
266 SendNoteOnToSampler(Key, Velocity);
267 #endif
268 }
269
270 void LinuxSamplerPlugin::__onVirtualKeyboardKeyReleased(int Key, int Velocity) {
271 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
272 SendNoteOffToSampler(Key, Velocity);
273 #endif
274 }
275
276 void LinuxSamplerPlugin::__requestSamplerToSwitchInstrument(gig::Instrument* pInstrument) {
277 if (!pInstrument) return;
278
279 LinuxSampler::EngineChannel* pEngineChannel = GetEngineChannel();
280 if (!pEngineChannel) return;
281
282 LinuxSampler::Engine* pEngine = pEngineChannel->GetEngine();
283 if (!pEngine) return;
284
285 LinuxSampler::InstrumentManager* pInstrumentManager = pEngine->GetInstrumentManager();
286 if (!pInstrumentManager) return;
287
288 gig::File* pFile = (gig::File*) pInstrument->GetParent();
289
290 // resolve instrument's index number in its gig file
291 int index = -1;
292 for (int i = 0; pFile->GetInstrument(i); ++i) {
293 if (pFile->GetInstrument(i) == pInstrument) {
294 index = i;
295 break;
296 }
297 }
298 if (index < 0) return;
299
300 LinuxSampler::InstrumentManager::instrument_id_t id;
301 id.FileName = pFile->GetFileName();
302 id.Index = index;
303 pInstrumentManager->LoadInstrumentInBackground(id, pEngineChannel);
304 }
305
306 bool LinuxSamplerPlugin::IsTypeSupported(String sTypeName, String sTypeVersion) {
307 return sTypeName == gig::libraryName() &&
308 sTypeVersion == gig::libraryVersion();
309 }
310
311 String LinuxSamplerPlugin::Name() {
312 return "gigedit";
313 }
314
315 String LinuxSamplerPlugin::Version() {
316 return VERSION; // gigedit's version
317 }
318
319 String LinuxSamplerPlugin::Description() {
320 return "Gigedit is an instrument editor for gig files.";
321 }

  ViewVC Help
Powered by ViewVC