/[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 1852 - (hide annotations) (download)
Sun Mar 1 22:22:03 2009 UTC (15 years, 1 month ago) by schoenebeck
File size: 49025 byte(s)
* bugfix: on sample reference changes (instrument editor), only un-cache
  the respective sample if it's really not used by any sampler engine
  anymore
* re-cache samples in case they were changed by an instrument editor, e.g.
  when a sample was added while playing (#82)
* bumped version to 0.5.1.10cvs

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

  ViewVC Help
Powered by ViewVC