/[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 1424 - (hide annotations) (download)
Sun Oct 14 22:00:17 2007 UTC (16 years, 6 months ago) by schoenebeck
File size: 36706 byte(s)
* code cleanup:
- global.h now only covers global definitions that are needed for the C++
  API header files, all implementation internal global definitions are now
  in global_private.h
- atomic.h is not exposed to the C++ API anymore (replaced the references
  in SynchronizedConfig.h for this with local definitions)
- no need to include config.h anymore for using LS's API header files
- DB instruments classes are not exposed to the C++ API
- POSIX callback functions of Thread.h are hidden
- the (optional) gig Engine benchmark compiles again
- updated Doxyfile.in
- fixed warnings in API doc generation
* preparations for release 0.5.0

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

  ViewVC Help
Powered by ViewVC