/[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 1646 - (hide annotations) (download)
Sun Jan 20 15:04:51 2008 UTC (16 years, 2 months ago) by persson
File size: 39442 byte(s)
* made it possible to load an instrument even if the audio thread
  isn't running

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 persson 1646 * Copyright (C) 2005 - 2008 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     };
71    
72 schoenebeck 517 /**
73     * Callback function which will be called by libgig during loading of
74     * instruments to inform about the current progress. Or to be more
75     * specific; it will be called during the GetInstrument() call.
76     *
77     * @param pProgress - contains current progress value, pointer to the
78     * InstrumentResourceManager instance and
79     * instrument ID
80     */
81     void InstrumentResourceManager::OnInstrumentLoadingProgress(::gig::progress_t* pProgress) {
82     dmsg(7,("gig::InstrumentResourceManager: progress %f%", pProgress->factor));
83     progress_callback_arg_t* pArg = static_cast<progress_callback_arg_t*>(pProgress->custom);
84     // we randomly schedule 90% for the .gig file loading and the remaining 10% later for sample caching
85     const float localProgress = 0.9f * pProgress->factor;
86     pArg->pManager->DispatchResourceProgressEvent(*pArg->pInstrumentKey, localProgress);
87     }
88    
89 schoenebeck 947 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::Instruments() {
90     return Entries();
91     }
92    
93     InstrumentManager::mode_t InstrumentResourceManager::GetMode(const instrument_id_t& ID) {
94     return static_cast<InstrumentManager::mode_t>(AvailabilityMode(ID));
95     }
96    
97     void InstrumentResourceManager::SetMode(const instrument_id_t& ID, InstrumentManager::mode_t Mode) {
98     dmsg(2,("gig::InstrumentResourceManager: setting mode for %s (Index=%d) to %d\n",ID.FileName.c_str(),ID.Index,Mode));
99     SetAvailabilityMode(ID, static_cast<ResourceManager<InstrumentManager::instrument_id_t, ::gig::Instrument>::mode_t>(Mode));
100     }
101    
102     String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
103 schoenebeck 970 Lock();
104     ::gig::Instrument* pInstrument = Resource(ID, false);
105     String res = (pInstrument) ? pInstrument->pInfo->Name : "";
106     Unlock();
107     return res;
108 schoenebeck 947 }
109    
110 schoenebeck 1321 String InstrumentResourceManager::GetInstrumentDataStructureName(instrument_id_t ID) {
111 schoenebeck 1212 return ::gig::libraryName();
112     }
113    
114 schoenebeck 1321 String InstrumentResourceManager::GetInstrumentDataStructureVersion(instrument_id_t ID) {
115 schoenebeck 1212 return ::gig::libraryVersion();
116     }
117    
118 schoenebeck 1525 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::GetInstrumentFileContent(String File) throw (InstrumentManagerException) {
119     ::RIFF::File* riff = NULL;
120     ::gig::File* gig = NULL;
121     try {
122     std::vector<instrument_id_t> result;
123     riff = new ::RIFF::File(File);
124     gig = new ::gig::File(riff);
125     gig->SetAutoLoad(false); // avoid time consuming samples scanning
126     for (int i = 0; gig->GetInstrument(i); i++) {
127     instrument_id_t id;
128     id.FileName = File;
129     id.Index = i;
130     result.push_back(id);
131     }
132     if (gig) delete gig;
133     if (riff) delete riff;
134     return result;
135     } catch (::RIFF::Exception e) {
136     if (gig) delete gig;
137     if (riff) delete riff;
138     throw InstrumentManagerException(e.Message);
139     } catch (...) {
140     if (gig) delete gig;
141     if (riff) delete riff;
142     throw InstrumentManagerException("Unknown exception while trying to parse '" + File + "'");
143     }
144     }
145    
146     InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) {
147     std::vector<instrument_id_t> result;
148     ::RIFF::File* riff = NULL;
149     ::gig::File* gig = NULL;
150     try {
151     riff = new ::RIFF::File(ID.FileName);
152     gig = new ::gig::File(riff);
153     gig->SetAutoLoad(false); // avoid time consuming samples scanning
154     ::gig::Instrument* pInstrument = gig->GetInstrument(ID.Index);
155     if (!pInstrument) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName);
156     instrument_info_t info;
157     if (gig->pVersion) {
158     info.FormatVersion = ToString(gig->pVersion->major);
159     info.Product = gig->pInfo->Product;
160     info.Artists = gig->pInfo->Artists;
161     }
162     info.InstrumentName = pInstrument->pInfo->Name;
163     if (gig) delete gig;
164     if (riff) delete riff;
165     return info;
166     } catch (::RIFF::Exception e) {
167     if (gig) delete gig;
168     if (riff) delete riff;
169     throw InstrumentManagerException(e.Message);
170     } catch (...) {
171     if (gig) delete gig;
172     if (riff) delete riff;
173     throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'");
174     }
175     }
176    
177 schoenebeck 1212 void InstrumentResourceManager::LaunchInstrumentEditor(instrument_id_t ID) throw (InstrumentManagerException) {
178 schoenebeck 1321 const String sDataType = GetInstrumentDataStructureName(ID);
179     const String sDataVersion = GetInstrumentDataStructureVersion(ID);
180 schoenebeck 1212 // find instrument editors capable to handle given instrument
181     std::vector<String> vEditors =
182     InstrumentEditorFactory::MatchingEditors(sDataType, sDataVersion);
183     if (!vEditors.size())
184     throw InstrumentManagerException(
185     "There is no instrument editor capable to handle this instrument"
186     );
187     // simply use the first editor in the result set
188     dmsg(1,("Found matching editor '%s' for instrument ('%s', %d) having data structure ('%s','%s')\n",
189     vEditors[0].c_str(), ID.FileName.c_str(), ID.Index, sDataType.c_str(), sDataVersion.c_str()));
190     InstrumentEditor* pEditor = InstrumentEditorFactory::Create(vEditors[0]);
191 schoenebeck 1321 // register for receiving notifications from the instrument editor
192 schoenebeck 1212 pEditor->AddListener(this);
193     // create a proxy that reacts on notification on behalf of the editor
194     InstrumentEditorProxy* pProxy = new InstrumentEditorProxy;
195     // borrow the instrument on behalf of the instrument editor
196     ::gig::Instrument* pInstrument = Borrow(ID, pProxy);
197     // remember the proxy and instrument for this instrument editor
198     pProxy->pInstrument = pInstrument;
199     InstrumentEditorProxiesMutex.Lock();
200     InstrumentEditorProxies[pEditor] = pProxy;
201     InstrumentEditorProxiesMutex.Unlock();
202     // launch the instrument editor for the given instrument
203     pEditor->Launch(pInstrument, sDataType, sDataVersion);
204     }
205    
206     /**
207     * Will be called by the respective instrument editor once it left its
208     * Main() loop. That way we can handle cleanup before its thread finally
209     * dies.
210     *
211     * @param pSender - instrument editor that stops execution
212     */
213     void InstrumentResourceManager::OnInstrumentEditorQuit(InstrumentEditor* pSender) {
214     dmsg(1,("InstrumentResourceManager: instrument editor quit, doing cleanup\n"));
215     // hand back instrument and free proxy
216     InstrumentEditorProxiesMutex.Lock();
217     if (InstrumentEditorProxies.count(pSender)) {
218     InstrumentEditorProxy* pProxy =
219     dynamic_cast<InstrumentEditorProxy*>(
220     InstrumentEditorProxies[pSender]
221     );
222     InstrumentEditorProxies.erase(pSender);
223     InstrumentEditorProxiesMutex.Unlock();
224     HandBack(pProxy->pInstrument, pProxy);
225     if (pProxy) delete pProxy;
226     } else {
227     InstrumentEditorProxiesMutex.Unlock();
228     std::cerr << "Eeeek, could not find instrument editor proxy, this is a bug!\n" << std::flush;
229     }
230 persson 1455
231     // Note that we don't need to free the editor here. As it
232     // derives from Thread, it will delete itself when the thread
233     // dies.
234 schoenebeck 1212 }
235    
236 schoenebeck 1321 void InstrumentResourceManager::OnSamplesToBeRemoved(std::set<void*> Samples, InstrumentEditor* pSender) {
237     if (Samples.empty()) {
238     std::cerr << "gig::InstrumentResourceManager: WARNING, "
239     "OnSamplesToBeRemoved() called with empty list, this "
240     "is a bug!\n" << std::flush;
241     return;
242     }
243     // TODO: ATM we assume here that all samples are from the same file
244     ::gig::Sample* pFirstSample = (::gig::Sample*) *Samples.begin();
245     ::gig::File* pCriticalFile = dynamic_cast< ::gig::File*>(pFirstSample->GetParent());
246     // completely suspend all engines that use that same file
247     SuspendEnginesUsing(pCriticalFile);
248     }
249    
250     void InstrumentResourceManager::OnSamplesRemoved(InstrumentEditor* pSender) {
251     // resume all previously, completely suspended engines
252     // (we don't have to un-cache the removed samples here, since that is
253     // automatically done by the gig::Sample destructor)
254     ResumeAllEngines();
255     }
256    
257     void InstrumentResourceManager::OnDataStructureToBeChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
258     //TODO: remove code duplication
259     if (sStructType == "gig::File") {
260     // completely suspend all engines that use that file
261     ::gig::File* pFile = (::gig::File*) pStruct;
262     SuspendEnginesUsing(pFile);
263     } else if (sStructType == "gig::Instrument") {
264     // completely suspend all engines that use that instrument
265     ::gig::Instrument* pInstrument = (::gig::Instrument*) pStruct;
266     SuspendEnginesUsing(pInstrument);
267     } else if (sStructType == "gig::Region") {
268     // only advice the engines to suspend the given region, so they'll
269     // only ignore that region (and probably already other suspended
270     // ones), but beside that continue normal playback
271     ::gig::Region* pRegion = (::gig::Region*) pStruct;
272     ::gig::Instrument* pInstrument =
273     (::gig::Instrument*) pRegion->GetParent();
274     Lock();
275     std::set<gig::Engine*> engines =
276     GetEnginesUsing(pInstrument, false/*don't lock again*/);
277     std::set<gig::Engine*>::iterator iter = engines.begin();
278     std::set<gig::Engine*>::iterator end = engines.end();
279     for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
280     Unlock();
281     } else if (sStructType == "gig::DimensionRegion") {
282     // only advice the engines to suspend the given DimensionRegions's
283     // parent region, so they'll only ignore that region (and probably
284     // already other suspended ones), but beside that continue normal
285     // playback
286     ::gig::DimensionRegion* pDimReg =
287     (::gig::DimensionRegion*) pStruct;
288     ::gig::Region* pRegion = pDimReg->GetParent();
289     ::gig::Instrument* pInstrument =
290     (::gig::Instrument*) pRegion->GetParent();
291     Lock();
292     std::set<gig::Engine*> engines =
293     GetEnginesUsing(pInstrument, false/*don't lock again*/);
294     std::set<gig::Engine*>::iterator iter = engines.begin();
295     std::set<gig::Engine*>::iterator end = engines.end();
296     for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
297     Unlock();
298     } else {
299     std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
300     "structure '" << sStructType << "' requested to be "
301     "suspended by instrument editor. This is a bug!\n"
302     << std::flush;
303     //TODO: we should inform the instrument editor that something seriously went wrong
304     }
305     }
306    
307     void InstrumentResourceManager::OnDataStructureChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
308     //TODO: remove code duplication
309     if (sStructType == "gig::File") {
310     // resume all previously suspended engines
311     ResumeAllEngines();
312     } else if (sStructType == "gig::Instrument") {
313     // resume all previously suspended engines
314     ResumeAllEngines();
315     } else if (sStructType == "gig::Region") {
316     // advice the engines to resume the given region, that is to
317     // using it for playback again
318     ::gig::Region* pRegion = (::gig::Region*) pStruct;
319     ::gig::Instrument* pInstrument =
320     (::gig::Instrument*) pRegion->GetParent();
321     Lock();
322     std::set<gig::Engine*> engines =
323     GetEnginesUsing(pInstrument, false/*don't lock again*/);
324     std::set<gig::Engine*>::iterator iter = engines.begin();
325     std::set<gig::Engine*>::iterator end = engines.end();
326     for (; iter != end; ++iter) (*iter)->Resume(pRegion);
327     Unlock();
328     } else if (sStructType == "gig::DimensionRegion") {
329     // advice the engines to resume the given DimensionRegion's parent
330     // region, that is to using it for playback again
331     ::gig::DimensionRegion* pDimReg =
332     (::gig::DimensionRegion*) pStruct;
333     ::gig::Region* pRegion = pDimReg->GetParent();
334     ::gig::Instrument* pInstrument =
335     (::gig::Instrument*) pRegion->GetParent();
336     Lock();
337     std::set<gig::Engine*> engines =
338     GetEnginesUsing(pInstrument, false/*don't lock again*/);
339     std::set<gig::Engine*>::iterator iter = engines.begin();
340     std::set<gig::Engine*>::iterator end = engines.end();
341     for (; iter != end; ++iter) (*iter)->Resume(pRegion);
342     Unlock();
343     } else {
344     std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
345     "structure '" << sStructType << "' requested to be "
346     "resumed by instrument editor. This is a bug!\n"
347     << std::flush;
348     //TODO: we should inform the instrument editor that something seriously went wrong
349     }
350     }
351    
352     void InstrumentResourceManager::OnSampleReferenceChanged(void* pOldSample, void* pNewSample, InstrumentEditor* pSender) {
353     // uncache old sample in case it's not used by anybody anymore
354     if (pOldSample) {
355     Lock();
356     ::gig::Sample* pSample = (::gig::Sample*) pOldSample;
357     ::gig::File* pFile = (::gig::File*) pSample->GetParent();
358     std::vector< ::gig::Instrument*> instruments =
359     GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
360     for (int i = 0; i < instruments.size(); i++)
361     if (!SampleReferencedByInstrument(pSample, instruments[i]))
362     UncacheInitialSamples(pSample);
363     Unlock();
364     }
365     // make sure new sample reference is cached
366     if (pNewSample) {
367     Lock();
368     ::gig::Sample* pSample = (::gig::Sample*) pNewSample;
369     ::gig::File* pFile = (::gig::File*) pSample->GetParent();
370     // get all engines that use that same gig::File
371     std::set<gig::Engine*> engines = GetEnginesUsing(pFile, false/*don't lock again*/);
372     std::set<gig::Engine*>::iterator iter = engines.begin();
373     std::set<gig::Engine*>::iterator end = engines.end();
374     for (; iter != end; ++iter)
375     CacheInitialSamples(pSample, *iter);
376     Unlock();
377     }
378     }
379    
380 schoenebeck 53 ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
381 schoenebeck 1321 // get gig file from internal gig file manager
382 schoenebeck 947 ::gig::File* pGig = Gigs.Borrow(Key.FileName, (GigConsumer*) Key.Index); // conversion kinda hackish :/
383 schoenebeck 53
384 schoenebeck 517 // we pass this to the progress callback mechanism of libgig
385     progress_callback_arg_t callbackArg;
386     callbackArg.pManager = this;
387     callbackArg.pInstrumentKey = &Key;
388    
389     ::gig::progress_t progress;
390     progress.callback = OnInstrumentLoadingProgress;
391     progress.custom = &callbackArg;
392    
393 schoenebeck 947 dmsg(1,("Loading gig instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
394     ::gig::Instrument* pInstrument = pGig->GetInstrument(Key.Index, &progress);
395 schoenebeck 53 if (!pInstrument) {
396     std::stringstream msg;
397 schoenebeck 947 msg << "There's no instrument with index " << Key.Index << ".";
398 schoenebeck 1212 throw InstrumentManagerException(msg.str());
399 schoenebeck 53 }
400     pGig->GetFirstSample(); // just to force complete instrument loading
401     dmsg(1,("OK\n"));
402    
403     // cache initial samples points (for actually needed samples)
404     dmsg(1,("Caching initial samples..."));
405 schoenebeck 517 uint iRegion = 0; // just for progress calculation
406 schoenebeck 53 ::gig::Region* pRgn = pInstrument->GetFirstRegion();
407     while (pRgn) {
408 schoenebeck 517 // we randomly schedule 90% for the .gig file loading and the remaining 10% now for sample caching
409     const float localProgress = 0.9f + 0.1f * (float) iRegion / (float) pInstrument->Regions;
410 schoenebeck 947 DispatchResourceProgressEvent(Key, localProgress);
411    
412 schoenebeck 354 if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {
413 schoenebeck 53 dmsg(2,("C"));
414 schoenebeck 947 CacheInitialSamples(pRgn->GetSample(), (gig::EngineChannel*) pConsumer);
415 schoenebeck 53 }
416     for (uint i = 0; i < pRgn->DimensionRegions; i++) {
417 schoenebeck 947 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, (gig::EngineChannel*) pConsumer);
418 schoenebeck 53 }
419    
420     pRgn = pInstrument->GetNextRegion();
421 schoenebeck 517 iRegion++;
422 schoenebeck 53 }
423     dmsg(1,("OK\n"));
424 schoenebeck 517 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
425 schoenebeck 53
426     // we need the following for destruction later
427     instr_entry_t* pEntry = new instr_entry_t;
428 schoenebeck 947 pEntry->ID.FileName = Key.FileName;
429     pEntry->ID.Index = Key.Index;
430 schoenebeck 53 pEntry->pGig = pGig;
431 persson 438
432 schoenebeck 1212 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
433 schoenebeck 411 // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'
434     pEntry->MaxSamplesPerCycle =
435 schoenebeck 1212 (!pEngineChannel) ? 0 /* don't care for instrument editors */ :
436     (pEngineChannel->GetEngine()) ?
437     dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
438     : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
439 schoenebeck 53 pArg = pEntry;
440    
441     return pInstrument;
442     }
443    
444     void InstrumentResourceManager::Destroy( ::gig::Instrument* pResource, void* pArg) {
445     instr_entry_t* pEntry = (instr_entry_t*) pArg;
446 schoenebeck 947 // we don't need the .gig file here anymore
447     Gigs.HandBack(pEntry->pGig, (GigConsumer*) pEntry->ID.Index); // conversion kinda hackish :/
448 schoenebeck 53 delete pEntry;
449     }
450    
451     void InstrumentResourceManager::OnBorrow(::gig::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) {
452     instr_entry_t* pEntry = (instr_entry_t*) pArg;
453 schoenebeck 411 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
454     uint maxSamplesPerCycle =
455 schoenebeck 1212 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
456 schoenebeck 411 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
457     if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
458 schoenebeck 53 Update(pResource, pConsumer);
459     }
460     }
461    
462     /**
463 persson 1038 * Give back an instrument. This should be used instead of
464     * HandBack if there are some dimension regions that are still in
465     * use. (When an instrument is changed, the voices currently
466 persson 1646 * playing are allowed to keep playing with the old instrument
467 persson 1038 * until note off arrives. New notes will use the new instrument.)
468     */
469     void InstrumentResourceManager::HandBackInstrument(::gig::Instrument* pResource, InstrumentConsumer* pConsumer,
470 persson 1646 RTList< ::gig::DimensionRegion*>* pDimRegionsInUse) {
471 persson 1038 DimRegInfoMutex.Lock();
472 persson 1646 for (RTList< ::gig::DimensionRegion*>::Iterator i = pDimRegionsInUse->first() ; i != pDimRegionsInUse->end() ; i++) {
473     DimRegInfo[*i].refCount++;
474     SampleRefCount[(*i)->pSample]++;
475 persson 1038 }
476     HandBack(pResource, pConsumer, true);
477     DimRegInfoMutex.Unlock();
478     }
479    
480     /**
481     * Give back a dimension region that belongs to an instrument that
482     * was previously handed back.
483     */
484     void InstrumentResourceManager::HandBackDimReg(::gig::DimensionRegion* pDimReg) {
485     DimRegInfoMutex.Lock();
486     dimreg_info_t& dimRegInfo = DimRegInfo[pDimReg];
487     int dimRegRefCount = --dimRegInfo.refCount;
488     int sampleRefCount = --SampleRefCount[pDimReg->pSample];
489     if (dimRegRefCount == 0) {
490     ::gig::File* gig = dimRegInfo.file;
491     ::RIFF::File* riff = dimRegInfo.riff;
492     DimRegInfo.erase(pDimReg);
493     // TODO: we could delete Region and Instrument here if
494     // they have become unused
495    
496     if (sampleRefCount == 0) {
497     SampleRefCount.erase(pDimReg->pSample);
498    
499     if (gig) {
500     gig->DeleteSample(pDimReg->pSample);
501     if (!gig->GetFirstSample()) {
502     dmsg(2,("No more samples in use - freeing gig\n"));
503     delete gig;
504     delete riff;
505     }
506     }
507     }
508     }
509     DimRegInfoMutex.Unlock();
510     }
511    
512     /**
513 schoenebeck 1321 * Just a wrapper around the other @c CacheInitialSamples() method.
514     *
515     * @param pSample - points to the sample to be cached
516     * @param pEngine - pointer to Gig Engine Channel which caused this call
517     * (may be NULL, in this case default amount of samples
518     * will be cached)
519     */
520     void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::EngineChannel* pEngineChannel) {
521     gig::Engine* pEngine =
522     (pEngineChannel && pEngineChannel->GetEngine()) ?
523     dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine()) : NULL;
524     CacheInitialSamples(pSample, pEngine);
525     }
526    
527     /**
528 schoenebeck 53 * Caches a certain size at the beginning of the given sample in RAM. If the
529     * sample is very short, the whole sample will be loaded into RAM and thus
530     * no disk streaming is needed for this sample. Caching an initial part of
531     * samples is needed to compensate disk reading latency.
532     *
533     * @param pSample - points to the sample to be cached
534 schoenebeck 1321 * @param pEngine - pointer to Gig Engine which caused this call
535     * (may be NULL, in this case default amount of samples
536     * will be cached)
537 schoenebeck 53 */
538 schoenebeck 1321 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::Engine* pEngine) {
539 schoenebeck 420 if (!pSample) {
540 persson 438 dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
541 schoenebeck 420 return;
542     }
543     if (!pSample->SamplesTotal) return; // skip zero size samples
544 persson 438
545 schoenebeck 554 if (pSample->SamplesTotal <= CONFIG_PRELOAD_SAMPLES) {
546 schoenebeck 53 // Sample is too short for disk streaming, so we load the whole
547 schoenebeck 554 // sample into RAM and place 'pAudioIO->FragmentSize << CONFIG_MAX_PITCH'
548 schoenebeck 53 // number of '0' samples (silence samples) behind the official buffer
549     // border, to allow the interpolator do it's work even at the end of
550 persson 438 // the sample.
551 schoenebeck 411 const uint maxSamplesPerCycle =
552 schoenebeck 1321 (pEngine) ? pEngine->pAudioOutputDevice->MaxSamplesPerCycle()
553     : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
554 schoenebeck 554 const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;
555 schoenebeck 420 const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;
556     if (currentlyCachedSilenceSamples < neededSilenceSamples) {
557     dmsg(3,("Caching whole sample (sample name: \"%s\", sample size: %d)\n", pSample->pInfo->Name.c_str(), pSample->SamplesTotal));
558     ::gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(neededSilenceSamples);
559     dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
560     }
561 schoenebeck 53 }
562 schoenebeck 554 else { // we only cache CONFIG_PRELOAD_SAMPLES and stream the other sample points from disk
563     if (!pSample->GetCache().Size) pSample->LoadSampleData(CONFIG_PRELOAD_SAMPLES);
564 schoenebeck 53 }
565    
566     if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
567     }
568    
569 schoenebeck 1321 void InstrumentResourceManager::UncacheInitialSamples(::gig::Sample* pSample) {
570     dmsg(1,("Uncaching sample %x\n",pSample));
571     if (pSample->GetCache().Size) pSample->ReleaseSampleData();
572     }
573 schoenebeck 53
574 schoenebeck 1321 /**
575     * Returns a list with all instruments currently in use, that are part of
576     * the given file.
577     *
578     * @param pFile - search criteria
579     * @param bLock - whether we should lock (mutex) the instrument manager
580     * during this call and unlock at the end of this call
581     */
582     std::vector< ::gig::Instrument*> InstrumentResourceManager::GetInstrumentsCurrentlyUsedOf(::gig::File* pFile, bool bLock) {
583     if (bLock) Lock();
584     std::vector< ::gig::Instrument*> result;
585     std::vector< ::gig::Instrument*> allInstruments = Resources(false/*don't lock again*/);
586     for (int i = 0; i < allInstruments.size(); i++)
587     if (
588     (::gig::File*) allInstruments[i]->GetParent()
589     == pFile
590     ) result.push_back(allInstruments[i]);
591     if (bLock) Unlock();
592     return result;
593     }
594 schoenebeck 53
595 schoenebeck 1321 /**
596     * Returns a list with all gig Engines that are currently using the given
597     * instrument.
598     *
599     * @param pInstrument - search criteria
600     * @param bLock - whether we should lock (mutex) the instrument manager
601     * during this call and unlock at the end of this call
602     */
603     std::set<gig::Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::Instrument* pInstrument, bool bLock) {
604     if (bLock) Lock();
605     std::set<gig::Engine*> result;
606     std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(pInstrument);
607     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
608     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
609     for (; iter != end; ++iter) {
610     gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(*iter);
611     if (!pEngineChannel) continue;
612     gig::Engine* pEngine = dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine());
613     if (!pEngine) continue;
614     result.insert(pEngine);
615     }
616     if (bLock) Unlock();
617     return result;
618     }
619    
620     /**
621     * Returns a list with all gig Engines that are currently using an
622     * instrument that is part of the given instrument file.
623     *
624     * @param pFile - search criteria
625     * @param bLock - whether we should lock (mutex) the instrument manager
626     * during this call and unlock at the end of this call
627     */
628     std::set<gig::Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::File* pFile, bool bLock) {
629     if (bLock) Lock();
630     // get all instruments (currently in usage) that use that same gig::File
631     std::vector< ::gig::Instrument*> instrumentsOfInterest =
632     GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
633    
634     // get all engines that use that same gig::File
635     std::set<gig::Engine*> result;
636     {
637     for (int i = 0; i < instrumentsOfInterest.size(); i++) {
638     std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(instrumentsOfInterest[i]);
639     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
640     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
641     for (; iter != end; ++iter) {
642     gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(*iter);
643     if (!pEngineChannel) continue;
644     gig::Engine* pEngine = dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine());
645     if (!pEngine) continue;
646     // the unique, sorted container std::set makes
647     // sure we won't have duplicates
648     result.insert(pEngine);
649     }
650     }
651     }
652     if (bLock) Unlock();
653     return result;
654     }
655    
656     /**
657     * Returns @c true in case the given sample is referenced somewhere by the
658     * given instrument, @c false otherwise.
659     *
660     * @param pSample - sample reference
661     * @param pInstrument - instrument that might use that sample
662     */
663     bool InstrumentResourceManager::SampleReferencedByInstrument(::gig::Sample* pSample, ::gig::Instrument* pInstrument) {
664     for (
665     ::gig::Region* pRegion = pInstrument->GetFirstRegion();
666     pRegion; pRegion = pInstrument->GetNextRegion()
667     ) {
668     for (
669     int i = 0; i < pRegion->DimensionRegions &&
670     pRegion->pDimensionRegions[i]; i++
671     ) {
672     if (pRegion->pDimensionRegions[i]->pSample == pSample)
673     return true;
674     }
675     }
676     return false;
677     }
678    
679     /**
680     * Suspend all gig engines that use the given instrument. This means
681     * completely stopping playback on those engines and killing all their
682     * voices and disk streams. This method will block until all voices AND
683     * their disk streams are finally deleted and the engine turned into a
684     * complete idle loop.
685     *
686     * All @c SuspendEnginesUsing() methods only serve one thread by one and
687     * block all other threads until the current active thread called
688     * @c ResumeAllEngines() .
689     *
690     * @param pInstrument - search criteria
691     */
692     void InstrumentResourceManager::SuspendEnginesUsing(::gig::Instrument* pInstrument) {
693     // make sure no other thread suspends whole engines at the same time
694     suspendedEnginesMutex.Lock();
695     // get all engines that use that same gig::Instrument
696     suspendedEngines = GetEnginesUsing(pInstrument, true/*lock*/);
697     // finally, completely suspend all engines that use that same gig::Instrument
698     std::set<gig::Engine*>::iterator iter = suspendedEngines.begin();
699     std::set<gig::Engine*>::iterator end = suspendedEngines.end();
700     for (; iter != end; ++iter) (*iter)->SuspendAll();
701     }
702    
703     /**
704     * Suspend all gig engines that use the given instrument file. This means
705     * completely stopping playback on those engines and killing all their
706     * voices and disk streams. This method will block until all voices AND
707     * their disk streams are finally deleted and the engine turned into a
708     * complete idle loop.
709     *
710     * All @c SuspendEnginesUsing() methods only serve one thread by one and
711     * block all other threads until the current active thread called
712     * @c ResumeAllEngines() .
713     *
714     * @param pFile - search criteria
715     */
716     void InstrumentResourceManager::SuspendEnginesUsing(::gig::File* pFile) {
717     // make sure no other thread suspends whole engines at the same time
718     suspendedEnginesMutex.Lock();
719     // get all engines that use that same gig::File
720     suspendedEngines = GetEnginesUsing(pFile, true/*lock*/);
721     // finally, completely suspend all engines that use that same gig::File
722     std::set<gig::Engine*>::iterator iter = suspendedEngines.begin();
723     std::set<gig::Engine*>::iterator end = suspendedEngines.end();
724     for (; iter != end; ++iter) (*iter)->SuspendAll();
725     }
726    
727     /**
728     * MUST be called after one called one of the @c SuspendEnginesUsing()
729     * methods, to resume normal playback on all previously suspended engines.
730     * As it's only possible for one thread to suspend whole engines at the
731     * same time, this method doesn't take any arguments.
732     */
733     void InstrumentResourceManager::ResumeAllEngines() {
734     // resume all previously completely suspended engines
735     std::set<Engine*>::iterator iter = suspendedEngines.begin();
736     std::set<Engine*>::iterator end = suspendedEngines.end();
737     for (; iter != end; ++iter) (*iter)->ResumeAll();
738     // no more suspended engines ...
739     suspendedEngines.clear();
740     // allow another thread to suspend whole engines
741     suspendedEnginesMutex.Unlock();
742     }
743    
744    
745    
746 schoenebeck 53 // internal gig file manager
747    
748     ::gig::File* InstrumentResourceManager::GigResourceManager::Create(String Key, GigConsumer* pConsumer, void*& pArg) {
749     dmsg(1,("Loading gig file \'%s\'...", Key.c_str()));
750     ::RIFF::File* pRIFF = new ::RIFF::File(Key);
751     ::gig::File* pGig = new ::gig::File(pRIFF);
752     pArg = pRIFF;
753     dmsg(1,("OK\n"));
754     return pGig;
755     }
756    
757     void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
758     dmsg(1,("Freeing gig file from memory..."));
759 persson 1038
760     // Delete as much as possible of the gig file. Some of the
761     // dimension regions and samples may still be in use - these
762     // will be deleted later by the HandBackDimReg function.
763     bool deleteFile = true;
764     ::gig::Instrument* nextInstrument;
765     for (::gig::Instrument* instrument = pResource->GetFirstInstrument() ;
766     instrument ;
767     instrument = nextInstrument) {
768     nextInstrument = pResource->GetNextInstrument();
769     bool deleteInstrument = true;
770     ::gig::Region* nextRegion;
771     for (::gig::Region *region = instrument->GetFirstRegion() ;
772     region ;
773     region = nextRegion) {
774     nextRegion = instrument->GetNextRegion();
775     bool deleteRegion = true;
776     for (int i = 0 ; i < region->DimensionRegions ; i++)
777     {
778     ::gig::DimensionRegion *d = region->pDimensionRegions[i];
779     std::map< ::gig::DimensionRegion*, dimreg_info_t>::iterator iter = parent->DimRegInfo.find(d);
780     if (iter != parent->DimRegInfo.end()) {
781     dimreg_info_t& dimRegInfo = (*iter).second;
782     dimRegInfo.file = pResource;
783     dimRegInfo.riff = (::RIFF::File*)pArg;
784     deleteFile = deleteInstrument = deleteRegion = false;
785     }
786     }
787     if (deleteRegion) instrument->DeleteRegion(region);
788     }
789     if (deleteInstrument) pResource->DeleteInstrument(instrument);
790     }
791     if (deleteFile) {
792     delete pResource;
793     delete (::RIFF::File*) pArg;
794     } else {
795     dmsg(2,("keeping some samples that are in use..."));
796     ::gig::Sample* nextSample;
797     for (::gig::Sample* sample = pResource->GetFirstSample() ;
798     sample ;
799     sample = nextSample) {
800     nextSample = pResource->GetNextSample();
801     if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
802     pResource->DeleteSample(sample);
803     }
804     }
805     }
806 schoenebeck 53 dmsg(1,("OK\n"));
807     }
808    
809     }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC