/[svn]/linuxsampler/trunk/src/engines/gig/InstrumentResourceManager.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/gig/InstrumentResourceManager.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4010 - (hide annotations) (download)
Thu Dec 23 15:26:02 2021 UTC (2 years, 3 months ago) by schoenebeck
File size: 50119 byte(s)
* Additional more verbose error output in case an appropriate
  instrument editor could not be found (if there is not any
  editor at all then say so, if there is one but not capable
  to handle the instrument, then make this case clear to the
  user; in both cases print the expected location for instrument
  editor plugins).

* Bumped version (2.2.0.svn9).

1 schoenebeck 3719
2 schoenebeck 53 /***************************************************************************
3     * *
4     * LinuxSampler - modular, streaming capable sampler *
5     * *
6 schoenebeck 56 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
7 schoenebeck 3917 * Copyright (C) 2005 - 2021 Christian Schoenebeck *
8 schoenebeck 53 * *
9     * This program is free software; you can redistribute it and/or modify *
10     * it under the terms of the GNU General Public License as published by *
11     * the Free Software Foundation; either version 2 of the License, or *
12     * (at your option) any later version. *
13     * *
14     * This program is distributed in the hope that it will be useful, *
15     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17     * GNU General Public License for more details. *
18     * *
19     * You should have received a copy of the GNU General Public License *
20     * along with this program; if not, write to the Free Software *
21     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
22     * MA 02111-1307 USA *
23     ***************************************************************************/
24    
25     #include <sstream>
26    
27     #include "InstrumentResourceManager.h"
28 iliev 2012 #include "EngineChannel.h"
29     #include "Engine.h"
30 schoenebeck 53
31 schoenebeck 1424 #include "../../common/global_private.h"
32 schoenebeck 1375 #include "../../plugins/InstrumentEditorFactory.h"
33 schoenebeck 1212
34 schoenebeck 53 namespace LinuxSampler { namespace gig {
35    
36 schoenebeck 517 // some data needed for the libgig callback function
37     struct progress_callback_arg_t {
38 schoenebeck 947 InstrumentResourceManager* pManager;
39     InstrumentManager::instrument_id_t* pInstrumentKey;
40 schoenebeck 517 };
41    
42 schoenebeck 1212 // we use this to react on events concerning an instrument on behalf of an instrument editor
43     class InstrumentEditorProxy : public InstrumentConsumer {
44     public:
45 schoenebeck 3034 virtual ~InstrumentEditorProxy() {}
46    
47 schoenebeck 1212 virtual void ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
48     //TODO: inform the instrument editor about the pending update
49     }
50    
51     virtual void ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
52     //TODO:: inform the instrument editor about finished update
53     }
54    
55     virtual void OnResourceProgress(float fProgress) {
56     //TODO: inform the instrument editor about the progress of an update
57     }
58    
59     // the instrument we borrowed on behalf of the editor
60     ::gig::Instrument* pInstrument;
61 schoenebeck 1653 // the instrument editor we work on behalf
62     InstrumentEditor* pEditor;
63 schoenebeck 1212 };
64    
65 schoenebeck 517 /**
66     * Callback function which will be called by libgig during loading of
67     * instruments to inform about the current progress. Or to be more
68     * specific; it will be called during the GetInstrument() call.
69     *
70     * @param pProgress - contains current progress value, pointer to the
71     * InstrumentResourceManager instance and
72     * instrument ID
73     */
74     void InstrumentResourceManager::OnInstrumentLoadingProgress(::gig::progress_t* pProgress) {
75 persson 2837 dmsg(7,("gig::InstrumentResourceManager: progress %f%%", pProgress->factor));
76 schoenebeck 517 progress_callback_arg_t* pArg = static_cast<progress_callback_arg_t*>(pProgress->custom);
77     // we randomly schedule 90% for the .gig file loading and the remaining 10% later for sample caching
78     const float localProgress = 0.9f * pProgress->factor;
79     pArg->pManager->DispatchResourceProgressEvent(*pArg->pInstrumentKey, localProgress);
80     }
81    
82 schoenebeck 947 String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
83 schoenebeck 970 Lock();
84     ::gig::Instrument* pInstrument = Resource(ID, false);
85     String res = (pInstrument) ? pInstrument->pInfo->Name : "";
86     Unlock();
87     return res;
88 schoenebeck 947 }
89    
90 schoenebeck 1321 String InstrumentResourceManager::GetInstrumentDataStructureName(instrument_id_t ID) {
91 schoenebeck 1212 return ::gig::libraryName();
92     }
93    
94 schoenebeck 1321 String InstrumentResourceManager::GetInstrumentDataStructureVersion(instrument_id_t ID) {
95 schoenebeck 1212 return ::gig::libraryVersion();
96     }
97    
98 schoenebeck 1525 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::GetInstrumentFileContent(String File) throw (InstrumentManagerException) {
99     ::RIFF::File* riff = NULL;
100     ::gig::File* gig = NULL;
101     try {
102     std::vector<instrument_id_t> result;
103     riff = new ::RIFF::File(File);
104     gig = new ::gig::File(riff);
105     gig->SetAutoLoad(false); // avoid time consuming samples scanning
106     for (int i = 0; gig->GetInstrument(i); i++) {
107     instrument_id_t id;
108     id.FileName = File;
109     id.Index = i;
110     result.push_back(id);
111     }
112     if (gig) delete gig;
113     if (riff) delete riff;
114     return result;
115     } catch (::RIFF::Exception e) {
116     if (gig) delete gig;
117     if (riff) delete riff;
118     throw InstrumentManagerException(e.Message);
119     } catch (...) {
120     if (gig) delete gig;
121     if (riff) delete riff;
122     throw InstrumentManagerException("Unknown exception while trying to parse '" + File + "'");
123     }
124     }
125    
126     InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) {
127 iliev 1771 Lock();
128     ::gig::Instrument* pInstrument = Resource(ID, false);
129     bool loaded = (pInstrument != NULL);
130     if (!loaded) Unlock();
131    
132 schoenebeck 1525 ::RIFF::File* riff = NULL;
133     ::gig::File* gig = NULL;
134     try {
135 persson 2326 if (!loaded) {
136 iliev 1771 riff = new ::RIFF::File(ID.FileName);
137     gig = new ::gig::File(riff);
138     gig->SetAutoLoad(false); // avoid time consuming samples scanning
139     pInstrument = gig->GetInstrument(ID.Index);
140     }
141 schoenebeck 1852
142 schoenebeck 1525 if (!pInstrument) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName);
143 iliev 1771
144 schoenebeck 1525 instrument_info_t info;
145 iliev 1771 for (int i = 0; i < 128; i++) { info.KeyBindings[i] = info.KeySwitchBindings[i] = 0; }
146    
147     ::gig::File* pFile = (::gig::File*) pInstrument->GetParent();
148    
149     if (pFile->pVersion) {
150     info.FormatVersion = ToString(pFile->pVersion->major);
151     info.Product = pFile->pInfo->Product;
152     info.Artists = pFile->pInfo->Artists;
153 schoenebeck 1525 }
154 iliev 1771
155 schoenebeck 1525 info.InstrumentName = pInstrument->pInfo->Name;
156 iliev 1771
157 schoenebeck 3966 size_t iRgn = 0;
158     for (::gig::Region* pRegion = pInstrument->GetRegionAt(iRgn);
159     pRegion; pRegion = pInstrument->GetRegionAt(++iRgn))
160     {
161 iliev 1771 int low = pRegion->KeyRange.low;
162     int high = pRegion->KeyRange.high;
163     if (low < 0 || low > 127 || high < 0 || high > 127 || low > high) {
164     std::cerr << "Invalid key range: " << low << " - " << high << std::endl;
165     } else {
166     for (int i = low; i <= high; i++) info.KeyBindings[i] = 1;
167     }
168     }
169 persson 1797
170 iliev 1774 if (loaded) { // retrieve keyswitching only if the instrument is fully loaded.
171 persson 1797
172     // only return keyswitch range if keyswitching is used
173     bool hasKeyswitches = false;
174 schoenebeck 3966 size_t iRgn = 0;
175     for (::gig::Region* pRegion = pInstrument->GetRegionAt(iRgn);
176 persson 1797 pRegion && !hasKeyswitches ;
177 schoenebeck 3966 pRegion = pInstrument->GetRegionAt(++iRgn))
178     {
179 persson 1797 for (int i = 0 ; i < pRegion->Dimensions ; i++) {
180     if (pRegion->pDimensionDefinitions[i].dimension == ::gig::dimension_keyboard) {
181     hasKeyswitches = true;
182     break;
183     }
184     }
185 iliev 1774 }
186 persson 1797
187     if (hasKeyswitches) {
188     int low = pInstrument->DimensionKeyRange.low;
189     int high = pInstrument->DimensionKeyRange.high;
190     if (low < 0 || low > 127 || high < 0 || high > 127 || low > high) {
191     std::cerr << "Invalid keyswitch range: " << low << " - " << high << std::endl;
192     } else {
193     for (int i = low; i <= high; i++) info.KeySwitchBindings[i] = 1;
194     }
195     }
196 iliev 1771 }
197    
198     if (loaded) Unlock();
199    
200 schoenebeck 1525 if (gig) delete gig;
201     if (riff) delete riff;
202     return info;
203     } catch (::RIFF::Exception e) {
204 iliev 1771 if (loaded) Unlock();
205 schoenebeck 1525 if (gig) delete gig;
206     if (riff) delete riff;
207     throw InstrumentManagerException(e.Message);
208     } catch (...) {
209 iliev 1771 if (loaded) Unlock();
210 schoenebeck 1525 if (gig) delete gig;
211     if (riff) delete riff;
212     throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'");
213     }
214     }
215    
216 schoenebeck 2687 InstrumentEditor* InstrumentResourceManager::LaunchInstrumentEditor(LinuxSampler::EngineChannel* pEngineChannel, instrument_id_t ID, void* pUserData) throw (InstrumentManagerException) {
217 schoenebeck 1321 const String sDataType = GetInstrumentDataStructureName(ID);
218     const String sDataVersion = GetInstrumentDataStructureVersion(ID);
219 schoenebeck 1212 // find instrument editors capable to handle given instrument
220     std::vector<String> vEditors =
221     InstrumentEditorFactory::MatchingEditors(sDataType, sDataVersion);
222 schoenebeck 4009 if (!vEditors.size()) {
223 schoenebeck 4010 if (InstrumentEditorFactory::AvailableEditors().empty()) {
224     fprintf(stderr,
225     "ERROR: There is not any instrument editor registered to the sampler!\n"
226     "[Cause: Make sure an instrument editor is installed to the sampler's plugin dir (%s)]\n",
227     InstrumentEditorFactory::PluginDirsAsString().c_str()
228     );
229     throw InstrumentManagerException(
230     "There is not any instrument editor installed and registered to the sampler"
231     );
232     }
233 schoenebeck 4009 fprintf(stderr,
234 schoenebeck 4010 "ERROR: Did not find a matching .gig editor for instrument "
235     "('%s', %d) having data structure ('%s','%s'); installed "
236     "and registered editor candidates were: %s\n"
237     "[Cause: Make sure a .gig instrument editor (e.g. Gigedit) "
238     "is installed to the sampler's plugin dir (%s) and that the "
239     "installed editor version is binary compatible to this "
240     "sampler's version (i.e. both must use the same libgig "
241     "version).]\n",
242 schoenebeck 4009 ID.FileName.c_str(), ID.Index,
243 schoenebeck 4010 sDataType.c_str(), sDataVersion.c_str(),
244     InstrumentEditorFactory::AvailableEditorsAsString().c_str(),
245     InstrumentEditorFactory::PluginDirsAsString().c_str()
246 schoenebeck 4009 );
247 schoenebeck 1212 throw InstrumentManagerException(
248 schoenebeck 4010 "There is no instrument editor installed and registered to "
249     "the sampler that would be capable to handle this .gig instrument"
250 schoenebeck 1212 );
251 schoenebeck 4009 }
252 schoenebeck 1212 // simply use the first editor in the result set
253     dmsg(1,("Found matching editor '%s' for instrument ('%s', %d) having data structure ('%s','%s')\n",
254     vEditors[0].c_str(), ID.FileName.c_str(), ID.Index, sDataType.c_str(), sDataVersion.c_str()));
255     InstrumentEditor* pEditor = InstrumentEditorFactory::Create(vEditors[0]);
256 schoenebeck 1321 // register for receiving notifications from the instrument editor
257 schoenebeck 1212 pEditor->AddListener(this);
258     // create a proxy that reacts on notification on behalf of the editor
259     InstrumentEditorProxy* pProxy = new InstrumentEditorProxy;
260     // borrow the instrument on behalf of the instrument editor
261     ::gig::Instrument* pInstrument = Borrow(ID, pProxy);
262     // remember the proxy and instrument for this instrument editor
263     pProxy->pInstrument = pInstrument;
264 schoenebeck 1653 pProxy->pEditor = pEditor;
265 schoenebeck 1212 InstrumentEditorProxiesMutex.Lock();
266 schoenebeck 1653 InstrumentEditorProxies.add(pProxy);
267 schoenebeck 1212 InstrumentEditorProxiesMutex.Unlock();
268     // launch the instrument editor for the given instrument
269 schoenebeck 2687 pEditor->Launch(pEngineChannel, pInstrument, sDataType, sDataVersion, pUserData);
270 schoenebeck 1659
271     // register the instrument editor as virtual MIDI device as well ...
272     VirtualMidiDevice* pVirtualMidiDevice =
273     dynamic_cast<VirtualMidiDevice*>(pEditor);
274     if (!pVirtualMidiDevice) {
275     std::cerr << "Instrument editor not a virtual MIDI device\n" << std::flush;
276 schoenebeck 1868 return pEditor;
277 schoenebeck 1659 }
278     // NOTE: for now connect the virtual MIDI keyboard of the instrument editor (if any) with all engine channels that have the same instrument as the editor was opened for ( other ideas ? )
279     Lock();
280 iliev 2012 std::set<EngineChannel*> engineChannels =
281 schoenebeck 1659 GetEngineChannelsUsing(pInstrument, false/*don't lock again*/);
282 iliev 2012 std::set<EngineChannel*>::iterator iter = engineChannels.begin();
283     std::set<EngineChannel*>::iterator end = engineChannels.end();
284     for (; iter != end; ++iter) (static_cast<AbstractEngineChannel*>(*iter))->Connect(pVirtualMidiDevice);
285 schoenebeck 1659 Unlock();
286 schoenebeck 1868
287     return pEditor;
288 schoenebeck 1212 }
289    
290     /**
291     * Will be called by the respective instrument editor once it left its
292     * Main() loop. That way we can handle cleanup before its thread finally
293     * dies.
294     *
295     * @param pSender - instrument editor that stops execution
296     */
297     void InstrumentResourceManager::OnInstrumentEditorQuit(InstrumentEditor* pSender) {
298     dmsg(1,("InstrumentResourceManager: instrument editor quit, doing cleanup\n"));
299 schoenebeck 1659
300     ::gig::Instrument* pInstrument = NULL;
301     InstrumentEditorProxy* pProxy = NULL;
302     int iProxyIndex = -1;
303    
304     // first find the editor proxy entry for this editor
305 persson 2427 {
306     LockGuard lock(InstrumentEditorProxiesMutex);
307     for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
308     InstrumentEditorProxy* pCurProxy =
309     dynamic_cast<InstrumentEditorProxy*>(
310     InstrumentEditorProxies[i]
311     );
312     if (pCurProxy->pEditor == pSender) {
313     pProxy = pCurProxy;
314     iProxyIndex = i;
315     pInstrument = pCurProxy->pInstrument;
316     }
317 schoenebeck 1653 }
318 schoenebeck 1212 }
319 persson 1455
320 schoenebeck 1659 if (!pProxy) {
321     std::cerr << "Eeeek, could not find instrument editor proxy, "
322     "this is a bug!\n" << std::flush;
323     return;
324     }
325    
326     // now unregister editor as not being available as a virtual MIDI device anymore
327     VirtualMidiDevice* pVirtualMidiDevice =
328     dynamic_cast<VirtualMidiDevice*>(pSender);
329     if (pVirtualMidiDevice) {
330     Lock();
331     // NOTE: see note in LaunchInstrumentEditor()
332 iliev 2012 std::set<EngineChannel*> engineChannels =
333 schoenebeck 1659 GetEngineChannelsUsing(pInstrument, false/*don't lock again*/);
334 iliev 2012 std::set<EngineChannel*>::iterator iter = engineChannels.begin();
335     std::set<EngineChannel*>::iterator end = engineChannels.end();
336 schoenebeck 1659 for (; iter != end; ++iter) (*iter)->Disconnect(pVirtualMidiDevice);
337     Unlock();
338     } else {
339     std::cerr << "Could not unregister editor as not longer acting as "
340     "virtual MIDI device. Wasn't it registered?\n"
341     << std::flush;
342     }
343    
344     // finally delete proxy entry and hand back instrument
345     if (pInstrument) {
346 persson 2427 {
347     LockGuard lock(InstrumentEditorProxiesMutex);
348     InstrumentEditorProxies.remove(iProxyIndex);
349     }
350 schoenebeck 1659
351     HandBack(pInstrument, pProxy);
352     delete pProxy;
353     }
354    
355 persson 1455 // Note that we don't need to free the editor here. As it
356     // derives from Thread, it will delete itself when the thread
357     // dies.
358 schoenebeck 1212 }
359    
360 schoenebeck 1662 #if 0 // currently unused :
361 schoenebeck 1653 /**
362     * Try to inform the respective instrument editor(s), that a note on
363     * event just occured. This method is called by the MIDI thread. If any
364     * obstacles are in the way (e.g. if a wait for an unlock would be
365     * required) we give up immediately, since the RT safeness of the MIDI
366     * thread has absolute priority.
367     */
368     void InstrumentResourceManager::TrySendNoteOnToEditors(uint8_t Key, uint8_t Velocity, ::gig::Instrument* pInstrument) {
369     const bool bGotLock = InstrumentEditorProxiesMutex.Trylock(); // naively assumes RT safe implementation
370     if (!bGotLock) return; // hell, forget it, not worth the hassle
371     for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
372     InstrumentEditorProxy* pProxy =
373     dynamic_cast<InstrumentEditorProxy*>(
374     InstrumentEditorProxies[i]
375     );
376     if (pProxy->pInstrument == pInstrument)
377 schoenebeck 1659 pProxy->pEditor->SendNoteOnToDevice(Key, Velocity);
378 schoenebeck 1653 }
379     InstrumentEditorProxiesMutex.Unlock(); // naively assumes RT safe implementation
380     }
381    
382     /**
383     * Try to inform the respective instrument editor(s), that a note off
384     * event just occured. This method is called by the MIDI thread. If any
385     * obstacles are in the way (e.g. if a wait for an unlock would be
386     * required) we give up immediately, since the RT safeness of the MIDI
387     * thread has absolute priority.
388     */
389     void InstrumentResourceManager::TrySendNoteOffToEditors(uint8_t Key, uint8_t Velocity, ::gig::Instrument* pInstrument) {
390     const bool bGotLock = InstrumentEditorProxiesMutex.Trylock(); // naively assumes RT safe implementation
391     if (!bGotLock) return; // hell, forget it, not worth the hassle
392     for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
393     InstrumentEditorProxy* pProxy =
394     dynamic_cast<InstrumentEditorProxy*>(
395     InstrumentEditorProxies[i]
396     );
397     if (pProxy->pInstrument == pInstrument)
398 schoenebeck 1659 pProxy->pEditor->SendNoteOffToDevice(Key, Velocity);
399 schoenebeck 1653 }
400     InstrumentEditorProxiesMutex.Unlock(); // naively assumes RT safe implementation
401     }
402 schoenebeck 1662 #endif // unused
403 schoenebeck 1653
404 schoenebeck 1321 void InstrumentResourceManager::OnSamplesToBeRemoved(std::set<void*> Samples, InstrumentEditor* pSender) {
405     if (Samples.empty()) {
406     std::cerr << "gig::InstrumentResourceManager: WARNING, "
407     "OnSamplesToBeRemoved() called with empty list, this "
408     "is a bug!\n" << std::flush;
409     return;
410     }
411     // TODO: ATM we assume here that all samples are from the same file
412     ::gig::Sample* pFirstSample = (::gig::Sample*) *Samples.begin();
413     ::gig::File* pCriticalFile = dynamic_cast< ::gig::File*>(pFirstSample->GetParent());
414     // completely suspend all engines that use that same file
415     SuspendEnginesUsing(pCriticalFile);
416     }
417    
418     void InstrumentResourceManager::OnSamplesRemoved(InstrumentEditor* pSender) {
419     // resume all previously, completely suspended engines
420     // (we don't have to un-cache the removed samples here, since that is
421     // automatically done by the gig::Sample destructor)
422     ResumeAllEngines();
423     }
424    
425     void InstrumentResourceManager::OnDataStructureToBeChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
426 schoenebeck 2902 dmsg(5,("gig::InstrumentResourceManager::OnDataStructureToBeChanged(%s)\n", sStructType.c_str()));
427 schoenebeck 1321 //TODO: remove code duplication
428     if (sStructType == "gig::File") {
429     // completely suspend all engines that use that file
430     ::gig::File* pFile = (::gig::File*) pStruct;
431     SuspendEnginesUsing(pFile);
432     } else if (sStructType == "gig::Instrument") {
433     // completely suspend all engines that use that instrument
434     ::gig::Instrument* pInstrument = (::gig::Instrument*) pStruct;
435     SuspendEnginesUsing(pInstrument);
436     } else if (sStructType == "gig::Region") {
437     // only advice the engines to suspend the given region, so they'll
438     // only ignore that region (and probably already other suspended
439     // ones), but beside that continue normal playback
440     ::gig::Region* pRegion = (::gig::Region*) pStruct;
441     ::gig::Instrument* pInstrument =
442     (::gig::Instrument*) pRegion->GetParent();
443     Lock();
444 iliev 2012 std::set<Engine*> engines =
445 schoenebeck 1321 GetEnginesUsing(pInstrument, false/*don't lock again*/);
446 iliev 2012 std::set<Engine*>::iterator iter = engines.begin();
447     std::set<Engine*>::iterator end = engines.end();
448 schoenebeck 1321 for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
449     Unlock();
450     } else if (sStructType == "gig::DimensionRegion") {
451     // only advice the engines to suspend the given DimensionRegions's
452     // parent region, so they'll only ignore that region (and probably
453     // already other suspended ones), but beside that continue normal
454     // playback
455     ::gig::DimensionRegion* pDimReg =
456     (::gig::DimensionRegion*) pStruct;
457     ::gig::Region* pRegion = pDimReg->GetParent();
458     ::gig::Instrument* pInstrument =
459     (::gig::Instrument*) pRegion->GetParent();
460     Lock();
461 iliev 2012 std::set<Engine*> engines =
462 schoenebeck 1321 GetEnginesUsing(pInstrument, false/*don't lock again*/);
463 iliev 2012 std::set<Engine*>::iterator iter = engines.begin();
464     std::set<Engine*>::iterator end = engines.end();
465 schoenebeck 1321 for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
466     Unlock();
467 schoenebeck 2902 } else if (sStructType == "gig::Script") {
468     // no need to suspend anything here, since the sampler is
469     // processing a translated VM representation of the original script
470     // source code, not accessing the source code itself during playback
471     ::gig::Script* pScript = (::gig::Script*) pStruct;
472     // remember the original source code of the script, since the script
473     // resource manager uses the source code as key
474     pendingScriptUpdatesMutex.Lock();
475     pendingScriptUpdates[pScript] = pScript->GetScriptAsText();
476     pendingScriptUpdatesMutex.Unlock();
477 schoenebeck 1321 } else {
478     std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
479     "structure '" << sStructType << "' requested to be "
480     "suspended by instrument editor. This is a bug!\n"
481     << std::flush;
482     //TODO: we should inform the instrument editor that something seriously went wrong
483     }
484     }
485    
486     void InstrumentResourceManager::OnDataStructureChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
487 schoenebeck 2902 dmsg(5,("gig::InstrumentResourceManager::OnDataStructureChanged(%s)\n", sStructType.c_str()));
488 schoenebeck 1321 //TODO: remove code duplication
489     if (sStructType == "gig::File") {
490     // resume all previously suspended engines
491     ResumeAllEngines();
492     } else if (sStructType == "gig::Instrument") {
493     // resume all previously suspended engines
494     ResumeAllEngines();
495 schoenebeck 1852 } else if (sStructType == "gig::Sample") {
496     // we're assuming here, that OnDataStructureToBeChanged() with
497     // "gig::File" was called previously, so we won't resume anything
498     // here, but just re-cache the given sample
499     Lock();
500     ::gig::Sample* pSample = (::gig::Sample*) pStruct;
501     ::gig::File* pFile = (::gig::File*) pSample->GetParent();
502     UncacheInitialSamples(pSample);
503     // now re-cache ...
504     std::vector< ::gig::Instrument*> instruments =
505     GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
506     for (int i = 0; i < instruments.size(); i++) {
507     if (SampleReferencedByInstrument(pSample, instruments[i])) {
508 iliev 2012 std::set<EngineChannel*> engineChannels =
509 schoenebeck 1852 GetEngineChannelsUsing(instruments[i], false/*don't lock again*/);
510 iliev 2012 std::set<EngineChannel*>::iterator iter = engineChannels.begin();
511     std::set<EngineChannel*>::iterator end = engineChannels.end();
512 schoenebeck 1852 for (; iter != end; ++iter)
513     CacheInitialSamples(pSample, *iter);
514     }
515     }
516     Unlock();
517 schoenebeck 1321 } else if (sStructType == "gig::Region") {
518     // advice the engines to resume the given region, that is to
519     // using it for playback again
520     ::gig::Region* pRegion = (::gig::Region*) pStruct;
521     ::gig::Instrument* pInstrument =
522     (::gig::Instrument*) pRegion->GetParent();
523     Lock();
524 iliev 2012 std::set<Engine*> engines =
525 schoenebeck 1321 GetEnginesUsing(pInstrument, false/*don't lock again*/);
526 iliev 2012 std::set<Engine*>::iterator iter = engines.begin();
527     std::set<Engine*>::iterator end = engines.end();
528 schoenebeck 1321 for (; iter != end; ++iter) (*iter)->Resume(pRegion);
529     Unlock();
530     } else if (sStructType == "gig::DimensionRegion") {
531     // advice the engines to resume the given DimensionRegion's parent
532     // region, that is to using it for playback again
533     ::gig::DimensionRegion* pDimReg =
534     (::gig::DimensionRegion*) pStruct;
535     ::gig::Region* pRegion = pDimReg->GetParent();
536     ::gig::Instrument* pInstrument =
537     (::gig::Instrument*) pRegion->GetParent();
538     Lock();
539 iliev 2012 std::set<Engine*> engines =
540 schoenebeck 1321 GetEnginesUsing(pInstrument, false/*don't lock again*/);
541 iliev 2012 std::set<Engine*>::iterator iter = engines.begin();
542     std::set<Engine*>::iterator end = engines.end();
543 schoenebeck 1321 for (; iter != end; ++iter) (*iter)->Resume(pRegion);
544     Unlock();
545 schoenebeck 2902 } else if (sStructType == "gig::Script") {
546     // inform all engine channels which are using this script, that
547     // they need to reload (parse) the script's source code text
548     ::gig::Script* pScript = (::gig::Script*) pStruct;
549     pendingScriptUpdatesMutex.Lock();
550     if (pendingScriptUpdates.count(pScript)) {
551     const String& code = pendingScriptUpdates[pScript];
552     std::set<EngineChannel*> channels = GetEngineChannelsUsingScriptSourceCode(code, true/*lock*/);
553     pendingScriptUpdates.erase(pScript);
554     std::set<EngineChannel*>::iterator iter = channels.begin();
555     std::set<EngineChannel*>::iterator end = channels.end();
556     for (; iter != end; ++iter) (*iter)->reloadScript(pScript);
557     }
558     pendingScriptUpdatesMutex.Unlock();
559 schoenebeck 1321 } else {
560     std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
561     "structure '" << sStructType << "' requested to be "
562     "resumed by instrument editor. This is a bug!\n"
563     << std::flush;
564     //TODO: we should inform the instrument editor that something seriously went wrong
565     }
566     }
567    
568     void InstrumentResourceManager::OnSampleReferenceChanged(void* pOldSample, void* pNewSample, InstrumentEditor* pSender) {
569     // uncache old sample in case it's not used by anybody anymore
570     if (pOldSample) {
571     Lock();
572     ::gig::Sample* pSample = (::gig::Sample*) pOldSample;
573     ::gig::File* pFile = (::gig::File*) pSample->GetParent();
574 schoenebeck 1852 bool bSampleStillInUse = false;
575 schoenebeck 1321 std::vector< ::gig::Instrument*> instruments =
576     GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
577 schoenebeck 1852 for (int i = 0; i < instruments.size(); i++) {
578     if (SampleReferencedByInstrument(pSample, instruments[i])) {
579     bSampleStillInUse = true;
580     break;
581     }
582     }
583     if (!bSampleStillInUse) UncacheInitialSamples(pSample);
584 schoenebeck 1321 Unlock();
585     }
586     // make sure new sample reference is cached
587     if (pNewSample) {
588     Lock();
589     ::gig::Sample* pSample = (::gig::Sample*) pNewSample;
590     ::gig::File* pFile = (::gig::File*) pSample->GetParent();
591     // get all engines that use that same gig::File
592 iliev 2012 std::set<Engine*> engines = GetEnginesUsing(pFile, false/*don't lock again*/);
593     std::set<Engine*>::iterator iter = engines.begin();
594     std::set<Engine*>::iterator end = engines.end();
595 schoenebeck 1321 for (; iter != end; ++iter)
596     CacheInitialSamples(pSample, *iter);
597     Unlock();
598     }
599     }
600    
601 schoenebeck 53 ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
602 schoenebeck 1321 // get gig file from internal gig file manager
603 persson 2185 ::gig::File* pGig = Gigs.Borrow(Key.FileName, reinterpret_cast<GigConsumer*>(Key.Index)); // conversion kinda hackish :/
604 schoenebeck 53
605 schoenebeck 517 // we pass this to the progress callback mechanism of libgig
606     progress_callback_arg_t callbackArg;
607     callbackArg.pManager = this;
608     callbackArg.pInstrumentKey = &Key;
609    
610     ::gig::progress_t progress;
611     progress.callback = OnInstrumentLoadingProgress;
612     progress.custom = &callbackArg;
613    
614 schoenebeck 947 dmsg(1,("Loading gig instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
615     ::gig::Instrument* pInstrument = pGig->GetInstrument(Key.Index, &progress);
616 schoenebeck 53 if (!pInstrument) {
617     std::stringstream msg;
618 schoenebeck 947 msg << "There's no instrument with index " << Key.Index << ".";
619 schoenebeck 1212 throw InstrumentManagerException(msg.str());
620 schoenebeck 53 }
621 schoenebeck 3964 pGig->GetSample(0); // just to force complete instrument loading
622 schoenebeck 53 dmsg(1,("OK\n"));
623    
624 persson 2326 uint maxSamplesPerCycle = GetMaxSamplesPerCycle(pConsumer);
625    
626 schoenebeck 53 // cache initial samples points (for actually needed samples)
627     dmsg(1,("Caching initial samples..."));
628 schoenebeck 3966 size_t iRegion = 0;
629     for (::gig::Region* pRgn = pInstrument->GetRegionAt(iRegion); pRgn;
630     pRgn = pInstrument->GetRegionAt(++iRegion))
631     {
632 schoenebeck 517 // we randomly schedule 90% for the .gig file loading and the remaining 10% now for sample caching
633     const float localProgress = 0.9f + 0.1f * (float) iRegion / (float) pInstrument->Regions;
634 schoenebeck 947 DispatchResourceProgressEvent(Key, localProgress);
635    
636 schoenebeck 354 if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {
637 schoenebeck 53 dmsg(2,("C"));
638 persson 2326 CacheInitialSamples(pRgn->GetSample(), maxSamplesPerCycle);
639 schoenebeck 53 }
640     for (uint i = 0; i < pRgn->DimensionRegions; i++) {
641 persson 2326 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, maxSamplesPerCycle);
642 schoenebeck 53 }
643     }
644     dmsg(1,("OK\n"));
645 schoenebeck 517 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
646 schoenebeck 53
647     // we need the following for destruction later
648     instr_entry_t* pEntry = new instr_entry_t;
649 schoenebeck 947 pEntry->ID.FileName = Key.FileName;
650     pEntry->ID.Index = Key.Index;
651 persson 2326 pEntry->pFile = pGig;
652 persson 438
653 persson 2326 // and we save this to check if we need to reallocate for an engine with higher value of 'MaxSamplesPerSecond'
654     pEntry->MaxSamplesPerCycle = maxSamplesPerCycle;
655 schoenebeck 2275
656 schoenebeck 53 pArg = pEntry;
657    
658     return pInstrument;
659     }
660    
661 persson 2326 void InstrumentResourceManager::Destroy(::gig::Instrument* pResource, void* pArg) {
662 schoenebeck 53 instr_entry_t* pEntry = (instr_entry_t*) pArg;
663 schoenebeck 947 // we don't need the .gig file here anymore
664 persson 2326 Gigs.HandBack(pEntry->pFile, reinterpret_cast<GigConsumer*>(pEntry->ID.Index)); // conversion kinda hackish :/
665 schoenebeck 53 delete pEntry;
666     }
667    
668 iliev 2012 void InstrumentResourceManager::DeleteRegionIfNotUsed(::gig::DimensionRegion* pRegion, region_info_t* pRegInfo) {
669     // TODO: we could delete Region and Instrument here if they have become unused
670 persson 1038 }
671 schoenebeck 2275
672 iliev 2012 void InstrumentResourceManager::DeleteSampleIfNotUsed(::gig::Sample* pSample, region_info_t* pRegInfo) {
673     ::gig::File* gig = pRegInfo->file;
674     ::RIFF::File* riff = static_cast< ::RIFF::File*>(pRegInfo->pArg);
675     if (gig) {
676     gig->DeleteSample(pSample);
677 schoenebeck 3964 if (!gig->GetSample(0)) {
678 iliev 2012 dmsg(2,("No more samples in use - freeing gig\n"));
679     delete gig;
680     delete riff;
681 persson 1038 }
682     }
683     }
684    
685     /**
686 schoenebeck 1321 * Just a wrapper around the other @c CacheInitialSamples() method.
687     *
688     * @param pSample - points to the sample to be cached
689     * @param pEngine - pointer to Gig Engine Channel which caused this call
690     * (may be NULL, in this case default amount of samples
691     * will be cached)
692     */
693 iliev 2012 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, EngineChannel* pEngineChannel) {
694     Engine* pEngine =
695 schoenebeck 1321 (pEngineChannel && pEngineChannel->GetEngine()) ?
696 iliev 2012 dynamic_cast<Engine*>(pEngineChannel->GetEngine()) : NULL;
697 schoenebeck 1321 CacheInitialSamples(pSample, pEngine);
698     }
699    
700     /**
701 schoenebeck 53 * Caches a certain size at the beginning of the given sample in RAM. If the
702     * sample is very short, the whole sample will be loaded into RAM and thus
703     * no disk streaming is needed for this sample. Caching an initial part of
704     * samples is needed to compensate disk reading latency.
705     *
706     * @param pSample - points to the sample to be cached
707 schoenebeck 1321 * @param pEngine - pointer to Gig Engine which caused this call
708     * (may be NULL, in this case default amount of samples
709     * will be cached)
710 schoenebeck 53 */
711 iliev 2012 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, AbstractEngine* pEngine) {
712 persson 2326 uint maxSamplesPerCycle =
713     (pEngine) ? pEngine->pAudioOutputDevice->MaxSamplesPerCycle() :
714     DefaultMaxSamplesPerCycle();
715     CacheInitialSamples(pSample, maxSamplesPerCycle);
716     }
717    
718     void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, uint maxSamplesPerCycle) {
719 schoenebeck 420 if (!pSample) {
720 persson 438 dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
721 schoenebeck 420 return;
722     }
723     if (!pSample->SamplesTotal) return; // skip zero size samples
724 persson 438
725 schoenebeck 554 if (pSample->SamplesTotal <= CONFIG_PRELOAD_SAMPLES) {
726 schoenebeck 53 // Sample is too short for disk streaming, so we load the whole
727 schoenebeck 554 // sample into RAM and place 'pAudioIO->FragmentSize << CONFIG_MAX_PITCH'
728 schoenebeck 53 // number of '0' samples (silence samples) behind the official buffer
729     // border, to allow the interpolator do it's work even at the end of
730 persson 438 // the sample.
731 schoenebeck 3719 const uint neededSilenceSamples = uint((maxSamplesPerCycle << CONFIG_MAX_PITCH) + 6);
732 schoenebeck 3054 const uint currentlyCachedSilenceSamples = uint(pSample->GetCache().NullExtensionSize / pSample->FrameSize);
733 schoenebeck 420 if (currentlyCachedSilenceSamples < neededSilenceSamples) {
734 schoenebeck 3054 dmsg(3,("Caching whole sample (sample name: \"%s\", sample size: %llu)\n", pSample->pInfo->Name.c_str(), (long long)pSample->SamplesTotal));
735 schoenebeck 420 ::gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(neededSilenceSamples);
736 schoenebeck 3054 dmsg(4,("Cached %llu Bytes, %llu silence bytes.\n", (long long)buf.Size, (long long)buf.NullExtensionSize));
737 schoenebeck 420 }
738 schoenebeck 53 }
739 schoenebeck 554 else { // we only cache CONFIG_PRELOAD_SAMPLES and stream the other sample points from disk
740     if (!pSample->GetCache().Size) pSample->LoadSampleData(CONFIG_PRELOAD_SAMPLES);
741 schoenebeck 53 }
742    
743     if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
744     }
745    
746 schoenebeck 1321 void InstrumentResourceManager::UncacheInitialSamples(::gig::Sample* pSample) {
747 persson 2837 dmsg(1,("Uncaching sample %p\n",(void*)pSample));
748 schoenebeck 1321 if (pSample->GetCache().Size) pSample->ReleaseSampleData();
749     }
750 schoenebeck 53
751 schoenebeck 1321 /**
752     * Returns a list with all instruments currently in use, that are part of
753     * the given file.
754     *
755     * @param pFile - search criteria
756     * @param bLock - whether we should lock (mutex) the instrument manager
757     * during this call and unlock at the end of this call
758     */
759     std::vector< ::gig::Instrument*> InstrumentResourceManager::GetInstrumentsCurrentlyUsedOf(::gig::File* pFile, bool bLock) {
760     if (bLock) Lock();
761     std::vector< ::gig::Instrument*> result;
762     std::vector< ::gig::Instrument*> allInstruments = Resources(false/*don't lock again*/);
763     for (int i = 0; i < allInstruments.size(); i++)
764     if (
765     (::gig::File*) allInstruments[i]->GetParent()
766     == pFile
767     ) result.push_back(allInstruments[i]);
768     if (bLock) Unlock();
769     return result;
770     }
771 schoenebeck 53
772 schoenebeck 1321 /**
773 schoenebeck 1659 * Returns a list with all gig engine channels that are currently using
774 schoenebeck 2902 * the given real-time instrument script (provided as source code).
775     *
776     * @param pScript - search criteria
777     * @param bLock - whether we should lock (mutex) the instrument manager
778     * during this call and unlock at the end of this call
779     */
780     std::set<EngineChannel*> InstrumentResourceManager::GetEngineChannelsUsingScriptSourceCode(const String& code, bool bLock) {
781     if (bLock) Lock();
782     std::set<EngineChannel*> result;
783 schoenebeck 3733 std::set<InstrumentScriptConsumer*> consumers = scripts.ConsumersOf({
784 schoenebeck 3734 .code = code,
785     .patchVars = std::map<String,String>(), // just required for GCC
786 schoenebeck 3971 .engineChannel = NULL, // just required for GCC
787     .wildcardPatchVars = true,
788     .wildcardEngineChannel = true
789 schoenebeck 3733 });
790 schoenebeck 2902 std::set<InstrumentScriptConsumer*>::iterator iter = consumers.begin();
791     std::set<InstrumentScriptConsumer*>::iterator end = consumers.end();
792     for (; iter != end; ++iter) {
793     EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(*iter);
794     if (!pEngineChannel) continue;
795     result.insert(pEngineChannel);
796     }
797     if (bLock) Unlock();
798     return result;
799     }
800    
801     /**
802     * Returns a list with all gig engine channels that are currently using
803 schoenebeck 1659 * the given instrument.
804     *
805     * @param pInstrument - search criteria
806     * @param bLock - whether we should lock (mutex) the instrument manager
807     * during this call and unlock at the end of this call
808     */
809 iliev 2012 std::set<EngineChannel*> InstrumentResourceManager::GetEngineChannelsUsing(::gig::Instrument* pInstrument, bool bLock) {
810 schoenebeck 1659 if (bLock) Lock();
811 iliev 2012 std::set<EngineChannel*> result;
812 schoenebeck 1659 std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(pInstrument);
813     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
814     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
815     for (; iter != end; ++iter) {
816 iliev 2012 EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(*iter);
817 schoenebeck 1659 if (!pEngineChannel) continue;
818     result.insert(pEngineChannel);
819     }
820     if (bLock) Unlock();
821     return result;
822     }
823    
824     /**
825 schoenebeck 1321 * Returns a list with all gig Engines that are currently using the given
826     * instrument.
827     *
828     * @param pInstrument - search criteria
829     * @param bLock - whether we should lock (mutex) the instrument manager
830     * during this call and unlock at the end of this call
831     */
832 iliev 2012 std::set<Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::Instrument* pInstrument, bool bLock) {
833 schoenebeck 1321 if (bLock) Lock();
834 iliev 2012 std::set<Engine*> result;
835 schoenebeck 1321 std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(pInstrument);
836     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
837     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
838     for (; iter != end; ++iter) {
839 iliev 2012 EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(*iter);
840 schoenebeck 1321 if (!pEngineChannel) continue;
841 iliev 2012 Engine* pEngine = dynamic_cast<Engine*>(pEngineChannel->GetEngine());
842 schoenebeck 1321 if (!pEngine) continue;
843     result.insert(pEngine);
844     }
845     if (bLock) Unlock();
846     return result;
847     }
848    
849     /**
850     * Returns a list with all gig Engines that are currently using an
851     * instrument that is part of the given instrument file.
852     *
853     * @param pFile - search criteria
854     * @param bLock - whether we should lock (mutex) the instrument manager
855     * during this call and unlock at the end of this call
856     */
857 iliev 2012 std::set<Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::File* pFile, bool bLock) {
858 schoenebeck 1321 if (bLock) Lock();
859     // get all instruments (currently in usage) that use that same gig::File
860     std::vector< ::gig::Instrument*> instrumentsOfInterest =
861     GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
862    
863     // get all engines that use that same gig::File
864 iliev 2012 std::set<Engine*> result;
865 schoenebeck 1321 {
866     for (int i = 0; i < instrumentsOfInterest.size(); i++) {
867     std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(instrumentsOfInterest[i]);
868     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
869     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
870     for (; iter != end; ++iter) {
871 iliev 2012 EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(*iter);
872 schoenebeck 1321 if (!pEngineChannel) continue;
873 iliev 2012 Engine* pEngine = dynamic_cast<Engine*>(pEngineChannel->GetEngine());
874 schoenebeck 1321 if (!pEngine) continue;
875     // the unique, sorted container std::set makes
876     // sure we won't have duplicates
877     result.insert(pEngine);
878     }
879     }
880     }
881     if (bLock) Unlock();
882     return result;
883     }
884    
885     /**
886     * Returns @c true in case the given sample is referenced somewhere by the
887     * given instrument, @c false otherwise.
888     *
889     * @param pSample - sample reference
890     * @param pInstrument - instrument that might use that sample
891     */
892     bool InstrumentResourceManager::SampleReferencedByInstrument(::gig::Sample* pSample, ::gig::Instrument* pInstrument) {
893 schoenebeck 3966 size_t iRgn = 0;
894 schoenebeck 1321 for (
895 schoenebeck 3966 ::gig::Region* pRegion = pInstrument->GetRegionAt(iRgn);
896     pRegion; pRegion = pInstrument->GetRegionAt(++iRgn)
897 schoenebeck 1321 ) {
898     for (
899     int i = 0; i < pRegion->DimensionRegions &&
900     pRegion->pDimensionRegions[i]; i++
901     ) {
902     if (pRegion->pDimensionRegions[i]->pSample == pSample)
903     return true;
904     }
905     }
906     return false;
907     }
908    
909     /**
910     * Suspend all gig engines that use the given instrument. This means
911     * completely stopping playback on those engines and killing all their
912     * voices and disk streams. This method will block until all voices AND
913     * their disk streams are finally deleted and the engine turned into a
914     * complete idle loop.
915     *
916     * All @c SuspendEnginesUsing() methods only serve one thread by one and
917     * block all other threads until the current active thread called
918     * @c ResumeAllEngines() .
919     *
920     * @param pInstrument - search criteria
921     */
922     void InstrumentResourceManager::SuspendEnginesUsing(::gig::Instrument* pInstrument) {
923     // make sure no other thread suspends whole engines at the same time
924     suspendedEnginesMutex.Lock();
925     // get all engines that use that same gig::Instrument
926     suspendedEngines = GetEnginesUsing(pInstrument, true/*lock*/);
927     // finally, completely suspend all engines that use that same gig::Instrument
928 iliev 2012 std::set<Engine*>::iterator iter = suspendedEngines.begin();
929     std::set<Engine*>::iterator end = suspendedEngines.end();
930 schoenebeck 1321 for (; iter != end; ++iter) (*iter)->SuspendAll();
931     }
932    
933     /**
934     * Suspend all gig engines that use the given instrument file. This means
935     * completely stopping playback on those engines and killing all their
936     * voices and disk streams. This method will block until all voices AND
937     * their disk streams are finally deleted and the engine turned into a
938     * complete idle loop.
939     *
940     * All @c SuspendEnginesUsing() methods only serve one thread by one and
941     * block all other threads until the current active thread called
942     * @c ResumeAllEngines() .
943     *
944     * @param pFile - search criteria
945     */
946     void InstrumentResourceManager::SuspendEnginesUsing(::gig::File* pFile) {
947     // make sure no other thread suspends whole engines at the same time
948     suspendedEnginesMutex.Lock();
949     // get all engines that use that same gig::File
950     suspendedEngines = GetEnginesUsing(pFile, true/*lock*/);
951     // finally, completely suspend all engines that use that same gig::File
952 iliev 2012 std::set<Engine*>::iterator iter = suspendedEngines.begin();
953     std::set<Engine*>::iterator end = suspendedEngines.end();
954 schoenebeck 1321 for (; iter != end; ++iter) (*iter)->SuspendAll();
955     }
956    
957     /**
958     * MUST be called after one called one of the @c SuspendEnginesUsing()
959     * methods, to resume normal playback on all previously suspended engines.
960     * As it's only possible for one thread to suspend whole engines at the
961     * same time, this method doesn't take any arguments.
962     */
963     void InstrumentResourceManager::ResumeAllEngines() {
964     // resume all previously completely suspended engines
965     std::set<Engine*>::iterator iter = suspendedEngines.begin();
966     std::set<Engine*>::iterator end = suspendedEngines.end();
967     for (; iter != end; ++iter) (*iter)->ResumeAll();
968     // no more suspended engines ...
969     suspendedEngines.clear();
970     // allow another thread to suspend whole engines
971     suspendedEnginesMutex.Unlock();
972     }
973    
974    
975    
976 schoenebeck 53 // internal gig file manager
977    
978     ::gig::File* InstrumentResourceManager::GigResourceManager::Create(String Key, GigConsumer* pConsumer, void*& pArg) {
979     dmsg(1,("Loading gig file \'%s\'...", Key.c_str()));
980     ::RIFF::File* pRIFF = new ::RIFF::File(Key);
981 schoenebeck 3917 // due to the multi-threaded scenario use separate file I/O handles for
982     // each thread to avoid file I/O concurrency issues with .gig file
983     pRIFF->SetIOPerThread(true);
984    
985 schoenebeck 53 ::gig::File* pGig = new ::gig::File(pRIFF);
986     pArg = pRIFF;
987     dmsg(1,("OK\n"));
988     return pGig;
989     }
990    
991     void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
992 schoenebeck 2275 dmsg(1,("Freeing gig file '%s' from memory ...", pResource->GetFileName().c_str()));
993 persson 1038
994     // Delete as much as possible of the gig file. Some of the
995     // dimension regions and samples may still be in use - these
996     // will be deleted later by the HandBackDimReg function.
997     bool deleteFile = true;
998 schoenebeck 3967 ssize_t iInstr = pResource->CountInstruments();
999     for (--iInstr; iInstr >= 0; --iInstr) {
1000     ::gig::Instrument* instrument = pResource->GetInstrument(iInstr);
1001 persson 1038 bool deleteInstrument = true;
1002 schoenebeck 3966 ssize_t iRgn = instrument->CountRegions();
1003     for (--iRgn; iRgn >= 0; --iRgn) {
1004     ::gig::Region* region = instrument->GetRegionAt(iRgn);
1005 persson 1038 bool deleteRegion = true;
1006     for (int i = 0 ; i < region->DimensionRegions ; i++)
1007     {
1008     ::gig::DimensionRegion *d = region->pDimensionRegions[i];
1009 iliev 2012 std::map< ::gig::DimensionRegion*, region_info_t>::iterator iter = parent->RegionInfo.find(d);
1010     if (iter != parent->RegionInfo.end()) {
1011     region_info_t& dimRegInfo = (*iter).second;
1012 persson 1038 dimRegInfo.file = pResource;
1013 iliev 2012 dimRegInfo.pArg = (::RIFF::File*)pArg;
1014 persson 1038 deleteFile = deleteInstrument = deleteRegion = false;
1015     }
1016     }
1017     if (deleteRegion) instrument->DeleteRegion(region);
1018     }
1019     if (deleteInstrument) pResource->DeleteInstrument(instrument);
1020     }
1021     if (deleteFile) {
1022     delete pResource;
1023     delete (::RIFF::File*) pArg;
1024     } else {
1025     dmsg(2,("keeping some samples that are in use..."));
1026 schoenebeck 3964 ssize_t i = pResource->CountSamples();
1027     for (--i; i >= 0; --i) {
1028     ::gig::Sample* sample = pResource->GetSample(i);
1029 persson 1038 if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
1030     pResource->DeleteSample(sample);
1031     }
1032     }
1033     }
1034 schoenebeck 53 dmsg(1,("OK\n"));
1035     }
1036    
1037     }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC