/[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 1321 - (hide annotations) (download)
Tue Sep 4 01:12:49 2007 UTC (16 years, 7 months ago) by schoenebeck
File size: 36654 byte(s)
* added highly experimental code for synchronizing instrument editors
  hosted in the sampler's process to safely edit instruments while playing
  without a crash (hopefully) by either suspending single regions wherever
  possible or - if unavoidable - whole engine(s)
* disk thread: queue sizes are now proportional to CONFIG_MAX_STREAMS
  instead of fix values
* removed legacy Makefiles in meanwhile deleted src/lib directory and its
  subdirectories
* bumped version to 0.4.0.7cvs

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

  ViewVC Help
Powered by ViewVC