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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 958 by schoenebeck, Wed Nov 29 19:48:38 2006 UTC revision 1424 by schoenebeck, Sun Oct 14 22:00:17 2007 UTC
# Line 3  Line 3 
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
6   *   Copyright (C) 2005, 2006 Christian Schoenebeck                        *   *   Copyright (C) 2005 - 2007 Christian Schoenebeck                       *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   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  *   *   it under the terms of the GNU General Public License as published by  *
# Line 25  Line 25 
25    
26  #include "InstrumentResourceManager.h"  #include "InstrumentResourceManager.h"
27    
28    #include "../../common/global_private.h"
29    #include "../../plugins/InstrumentEditorFactory.h"
30    
31  // We need to know the maximum number of sample points which are going to  // 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  // 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  // how much initial sample points we need to cache into RAM. If the given
# Line 47  namespace LinuxSampler { namespace gig { Line 50  namespace LinuxSampler { namespace gig {
50          InstrumentManager::instrument_id_t* pInstrumentKey;          InstrumentManager::instrument_id_t* pInstrumentKey;
51      };      };
52    
53        // 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      /**      /**
73       * Callback function which will be called by libgig during loading of       * Callback function which will be called by libgig during loading of
74       * instruments to inform about the current progress. Or to be more       * instruments to inform about the current progress. Or to be more
# Line 78  namespace LinuxSampler { namespace gig { Line 100  namespace LinuxSampler { namespace gig {
100      }      }
101    
102      String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {      String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
103          ::gig::Instrument* pInstrument = Resource(ID);          Lock();
104          return (pInstrument) ? pInstrument->pInfo->Name : "";          ::gig::Instrument* pInstrument = Resource(ID, false);
105            String res = (pInstrument) ? pInstrument->pInfo->Name : "";
106            Unlock();
107            return res;
108        }
109    
110        String InstrumentResourceManager::GetInstrumentDataStructureName(instrument_id_t ID) {
111            return ::gig::libraryName();
112        }
113    
114        String InstrumentResourceManager::GetInstrumentDataStructureVersion(instrument_id_t ID) {
115            return ::gig::libraryVersion();
116        }
117    
118        void InstrumentResourceManager::LaunchInstrumentEditor(instrument_id_t ID) throw (InstrumentManagerException) {
119            const String sDataType    = GetInstrumentDataStructureName(ID);
120            const String sDataVersion = GetInstrumentDataStructureVersion(ID);
121            // 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            // register for receiving notifications from the instrument editor
133            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        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      ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {      ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
320          // get gig file from inernal gig file manager          // get gig file from internal gig file manager
321          ::gig::File* pGig = Gigs.Borrow(Key.FileName, (GigConsumer*) Key.Index); // conversion kinda hackish :/          ::gig::File* pGig = Gigs.Borrow(Key.FileName, (GigConsumer*) Key.Index); // conversion kinda hackish :/
322    
323          // we pass this to the progress callback mechanism of libgig          // we pass this to the progress callback mechanism of libgig
# Line 100  namespace LinuxSampler { namespace gig { Line 334  namespace LinuxSampler { namespace gig {
334          if (!pInstrument) {          if (!pInstrument) {
335              std::stringstream msg;              std::stringstream msg;
336              msg << "There's no instrument with index " << Key.Index << ".";              msg << "There's no instrument with index " << Key.Index << ".";
337              throw InstrumentResourceManagerException(msg.str());              throw InstrumentManagerException(msg.str());
338          }          }
339          pGig->GetFirstSample(); // just to force complete instrument loading          pGig->GetFirstSample(); // just to force complete instrument loading
340          dmsg(1,("OK\n"));          dmsg(1,("OK\n"));
# Line 134  namespace LinuxSampler { namespace gig { Line 368  namespace LinuxSampler { namespace gig {
368          pEntry->ID.Index      = Key.Index;          pEntry->ID.Index      = Key.Index;
369          pEntry->pGig          = pGig;          pEntry->pGig          = pGig;
370    
371          gig::EngineChannel* pEngineChannel = (gig::EngineChannel*) pConsumer;          gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
372          // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'          // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'
373          pEntry->MaxSamplesPerCycle =          pEntry->MaxSamplesPerCycle =
374              (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()              (!pEngineChannel) ? 0 /* don't care for instrument editors */ :
375                                            : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;                  (pEngineChannel->GetEngine()) ?
376                        dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
377                        : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
378          pArg = pEntry;          pArg = pEntry;
379    
380          return pInstrument;          return pInstrument;
# Line 155  namespace LinuxSampler { namespace gig { Line 391  namespace LinuxSampler { namespace gig {
391          instr_entry_t* pEntry = (instr_entry_t*) pArg;          instr_entry_t* pEntry = (instr_entry_t*) pArg;
392          gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);          gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
393          uint maxSamplesPerCycle =          uint maxSamplesPerCycle =
394              (pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()              (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
395                                            : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;                                            : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
396          if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {          if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
397              Update(pResource, pConsumer);              Update(pResource, pConsumer);
# Line 163  namespace LinuxSampler { namespace gig { Line 399  namespace LinuxSampler { namespace gig {
399      }      }
400    
401      /**      /**
402         * 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         * 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       *  Caches a certain size at the beginning of the given sample in RAM. If the       *  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       *  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       *  no disk streaming is needed for this sample. Caching an initial part of
470       *  samples is needed to compensate disk reading latency.       *  samples is needed to compensate disk reading latency.
471       *       *
472       *  @param pSample - points to the sample to be cached       *  @param pSample - points to the sample to be cached
473       *  @param pEngineChannel - pointer to Gig Engine Channel which caused this call       *  @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       */       */
477      void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::EngineChannel* pEngineChannel) {      void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::Engine* pEngine) {
478          if (!pSample) {          if (!pSample) {
479              dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));              dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
480              return;              return;
# Line 185  namespace LinuxSampler { namespace gig { Line 488  namespace LinuxSampler { namespace gig {
488              // border, to allow the interpolator do it's work even at the end of              // border, to allow the interpolator do it's work even at the end of
489              // the sample.              // the sample.
490              const uint maxSamplesPerCycle =              const uint maxSamplesPerCycle =
491                  (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()                  (pEngine) ? pEngine->pAudioOutputDevice->MaxSamplesPerCycle()
492                                                : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;                            : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
493              const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;              const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;
494              const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;              const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;
495              if (currentlyCachedSilenceSamples < neededSilenceSamples) {              if (currentlyCachedSilenceSamples < neededSilenceSamples) {
# Line 202  namespace LinuxSampler { namespace gig { Line 505  namespace LinuxSampler { namespace gig {
505          if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;          if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
506      }      }
507    
508        void InstrumentResourceManager::UncacheInitialSamples(::gig::Sample* pSample) {
509            dmsg(1,("Uncaching sample %x\n",pSample));
510            if (pSample->GetCache().Size) pSample->ReleaseSampleData();
511        }
512    
513        /**
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    
534        /**
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      // internal gig file manager      // internal gig file manager
# Line 217  namespace LinuxSampler { namespace gig { Line 695  namespace LinuxSampler { namespace gig {
695    
696      void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {      void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
697          dmsg(1,("Freeing gig file from memory..."));          dmsg(1,("Freeing gig file from memory..."));
698          delete pResource;  
699          delete (::RIFF::File*) pArg;          // 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          dmsg(1,("OK\n"));          dmsg(1,("OK\n"));
746      }      }
747    

Legend:
Removed from v.958  
changed lines
  Added in v.1424

  ViewVC Help
Powered by ViewVC