/[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 4008 - (hide annotations) (download)
Wed Dec 22 14:40:54 2021 UTC (2 years, 4 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 schoenebeck 1213 /*
2 persson 3461 * Copyright (C) 2007 - 2019 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 3364 #ifdef SIGCPP_HEADER_FILE
37     # include SIGCPP_HEADER_FILE(bind.h)
38     #else
39     # include <sigc++/bind.h>
40     #endif
41 schoenebeck 1656 #include <glibmm/main.h>
42 schoenebeck 3177 #include <set>
43 schoenebeck 1213
44     REGISTER_INSTRUMENT_EDITOR(LinuxSamplerPlugin)
45    
46 schoenebeck 3177 struct LSPluginPrivate {
47     std::set<gig::Region*> debounceRegionChange;
48     bool debounceRegionChangedScheduled;
49    
50     LSPluginPrivate() {
51     debounceRegionChangedScheduled = false;
52     }
53     };
54    
55 schoenebeck 1213 LinuxSamplerPlugin::LinuxSamplerPlugin() {
56 schoenebeck 1654 pApp = new GigEdit;
57 schoenebeck 3177 priv = new LSPluginPrivate;
58 schoenebeck 1213 }
59    
60 schoenebeck 1654 LinuxSamplerPlugin::~LinuxSamplerPlugin() {
61 persson 2841 if (pApp) delete static_cast<GigEdit*>(pApp);
62 schoenebeck 3177 if (priv) delete priv;
63 schoenebeck 1654 }
64    
65 schoenebeck 1877 int LinuxSamplerPlugin::Main(void* pInstrument, String sTypeName, String sTypeVersion, void* /*pUserData*/) {
66     return Main(pInstrument, sTypeName, sTypeVersion);
67     }
68    
69 schoenebeck 1213 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 persson 2841 GigEdit* app = static_cast<GigEdit*>(pApp);
73 schoenebeck 1654
74 schoenebeck 1322 // connect notification signals
75 schoenebeck 1654 app->signal_file_structure_to_be_changed().connect(
76 schoenebeck 1322 sigc::bind(
77     sigc::mem_fun(
78     *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
79     ),
80     "gig::File"
81     )
82     );
83 schoenebeck 1654 app->signal_file_structure_changed().connect(
84 schoenebeck 1322 sigc::bind(
85     sigc::mem_fun(
86     *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
87     ),
88     "gig::File"
89     )
90     );
91 schoenebeck 1654 app->signal_samples_to_be_removed().connect(
92 schoenebeck 1322 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onSamplesToBeRemoved)
93     );
94 schoenebeck 1654 app->signal_samples_removed().connect(
95 schoenebeck 1322 sigc::mem_fun(*this, &LinuxSamplerPlugin::NotifySamplesRemoved)
96     );
97 schoenebeck 1654 app->signal_region_to_be_changed().connect(
98 schoenebeck 1322 sigc::bind(
99     sigc::mem_fun(
100     *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
101     ),
102     "gig::Region"
103     )
104     );
105 schoenebeck 1654 app->signal_region_changed().connect(
106 schoenebeck 1322 sigc::bind(
107     sigc::mem_fun(
108     *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
109     ),
110     "gig::Region"
111     )
112     );
113 schoenebeck 1654 app->signal_dimreg_to_be_changed().connect(
114 schoenebeck 3177 // not connected directly anymore ...
115     /*sigc::bind(
116 schoenebeck 1322 sigc::mem_fun(
117     *this, &LinuxSamplerPlugin::NotifyDataStructureToBeChanged
118     ),
119     "gig::DimensionRegion"
120 schoenebeck 3177 )*/
121     // ... because we are doing some event debouncing here :
122     sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionToBeChanged)
123 schoenebeck 1322 );
124 schoenebeck 1654 app->signal_dimreg_changed().connect(
125 schoenebeck 3177 // not connected directly anymore ...
126     /*sigc::bind(
127 schoenebeck 1322 sigc::mem_fun(
128     *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
129     ),
130     "gig::DimensionRegion"
131 schoenebeck 3177 )*/
132     // ... because we are doing some event debouncing here :
133     sigc::mem_fun(*this, &LinuxSamplerPlugin::__onDimRegionChanged)
134 schoenebeck 1322 );
135 schoenebeck 1853 app->signal_sample_changed().connect(
136     sigc::bind(
137     sigc::mem_fun(
138     *this, &LinuxSamplerPlugin::NotifyDataStructureChanged
139     ),
140     "gig::Sample"
141     )
142     );
143 schoenebeck 1654 app->signal_sample_ref_changed().connect(
144 schoenebeck 1322 sigc::mem_fun(*this, &LinuxSamplerPlugin::NotifySampleReferenceChanged)
145     );
146 schoenebeck 1654
147 schoenebeck 1660 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 schoenebeck 2689 app->signal_switch_sampler_instrument().connect(
154     sigc::mem_fun(*this, &LinuxSamplerPlugin::__requestSamplerToSwitchInstrument)
155     );
156 schoenebeck 2903 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 schoenebeck 1660
173 schoenebeck 1656 // 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 persson 3461 sigc::mem_fun(*this, &LinuxSamplerPlugin::__onPollPeriod)
181 schoenebeck 1656 );
182     timeout_source->attach(Glib::MainContext::get_default());
183 schoenebeck 1654
184 schoenebeck 1322 // run gigedit application
185 schoenebeck 1654 return app->run(pGigInstr);
186 schoenebeck 1213 }
187    
188 schoenebeck 3177 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 schoenebeck 1656 bool LinuxSamplerPlugin::__onPollPeriod() {
243 schoenebeck 1680 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
244 persson 2841 GigEdit* app = static_cast<GigEdit*>(pApp);
245 schoenebeck 1654 if (!NotesChanged()) return true;
246     for (int iKey = 0; iKey < 128; iKey++)
247     if (NoteChanged(iKey))
248 schoenebeck 1664 NoteIsActive(iKey) ?
249     app->on_note_on_event(iKey, NoteOnVelocity(iKey)) :
250     app->on_note_off_event(iKey, NoteOffVelocity(iKey));
251 schoenebeck 1654 return true;
252 schoenebeck 1680 #else
253     return false;
254     #endif
255 schoenebeck 1654 }
256    
257 schoenebeck 1322 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 persson 2841 iter != lSamples.end(); ++iter
263 schoenebeck 1322 ) samples.insert((void*)*iter);
264     // finally send notification to sampler
265     NotifySamplesToBeRemoved(samples);
266     }
267    
268 schoenebeck 1660 void LinuxSamplerPlugin::__onVirtualKeyboardKeyHit(int Key, int Velocity) {
269 schoenebeck 1680 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
270 schoenebeck 1660 SendNoteOnToSampler(Key, Velocity);
271 schoenebeck 1680 #endif
272 schoenebeck 1660 }
273    
274     void LinuxSamplerPlugin::__onVirtualKeyboardKeyReleased(int Key, int Velocity) {
275 schoenebeck 1680 #if HAVE_LINUXSAMPLER_VIRTUAL_MIDI_DEVICE
276 schoenebeck 1660 SendNoteOffToSampler(Key, Velocity);
277 schoenebeck 1680 #endif
278 schoenebeck 1660 }
279    
280 schoenebeck 2689 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 schoenebeck 1213 bool LinuxSamplerPlugin::IsTypeSupported(String sTypeName, String sTypeVersion) {
311 schoenebeck 4008 // 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 schoenebeck 1213 }
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