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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3177 - (hide 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 schoenebeck 1213 /*
2 schoenebeck 3068 * Copyright (C) 2007 - 2017 Andreas Persson
3 schoenebeck 1213 *
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 schoenebeck 3068 #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 schoenebeck 1656 #include "../gigedit/gigedit.h"
33 schoenebeck 1680 #include "../gigedit/global.h"
34 schoenebeck 1213
35     #include <iostream>
36 schoenebeck 1322 #include <sigc++/bind.h>
37 schoenebeck 1656 #include <glibmm/main.h>
38 schoenebeck 3177 #include <set>
39 schoenebeck 1213
40     REGISTER_INSTRUMENT_EDITOR(LinuxSamplerPlugin)
41    
42 schoenebeck 3177 struct LSPluginPrivate {
43     std::set<gig::Region*> debounceRegionChange;
44     bool debounceRegionChangedScheduled;
45    
46     LSPluginPrivate() {
47     debounceRegionChangedScheduled = false;
48     }
49     };
50    
51 schoenebeck 1213 LinuxSamplerPlugin::LinuxSamplerPlugin() {
52 schoenebeck 1654 pApp = new GigEdit;
53 schoenebeck 3177 priv = new LSPluginPrivate;
54 schoenebeck 1213 }
55    
56 schoenebeck 1654 LinuxSamplerPlugin::~LinuxSamplerPlugin() {
57 persson 2841 if (pApp) delete static_cast<GigEdit*>(pApp);
58 schoenebeck 3177 if (priv) delete priv;
59 schoenebeck 1654 }
60    
61 schoenebeck 1877 int LinuxSamplerPlugin::Main(void* pInstrument, String sTypeName, String sTypeVersion, void* /*pUserData*/) {
62     return Main(pInstrument, sTypeName, sTypeVersion);
63     }
64    
65 schoenebeck 1213 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 persson 2841 GigEdit* app = static_cast<GigEdit*>(pApp);
69 schoenebeck 1654
70 schoenebeck 1322 // connect notification signals
71 schoenebeck 1654 app->signal_file_structure_to_be_changed().connect(
72 schoenebeck 1322 sigc::bind(
73     sigc::mem_fun(
74     *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
75     ),
76     "gig::File"
77     )
78     );
79 schoenebeck 1654 app->signal_file_structure_changed().connect(
80 schoenebeck 1322 sigc::bind(
81     sigc::mem_fun(
82     *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
83     ),
84     "gig::File"
85     )
86     );
87 schoenebeck 1654 app->signal_samples_to_be_removed().connect(
88 schoenebeck 1322 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onSamplesToBeRemoved)
89     );
90 schoenebeck 1654 app->signal_samples_removed().connect(
91 schoenebeck 1322 sigc::mem_fun(*this, &LinuxSamplerPlugin::NotifySamplesRemoved)
92     );
93 schoenebeck 1654 app->signal_region_to_be_changed().connect(
94 schoenebeck 1322 sigc::bind(
95     sigc::mem_fun(
96     *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
97     ),
98     "gig::Region"
99     )
100     );
101 schoenebeck 1654 app->signal_region_changed().connect(
102 schoenebeck 1322 sigc::bind(
103     sigc::mem_fun(
104     *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
105     ),
106     "gig::Region"
107     )
108     );
109 schoenebeck 1654 app->signal_dimreg_to_be_changed().connect(
110 schoenebeck 3177 // not connected directly anymore ...
111     /*sigc::bind(
112 schoenebeck 1322 sigc::mem_fun(
113     *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
114     ),
115     "gig::DimensionRegion"
116 schoenebeck 3177 )*/
117     // ... because we are doing some event debouncing here :
118     sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionToBeChanged)
119 schoenebeck 1322 );
120 schoenebeck 1654 app->signal_dimreg_changed().connect(
121 schoenebeck 3177 // not connected directly anymore ...
122     /*sigc::bind(
123 schoenebeck 1322 sigc::mem_fun(
124     *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
125     ),
126     "gig::DimensionRegion"
127 schoenebeck 3177 )*/
128     // ... because we are doing some event debouncing here :
129     sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionChanged)
130 schoenebeck 1322 );
131 schoenebeck 1853 app->signal_sample_changed().connect(
132     sigc::bind(
133     sigc::mem_fun(
134     *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
135     ),
136     "gig::Sample"
137     )
138     );
139 schoenebeck 1654 app->signal_sample_ref_changed().connect(
140 schoenebeck 1322 sigc::mem_fun(*this, &LinuxSamplerPlugin::NotifySampleReferenceChanged)
141     );
142 schoenebeck 1654
143 schoenebeck 1660 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 schoenebeck 2689 app->signal_switch_sampler_instrument().connect(
150     sigc::mem_fun(*this, &LinuxSamplerPlugin::__requestSamplerToSwitchInstrument)
151     );
152 schoenebeck 2903 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 schoenebeck 1660
169 schoenebeck 1656 // 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 schoenebeck 1654
180 schoenebeck 1322 // run gigedit application
181 schoenebeck 1654 return app->run(pGigInstr);
182 schoenebeck 1213 }
183    
184 schoenebeck 3177 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 schoenebeck 1656 bool LinuxSamplerPlugin::__onPollPeriod() {
239 schoenebeck 1680 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
240 persson 2841 GigEdit* app = static_cast<GigEdit*>(pApp);
241 schoenebeck 1654 if (!NotesChanged()) return true;
242     for (int iKey = 0; iKey < 128; iKey++)
243     if (NoteChanged(iKey))
244 schoenebeck 1664 NoteIsActive(iKey) ?
245     app->on_note_on_event(iKey, NoteOnVelocity(iKey)) :
246     app->on_note_off_event(iKey, NoteOffVelocity(iKey));
247 schoenebeck 1654 return true;
248 schoenebeck 1680 #else
249     return false;
250     #endif
251 schoenebeck 1654 }
252    
253 schoenebeck 1322 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 persson 2841 iter != lSamples.end(); ++iter
259 schoenebeck 1322 ) samples.insert((void*)*iter);
260     // finally send notification to sampler
261     NotifySamplesToBeRemoved(samples);
262     }
263    
264 schoenebeck 1660 void LinuxSamplerPlugin::__onVirtualKeyboardKeyHit(int Key, int Velocity) {
265 schoenebeck 1680 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
266 schoenebeck 1660 SendNoteOnToSampler(Key, Velocity);
267 schoenebeck 1680 #endif
268 schoenebeck 1660 }
269    
270     void LinuxSamplerPlugin::__onVirtualKeyboardKeyReleased(int Key, int Velocity) {
271 schoenebeck 1680 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
272 schoenebeck 1660 SendNoteOffToSampler(Key, Velocity);
273 schoenebeck 1680 #endif
274 schoenebeck 1660 }
275    
276 schoenebeck 2689 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 schoenebeck 1213 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