/[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 970 by schoenebeck, Wed Dec 6 22:28:17 2006 UTC revision 2012 by iliev, Fri Oct 23 17:53:17 2009 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 - 2009 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 24  Line 24 
24  #include <sstream>  #include <sstream>
25    
26  #include "InstrumentResourceManager.h"  #include "InstrumentResourceManager.h"
27    #include "EngineChannel.h"
28    #include "Engine.h"
29    
30    #include "../../common/global_private.h"
31    #include "../../plugins/InstrumentEditorFactory.h"
32    
33  // 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
34  // 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
# Line 47  namespace LinuxSampler { namespace gig { Line 52  namespace LinuxSampler { namespace gig {
52          InstrumentManager::instrument_id_t* pInstrumentKey;          InstrumentManager::instrument_id_t* pInstrumentKey;
53      };      };
54    
55        // we use this to react on events concerning an instrument on behalf of an instrument editor
56        class InstrumentEditorProxy : public InstrumentConsumer {
57        public:
58            virtual void ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
59                //TODO: inform the instrument editor about the pending update
60            }
61    
62            virtual void ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
63                //TODO:: inform the instrument editor about finished update
64            }
65    
66            virtual void OnResourceProgress(float fProgress) {
67                //TODO: inform the instrument editor about the progress of an update
68            }
69    
70            // the instrument we borrowed on behalf of the editor
71            ::gig::Instrument* pInstrument;
72            // the instrument editor we work on behalf
73            InstrumentEditor* pEditor;
74        };
75    
76      /**      /**
77       * Callback function which will be called by libgig during loading of       * Callback function which will be called by libgig during loading of
78       * instruments to inform about the current progress. Or to be more       * instruments to inform about the current progress. Or to be more
# Line 68  namespace LinuxSampler { namespace gig { Line 94  namespace LinuxSampler { namespace gig {
94          return Entries();          return Entries();
95      }      }
96    
97      InstrumentManager::mode_t InstrumentResourceManager::GetMode(const instrument_id_t& ID) {      String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
98          return static_cast<InstrumentManager::mode_t>(AvailabilityMode(ID));          Lock();
99            ::gig::Instrument* pInstrument = Resource(ID, false);
100            String res = (pInstrument) ? pInstrument->pInfo->Name : "";
101            Unlock();
102            return res;
103        }
104    
105        String InstrumentResourceManager::GetInstrumentDataStructureName(instrument_id_t ID) {
106            return ::gig::libraryName();
107      }      }
108    
109      void InstrumentResourceManager::SetMode(const instrument_id_t& ID, InstrumentManager::mode_t Mode) {      String InstrumentResourceManager::GetInstrumentDataStructureVersion(instrument_id_t ID) {
110          dmsg(2,("gig::InstrumentResourceManager: setting mode for %s (Index=%d) to %d\n",ID.FileName.c_str(),ID.Index,Mode));          return ::gig::libraryVersion();
         SetAvailabilityMode(ID, static_cast<ResourceManager<InstrumentManager::instrument_id_t, ::gig::Instrument>::mode_t>(Mode));  
111      }      }
112    
113      String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {      std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::GetInstrumentFileContent(String File) throw (InstrumentManagerException) {
114            ::RIFF::File* riff = NULL;
115            ::gig::File*  gig  = NULL;
116            try {
117                std::vector<instrument_id_t> result;
118                riff = new ::RIFF::File(File);
119                gig  = new ::gig::File(riff);
120                gig->SetAutoLoad(false); // avoid time consuming samples scanning
121                for (int i = 0; gig->GetInstrument(i); i++) {
122                    instrument_id_t id;
123                    id.FileName = File;
124                    id.Index    = i;
125                    result.push_back(id);
126                }
127                if (gig)  delete gig;
128                if (riff) delete riff;
129                return result;
130            } catch (::RIFF::Exception e) {
131                if (gig)  delete gig;
132                if (riff) delete riff;
133                throw InstrumentManagerException(e.Message);
134            } catch (...) {
135                if (gig)  delete gig;
136                if (riff) delete riff;
137                throw InstrumentManagerException("Unknown exception while trying to parse '" + File + "'");
138            }
139        }
140    
141        InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) {
142          Lock();          Lock();
143          ::gig::Instrument* pInstrument = Resource(ID, false);          ::gig::Instrument* pInstrument = Resource(ID, false);
144          String res = (pInstrument) ? pInstrument->pInfo->Name : "";          bool loaded = (pInstrument != NULL);
145            if (!loaded) Unlock();
146    
147            ::RIFF::File* riff = NULL;
148            ::gig::File*  gig  = NULL;
149            try {
150                if(!loaded) {
151                    riff = new ::RIFF::File(ID.FileName);
152                    gig  = new ::gig::File(riff);
153                    gig->SetAutoLoad(false); // avoid time consuming samples scanning
154                    pInstrument = gig->GetInstrument(ID.Index);
155                }
156    
157                if (!pInstrument) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName);
158    
159                instrument_info_t info;
160                for (int i = 0; i < 128; i++) { info.KeyBindings[i] = info.KeySwitchBindings[i] = 0; }
161    
162                ::gig::File* pFile = (::gig::File*) pInstrument->GetParent();
163    
164                if (pFile->pVersion) {
165                    info.FormatVersion = ToString(pFile->pVersion->major);
166                    info.Product = pFile->pInfo->Product;
167                    info.Artists = pFile->pInfo->Artists;
168                }
169    
170                info.InstrumentName = pInstrument->pInfo->Name;
171    
172                ::gig::Region* pRegion = pInstrument->GetFirstRegion();
173                while (pRegion) {
174                    int low = pRegion->KeyRange.low;
175                    int high = pRegion->KeyRange.high;
176                    if (low < 0 || low > 127 || high < 0 || high > 127 || low > high) {
177                        std::cerr << "Invalid key range: " << low << " - " << high << std::endl;
178                    } else {
179                        for (int i = low; i <= high; i++) info.KeyBindings[i] = 1;
180                    }
181    
182                    pRegion = pInstrument->GetNextRegion();
183                }
184    
185                if (loaded) { // retrieve keyswitching only if the instrument is fully loaded.
186    
187                    // only return keyswitch range if keyswitching is used
188                    bool hasKeyswitches = false;
189                    for (::gig::Region* pRegion = pInstrument->GetFirstRegion() ;
190                         pRegion && !hasKeyswitches ;
191                         pRegion = pInstrument->GetNextRegion()) {
192                        for (int i = 0 ; i < pRegion->Dimensions ; i++) {
193                            if (pRegion->pDimensionDefinitions[i].dimension == ::gig::dimension_keyboard) {
194                                hasKeyswitches = true;
195                                break;
196                            }
197                        }
198                    }
199    
200                    if (hasKeyswitches) {
201                        int low = pInstrument->DimensionKeyRange.low;
202                        int high = pInstrument->DimensionKeyRange.high;
203                        if (low < 0 || low > 127 || high < 0 || high > 127 || low > high) {
204                            std::cerr << "Invalid keyswitch range: " << low << " - " << high << std::endl;
205                        } else {
206                            for (int i = low; i <= high; i++) info.KeySwitchBindings[i] = 1;
207                        }
208                    }
209                }
210    
211                if (loaded) Unlock();
212    
213                if (gig)  delete gig;
214                if (riff) delete riff;
215                return info;
216            } catch (::RIFF::Exception e) {
217                if (loaded) Unlock();
218                if (gig)  delete gig;
219                if (riff) delete riff;
220                throw InstrumentManagerException(e.Message);
221            } catch (...) {
222                if (loaded) Unlock();
223                if (gig)  delete gig;
224                if (riff) delete riff;
225                throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'");
226            }
227        }
228    
229        InstrumentEditor* InstrumentResourceManager::LaunchInstrumentEditor(instrument_id_t ID, void* pUserData) throw (InstrumentManagerException) {
230            const String sDataType    = GetInstrumentDataStructureName(ID);
231            const String sDataVersion = GetInstrumentDataStructureVersion(ID);
232            // find instrument editors capable to handle given instrument
233            std::vector<String> vEditors =
234                InstrumentEditorFactory::MatchingEditors(sDataType, sDataVersion);
235            if (!vEditors.size())
236                throw InstrumentManagerException(
237                    "There is no instrument editor capable to handle this instrument"
238                );
239            // simply use the first editor in the result set
240            dmsg(1,("Found matching editor '%s' for instrument ('%s', %d) having data structure ('%s','%s')\n",
241                vEditors[0].c_str(), ID.FileName.c_str(), ID.Index, sDataType.c_str(), sDataVersion.c_str()));
242            InstrumentEditor* pEditor = InstrumentEditorFactory::Create(vEditors[0]);
243            // register for receiving notifications from the instrument editor
244            pEditor->AddListener(this);
245            // create a proxy that reacts on notification on behalf of the editor
246            InstrumentEditorProxy* pProxy = new InstrumentEditorProxy;
247            // borrow the instrument on behalf of the instrument editor
248            ::gig::Instrument* pInstrument = Borrow(ID, pProxy);
249            // remember the proxy and instrument for this instrument editor
250            pProxy->pInstrument = pInstrument;
251            pProxy->pEditor     = pEditor;
252            InstrumentEditorProxiesMutex.Lock();
253            InstrumentEditorProxies.add(pProxy);
254            InstrumentEditorProxiesMutex.Unlock();
255            // launch the instrument editor for the given instrument
256            pEditor->Launch(pInstrument, sDataType, sDataVersion, pUserData);
257    
258            // register the instrument editor as virtual MIDI device as well ...
259            VirtualMidiDevice* pVirtualMidiDevice =
260                dynamic_cast<VirtualMidiDevice*>(pEditor);
261            if (!pVirtualMidiDevice) {
262                std::cerr << "Instrument editor not a virtual MIDI device\n" << std::flush;
263                return pEditor;
264            }
265            // NOTE: for now connect the virtual MIDI keyboard of the instrument editor (if any) with all engine channels that have the same instrument as the editor was opened for ( other ideas ? )
266            Lock();
267            std::set<EngineChannel*> engineChannels =
268                GetEngineChannelsUsing(pInstrument, false/*don't lock again*/);
269            std::set<EngineChannel*>::iterator iter = engineChannels.begin();
270            std::set<EngineChannel*>::iterator end  = engineChannels.end();
271            for (; iter != end; ++iter) (static_cast<AbstractEngineChannel*>(*iter))->Connect(pVirtualMidiDevice);
272          Unlock();          Unlock();
273          return res;  
274            return pEditor;
275        }
276    
277        /**
278         * Will be called by the respective instrument editor once it left its
279         * Main() loop. That way we can handle cleanup before its thread finally
280         * dies.
281         *
282         * @param pSender - instrument editor that stops execution
283         */
284        void InstrumentResourceManager::OnInstrumentEditorQuit(InstrumentEditor* pSender) {
285            dmsg(1,("InstrumentResourceManager: instrument editor quit, doing cleanup\n"));
286    
287            ::gig::Instrument* pInstrument = NULL;
288            InstrumentEditorProxy* pProxy  = NULL;
289            int iProxyIndex                = -1;
290    
291            // first find the editor proxy entry for this editor
292            InstrumentEditorProxiesMutex.Lock();
293            for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
294                InstrumentEditorProxy* pCurProxy =
295                    dynamic_cast<InstrumentEditorProxy*>(
296                        InstrumentEditorProxies[i]
297                    );
298                if (pCurProxy->pEditor == pSender) {
299                    pProxy      = pCurProxy;
300                    iProxyIndex = i;
301                    pInstrument = pCurProxy->pInstrument;
302                }
303            }
304            InstrumentEditorProxiesMutex.Unlock();
305    
306            if (!pProxy) {
307                std::cerr << "Eeeek, could not find instrument editor proxy, "
308                             "this is a bug!\n" << std::flush;
309                return;
310            }
311    
312            // now unregister editor as not being available as a virtual MIDI device anymore
313            VirtualMidiDevice* pVirtualMidiDevice =
314                dynamic_cast<VirtualMidiDevice*>(pSender);
315            if (pVirtualMidiDevice) {
316                Lock();
317                // NOTE: see note in LaunchInstrumentEditor()
318                std::set<EngineChannel*> engineChannels =
319                    GetEngineChannelsUsing(pInstrument, false/*don't lock again*/);
320                std::set<EngineChannel*>::iterator iter = engineChannels.begin();
321                std::set<EngineChannel*>::iterator end  = engineChannels.end();
322                for (; iter != end; ++iter) (*iter)->Disconnect(pVirtualMidiDevice);
323                Unlock();
324            } else {
325                std::cerr << "Could not unregister editor as not longer acting as "
326                             "virtual MIDI device. Wasn't it registered?\n"
327                          << std::flush;
328            }
329    
330            // finally delete proxy entry and hand back instrument
331            if (pInstrument) {
332                InstrumentEditorProxiesMutex.Lock();
333                InstrumentEditorProxies.remove(iProxyIndex);
334                InstrumentEditorProxiesMutex.Unlock();
335    
336                HandBack(pInstrument, pProxy);
337                delete pProxy;
338            }
339    
340            // Note that we don't need to free the editor here. As it
341            // derives from Thread, it will delete itself when the thread
342            // dies.
343        }
344    
345    #if 0 // currently unused :
346        /**
347         * Try to inform the respective instrument editor(s), that a note on
348         * event just occured. This method is called by the MIDI thread. If any
349         * obstacles are in the way (e.g. if a wait for an unlock would be
350         * required) we give up immediately, since the RT safeness of the MIDI
351         * thread has absolute priority.
352         */
353        void InstrumentResourceManager::TrySendNoteOnToEditors(uint8_t Key, uint8_t Velocity, ::gig::Instrument* pInstrument) {
354            const bool bGotLock = InstrumentEditorProxiesMutex.Trylock(); // naively assumes RT safe implementation
355            if (!bGotLock) return; // hell, forget it, not worth the hassle
356            for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
357                InstrumentEditorProxy* pProxy =
358                    dynamic_cast<InstrumentEditorProxy*>(
359                        InstrumentEditorProxies[i]
360                    );
361                if (pProxy->pInstrument == pInstrument)
362                    pProxy->pEditor->SendNoteOnToDevice(Key, Velocity);
363            }
364            InstrumentEditorProxiesMutex.Unlock(); // naively assumes RT safe implementation
365        }
366    
367        /**
368         * Try to inform the respective instrument editor(s), that a note off
369         * event just occured. This method is called by the MIDI thread. If any
370         * obstacles are in the way (e.g. if a wait for an unlock would be
371         * required) we give up immediately, since the RT safeness of the MIDI
372         * thread has absolute priority.
373         */
374        void InstrumentResourceManager::TrySendNoteOffToEditors(uint8_t Key, uint8_t Velocity, ::gig::Instrument* pInstrument) {
375            const bool bGotLock = InstrumentEditorProxiesMutex.Trylock(); // naively assumes RT safe implementation
376            if (!bGotLock) return; // hell, forget it, not worth the hassle
377            for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
378                InstrumentEditorProxy* pProxy =
379                    dynamic_cast<InstrumentEditorProxy*>(
380                        InstrumentEditorProxies[i]
381                    );
382                if (pProxy->pInstrument == pInstrument)
383                    pProxy->pEditor->SendNoteOffToDevice(Key, Velocity);
384            }
385            InstrumentEditorProxiesMutex.Unlock(); // naively assumes RT safe implementation
386        }
387    #endif // unused
388    
389        void InstrumentResourceManager::OnSamplesToBeRemoved(std::set<void*> Samples, InstrumentEditor* pSender) {
390            if (Samples.empty()) {
391                std::cerr << "gig::InstrumentResourceManager: WARNING, "
392                             "OnSamplesToBeRemoved() called with empty list, this "
393                             "is a bug!\n" << std::flush;
394                return;
395            }
396            // TODO: ATM we assume here that all samples are from the same file
397            ::gig::Sample* pFirstSample = (::gig::Sample*) *Samples.begin();
398            ::gig::File* pCriticalFile = dynamic_cast< ::gig::File*>(pFirstSample->GetParent());
399            // completely suspend all engines that use that same file
400            SuspendEnginesUsing(pCriticalFile);
401        }
402    
403        void InstrumentResourceManager::OnSamplesRemoved(InstrumentEditor* pSender) {
404            // resume all previously, completely suspended engines
405            // (we don't have to un-cache the removed samples here, since that is
406            // automatically done by the gig::Sample destructor)
407            ResumeAllEngines();
408        }
409    
410        void InstrumentResourceManager::OnDataStructureToBeChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
411            //TODO: remove code duplication
412            if (sStructType == "gig::File") {
413                // completely suspend all engines that use that file
414                ::gig::File* pFile = (::gig::File*) pStruct;
415                SuspendEnginesUsing(pFile);
416            } else if (sStructType == "gig::Instrument") {
417                // completely suspend all engines that use that instrument
418                ::gig::Instrument* pInstrument = (::gig::Instrument*) pStruct;
419                SuspendEnginesUsing(pInstrument);
420            } else if (sStructType == "gig::Region") {
421                // only advice the engines to suspend the given region, so they'll
422                // only ignore that region (and probably already other suspended
423                // ones), but beside that continue normal playback
424                ::gig::Region* pRegion = (::gig::Region*) pStruct;
425                ::gig::Instrument* pInstrument =
426                    (::gig::Instrument*) pRegion->GetParent();
427                Lock();
428                std::set<Engine*> engines =
429                    GetEnginesUsing(pInstrument, false/*don't lock again*/);
430                std::set<Engine*>::iterator iter = engines.begin();
431                std::set<Engine*>::iterator end  = engines.end();
432                for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
433                Unlock();
434            } else if (sStructType == "gig::DimensionRegion") {
435                // only advice the engines to suspend the given DimensionRegions's
436                // parent region, so they'll only ignore that region (and probably
437                // already other suspended ones), but beside that continue normal
438                // playback
439                ::gig::DimensionRegion* pDimReg =
440                    (::gig::DimensionRegion*) pStruct;
441                ::gig::Region* pRegion = pDimReg->GetParent();
442                ::gig::Instrument* pInstrument =
443                    (::gig::Instrument*) pRegion->GetParent();
444                Lock();
445                std::set<Engine*> engines =
446                    GetEnginesUsing(pInstrument, false/*don't lock again*/);
447                std::set<Engine*>::iterator iter = engines.begin();
448                std::set<Engine*>::iterator end  = engines.end();
449                for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
450                Unlock();
451            } else {
452                std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
453                             "structure '" << sStructType << "' requested to be "
454                             "suspended by instrument editor. This is a bug!\n"
455                          << std::flush;
456                //TODO: we should inform the instrument editor that something seriously went wrong
457            }
458        }
459    
460        void InstrumentResourceManager::OnDataStructureChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
461            //TODO: remove code duplication
462            if (sStructType == "gig::File") {
463                // resume all previously suspended engines
464                ResumeAllEngines();
465            } else if (sStructType == "gig::Instrument") {
466                // resume all previously suspended engines
467                ResumeAllEngines();
468            } else if (sStructType == "gig::Sample") {
469                // we're assuming here, that OnDataStructureToBeChanged() with
470                // "gig::File" was called previously, so we won't resume anything
471                // here, but just re-cache the given sample
472                Lock();
473                ::gig::Sample* pSample = (::gig::Sample*) pStruct;
474                ::gig::File* pFile = (::gig::File*) pSample->GetParent();
475                UncacheInitialSamples(pSample);
476                // now re-cache ...
477                std::vector< ::gig::Instrument*> instruments =
478                    GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
479                for (int i = 0; i < instruments.size(); i++) {
480                    if (SampleReferencedByInstrument(pSample, instruments[i])) {
481                        std::set<EngineChannel*> engineChannels =
482                            GetEngineChannelsUsing(instruments[i], false/*don't lock again*/);
483                        std::set<EngineChannel*>::iterator iter = engineChannels.begin();
484                        std::set<EngineChannel*>::iterator end  = engineChannels.end();
485                        for (; iter != end; ++iter)
486                            CacheInitialSamples(pSample, *iter);
487                    }
488                }
489                Unlock();
490            } else if (sStructType == "gig::Region") {
491                // advice the engines to resume the given region, that is to
492                // using it for playback again
493                ::gig::Region* pRegion = (::gig::Region*) pStruct;
494                ::gig::Instrument* pInstrument =
495                    (::gig::Instrument*) pRegion->GetParent();
496                Lock();
497                std::set<Engine*> engines =
498                    GetEnginesUsing(pInstrument, false/*don't lock again*/);
499                std::set<Engine*>::iterator iter = engines.begin();
500                std::set<Engine*>::iterator end  = engines.end();
501                for (; iter != end; ++iter) (*iter)->Resume(pRegion);
502                Unlock();
503            } else if (sStructType == "gig::DimensionRegion") {
504                // advice the engines to resume the given DimensionRegion's parent
505                // region, that is to using it for playback again
506                ::gig::DimensionRegion* pDimReg =
507                    (::gig::DimensionRegion*) pStruct;
508                ::gig::Region* pRegion = pDimReg->GetParent();
509                ::gig::Instrument* pInstrument =
510                    (::gig::Instrument*) pRegion->GetParent();
511                Lock();
512                std::set<Engine*> engines =
513                    GetEnginesUsing(pInstrument, false/*don't lock again*/);
514                std::set<Engine*>::iterator iter = engines.begin();
515                std::set<Engine*>::iterator end  = engines.end();
516                for (; iter != end; ++iter) (*iter)->Resume(pRegion);
517                Unlock();
518            } else {
519                std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
520                             "structure '" << sStructType << "' requested to be "
521                             "resumed by instrument editor. This is a bug!\n"
522                          << std::flush;
523                //TODO: we should inform the instrument editor that something seriously went wrong
524            }
525        }
526    
527        void InstrumentResourceManager::OnSampleReferenceChanged(void* pOldSample, void* pNewSample, InstrumentEditor* pSender) {
528            // uncache old sample in case it's not used by anybody anymore
529            if (pOldSample) {
530                Lock();
531                ::gig::Sample* pSample = (::gig::Sample*) pOldSample;
532                ::gig::File* pFile = (::gig::File*) pSample->GetParent();
533                bool bSampleStillInUse = false;
534                std::vector< ::gig::Instrument*> instruments =
535                    GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
536                for (int i = 0; i < instruments.size(); i++) {
537                    if (SampleReferencedByInstrument(pSample, instruments[i])) {
538                        bSampleStillInUse = true;
539                        break;
540                    }
541                }
542                if (!bSampleStillInUse) UncacheInitialSamples(pSample);
543                Unlock();
544            }
545            // make sure new sample reference is cached
546            if (pNewSample) {
547                Lock();
548                ::gig::Sample* pSample = (::gig::Sample*) pNewSample;
549                ::gig::File* pFile = (::gig::File*) pSample->GetParent();
550                // get all engines that use that same gig::File
551                std::set<Engine*> engines = GetEnginesUsing(pFile, false/*don't lock again*/);
552                std::set<Engine*>::iterator iter = engines.begin();
553                std::set<Engine*>::iterator end  = engines.end();
554                for (; iter != end; ++iter)
555                    CacheInitialSamples(pSample, *iter);
556                Unlock();
557            }
558      }      }
559    
560      ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {      ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
561          // get gig file from inernal gig file manager          // get gig file from internal gig file manager
562          ::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 :/
563    
564          // we pass this to the progress callback mechanism of libgig          // we pass this to the progress callback mechanism of libgig
# Line 103  namespace LinuxSampler { namespace gig { Line 575  namespace LinuxSampler { namespace gig {
575          if (!pInstrument) {          if (!pInstrument) {
576              std::stringstream msg;              std::stringstream msg;
577              msg << "There's no instrument with index " << Key.Index << ".";              msg << "There's no instrument with index " << Key.Index << ".";
578              throw InstrumentResourceManagerException(msg.str());              throw InstrumentManagerException(msg.str());
579          }          }
580          pGig->GetFirstSample(); // just to force complete instrument loading          pGig->GetFirstSample(); // just to force complete instrument loading
581          dmsg(1,("OK\n"));          dmsg(1,("OK\n"));
# Line 119  namespace LinuxSampler { namespace gig { Line 591  namespace LinuxSampler { namespace gig {
591    
592              if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {              if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {
593                  dmsg(2,("C"));                  dmsg(2,("C"));
594                  CacheInitialSamples(pRgn->GetSample(), (gig::EngineChannel*) pConsumer);                  CacheInitialSamples(pRgn->GetSample(), (EngineChannel*) pConsumer);
595              }              }
596              for (uint i = 0; i < pRgn->DimensionRegions; i++) {              for (uint i = 0; i < pRgn->DimensionRegions; i++) {
597                  CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, (gig::EngineChannel*) pConsumer);                  CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, (EngineChannel*) pConsumer);
598              }              }
599    
600              pRgn = pInstrument->GetNextRegion();              pRgn = pInstrument->GetNextRegion();
# Line 137  namespace LinuxSampler { namespace gig { Line 609  namespace LinuxSampler { namespace gig {
609          pEntry->ID.Index      = Key.Index;          pEntry->ID.Index      = Key.Index;
610          pEntry->pGig          = pGig;          pEntry->pGig          = pGig;
611    
612          gig::EngineChannel* pEngineChannel = (gig::EngineChannel*) pConsumer;          EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(pConsumer);
613          // 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'
614          pEntry->MaxSamplesPerCycle =          pEntry->MaxSamplesPerCycle =
615              (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()              (!pEngineChannel) ? 0 /* don't care for instrument editors */ :
616                                            : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;                  (pEngineChannel->GetEngine()) ?
617                        dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
618                        : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
619          pArg = pEntry;          pArg = pEntry;
620    
621          return pInstrument;          return pInstrument;
# Line 156  namespace LinuxSampler { namespace gig { Line 630  namespace LinuxSampler { namespace gig {
630    
631      void InstrumentResourceManager::OnBorrow(::gig::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) {      void InstrumentResourceManager::OnBorrow(::gig::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) {
632          instr_entry_t* pEntry = (instr_entry_t*) pArg;          instr_entry_t* pEntry = (instr_entry_t*) pArg;
633          gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);          EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(pConsumer);
634          uint maxSamplesPerCycle =          uint maxSamplesPerCycle =
635              (pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()              (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
636                                            : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;                                            : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
637          if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {          if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
638              Update(pResource, pConsumer);              Update(pResource, pConsumer);
639          }          }
640      }      }
641    
642        void InstrumentResourceManager::DeleteRegionIfNotUsed(::gig::DimensionRegion* pRegion, region_info_t* pRegInfo) {
643            // TODO: we could delete Region and Instrument here if they have become unused
644        }
645        void InstrumentResourceManager::DeleteSampleIfNotUsed(::gig::Sample* pSample, region_info_t* pRegInfo) {
646            ::gig::File* gig = pRegInfo->file;
647            ::RIFF::File* riff = static_cast< ::RIFF::File*>(pRegInfo->pArg);
648            if (gig) {
649                gig->DeleteSample(pSample);
650                if (!gig->GetFirstSample()) {
651                    dmsg(2,("No more samples in use - freeing gig\n"));
652                    delete gig;
653                    delete riff;
654                }
655            }
656        }
657    
658        /**
659         * Just a wrapper around the other @c CacheInitialSamples() method.
660         *
661         *  @param pSample - points to the sample to be cached
662         *  @param pEngine - pointer to Gig Engine Channel which caused this call
663         *                   (may be NULL, in this case default amount of samples
664         *                   will be cached)
665         */
666        void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, EngineChannel* pEngineChannel) {
667            Engine* pEngine =
668                (pEngineChannel && pEngineChannel->GetEngine()) ?
669                    dynamic_cast<Engine*>(pEngineChannel->GetEngine()) : NULL;
670            CacheInitialSamples(pSample, pEngine);
671        }
672    
673      /**      /**
674       *  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
675       *  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
# Line 172  namespace LinuxSampler { namespace gig { Line 677  namespace LinuxSampler { namespace gig {
677       *  samples is needed to compensate disk reading latency.       *  samples is needed to compensate disk reading latency.
678       *       *
679       *  @param pSample - points to the sample to be cached       *  @param pSample - points to the sample to be cached
680       *  @param pEngineChannel - pointer to Gig Engine Channel which caused this call       *  @param pEngine - pointer to Gig Engine which caused this call
681         *                   (may be NULL, in this case default amount of samples
682         *                   will be cached)
683       */       */
684      void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::EngineChannel* pEngineChannel) {      void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, AbstractEngine* pEngine) {
685          if (!pSample) {          if (!pSample) {
686              dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));              dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
687              return;              return;
# Line 188  namespace LinuxSampler { namespace gig { Line 695  namespace LinuxSampler { namespace gig {
695              // 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
696              // the sample.              // the sample.
697              const uint maxSamplesPerCycle =              const uint maxSamplesPerCycle =
698                  (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()                  (pEngine) ? pEngine->pAudioOutputDevice->MaxSamplesPerCycle()
699                                                : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;                            : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
700              const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;              const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;
701              const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;              const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;
702              if (currentlyCachedSilenceSamples < neededSilenceSamples) {              if (currentlyCachedSilenceSamples < neededSilenceSamples) {
# Line 205  namespace LinuxSampler { namespace gig { Line 712  namespace LinuxSampler { namespace gig {
712          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;
713      }      }
714    
715        void InstrumentResourceManager::UncacheInitialSamples(::gig::Sample* pSample) {
716            dmsg(1,("Uncaching sample %x\n",pSample));
717            if (pSample->GetCache().Size) pSample->ReleaseSampleData();
718        }
719    
720        /**
721         * Returns a list with all instruments currently in use, that are part of
722         * the given file.
723         *
724         * @param pFile - search criteria
725         * @param bLock - whether we should lock (mutex) the instrument manager
726         *                during this call and unlock at the end of this call
727         */
728        std::vector< ::gig::Instrument*> InstrumentResourceManager::GetInstrumentsCurrentlyUsedOf(::gig::File* pFile, bool bLock) {
729            if (bLock) Lock();
730            std::vector< ::gig::Instrument*> result;
731            std::vector< ::gig::Instrument*> allInstruments = Resources(false/*don't lock again*/);
732            for (int i = 0; i < allInstruments.size(); i++)
733                if (
734                    (::gig::File*) allInstruments[i]->GetParent()
735                    == pFile
736                ) result.push_back(allInstruments[i]);
737            if (bLock) Unlock();
738            return result;
739        }
740    
741        /**
742         * Returns a list with all gig engine channels that are currently using
743         * the given instrument.
744         *
745         * @param pInstrument - search criteria
746         * @param bLock - whether we should lock (mutex) the instrument manager
747         *                during this call and unlock at the end of this call
748         */
749        std::set<EngineChannel*> InstrumentResourceManager::GetEngineChannelsUsing(::gig::Instrument* pInstrument, bool bLock) {
750            if (bLock) Lock();
751            std::set<EngineChannel*> result;
752            std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(pInstrument);
753            std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
754            std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end  = consumers.end();
755            for (; iter != end; ++iter) {
756                EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(*iter);
757                if (!pEngineChannel) continue;
758                result.insert(pEngineChannel);
759            }
760            if (bLock) Unlock();
761            return result;
762        }
763    
764        /**
765         * Returns a list with all gig Engines that are currently using the given
766         * instrument.
767         *
768         * @param pInstrument - search criteria
769         * @param bLock - whether we should lock (mutex) the instrument manager
770         *                during this call and unlock at the end of this call
771         */
772        std::set<Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::Instrument* pInstrument, bool bLock) {
773            if (bLock) Lock();
774            std::set<Engine*> result;
775            std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(pInstrument);
776            std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
777            std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end  = consumers.end();
778            for (; iter != end; ++iter) {
779                EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(*iter);
780                if (!pEngineChannel) continue;
781                Engine* pEngine = dynamic_cast<Engine*>(pEngineChannel->GetEngine());
782                if (!pEngine) continue;
783                result.insert(pEngine);
784            }
785            if (bLock) Unlock();
786            return result;
787        }
788    
789        /**
790         * Returns a list with all gig Engines that are currently using an
791         * instrument that is part of the given instrument file.
792         *
793         * @param pFile - search criteria
794         * @param bLock - whether we should lock (mutex) the instrument manager
795         *                during this call and unlock at the end of this call
796         */
797        std::set<Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::File* pFile, bool bLock) {
798            if (bLock) Lock();
799            // get all instruments (currently in usage) that use that same gig::File
800            std::vector< ::gig::Instrument*> instrumentsOfInterest =
801                GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
802    
803            // get all engines that use that same gig::File
804            std::set<Engine*> result;
805            {
806                for (int i = 0; i < instrumentsOfInterest.size(); i++) {
807                    std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(instrumentsOfInterest[i]);
808                    std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
809                    std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end  = consumers.end();
810                    for (; iter != end; ++iter) {
811                        EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(*iter);
812                        if (!pEngineChannel) continue;
813                        Engine* pEngine = dynamic_cast<Engine*>(pEngineChannel->GetEngine());
814                        if (!pEngine) continue;
815                        // the unique, sorted container std::set makes
816                        // sure we won't have duplicates
817                        result.insert(pEngine);
818                    }
819                }
820            }
821            if (bLock) Unlock();
822            return result;
823        }
824    
825        /**
826         * Returns @c true in case the given sample is referenced somewhere by the
827         * given instrument, @c false otherwise.
828         *
829         * @param pSample - sample reference
830         * @param pInstrument - instrument that might use that sample
831         */
832        bool InstrumentResourceManager::SampleReferencedByInstrument(::gig::Sample* pSample, ::gig::Instrument* pInstrument) {
833            for (
834                ::gig::Region* pRegion = pInstrument->GetFirstRegion();
835                pRegion; pRegion = pInstrument->GetNextRegion()
836            ) {
837                for (
838                    int i = 0; i < pRegion->DimensionRegions &&
839                    pRegion->pDimensionRegions[i]; i++
840                ) {
841                    if (pRegion->pDimensionRegions[i]->pSample == pSample)
842                        return true;
843                }
844            }
845            return false;
846        }
847    
848        /**
849         * Suspend all gig engines that use the given instrument. This means
850         * completely stopping playback on those engines and killing all their
851         * voices and disk streams. This method will block until all voices AND
852         * their disk streams are finally deleted and the engine turned into a
853         * complete idle loop.
854         *
855         * All @c SuspendEnginesUsing() methods only serve one thread by one and
856         * block all other threads until the current active thread called
857         * @c ResumeAllEngines() .
858         *
859         * @param pInstrument - search criteria
860         */
861        void InstrumentResourceManager::SuspendEnginesUsing(::gig::Instrument* pInstrument) {
862            // make sure no other thread suspends whole engines at the same time
863            suspendedEnginesMutex.Lock();
864            // get all engines that use that same gig::Instrument
865            suspendedEngines = GetEnginesUsing(pInstrument, true/*lock*/);
866            // finally, completely suspend all engines that use that same gig::Instrument
867            std::set<Engine*>::iterator iter = suspendedEngines.begin();
868            std::set<Engine*>::iterator end  = suspendedEngines.end();
869            for (; iter != end; ++iter) (*iter)->SuspendAll();
870        }
871    
872        /**
873         * Suspend all gig engines that use the given instrument file. This means
874         * completely stopping playback on those engines and killing all their
875         * voices and disk streams. This method will block until all voices AND
876         * their disk streams are finally deleted and the engine turned into a
877         * complete idle loop.
878         *
879         * All @c SuspendEnginesUsing() methods only serve one thread by one and
880         * block all other threads until the current active thread called
881         * @c ResumeAllEngines() .
882         *
883         * @param pFile - search criteria
884         */
885        void InstrumentResourceManager::SuspendEnginesUsing(::gig::File* pFile) {
886            // make sure no other thread suspends whole engines at the same time
887            suspendedEnginesMutex.Lock();
888            // get all engines that use that same gig::File
889            suspendedEngines = GetEnginesUsing(pFile, true/*lock*/);
890            // finally, completely suspend all engines that use that same gig::File
891            std::set<Engine*>::iterator iter = suspendedEngines.begin();
892            std::set<Engine*>::iterator end  = suspendedEngines.end();
893            for (; iter != end; ++iter) (*iter)->SuspendAll();
894        }
895    
896        /**
897         * MUST be called after one called one of the @c SuspendEnginesUsing()
898         * methods, to resume normal playback on all previously suspended engines.
899         * As it's only possible for one thread to suspend whole engines at the
900         * same time, this method doesn't take any arguments.
901         */
902        void InstrumentResourceManager::ResumeAllEngines() {
903            // resume all previously completely suspended engines
904            std::set<Engine*>::iterator iter = suspendedEngines.begin();
905            std::set<Engine*>::iterator end  = suspendedEngines.end();
906            for (; iter != end; ++iter) (*iter)->ResumeAll();
907            // no more suspended engines ...
908            suspendedEngines.clear();
909            // allow another thread to suspend whole engines
910            suspendedEnginesMutex.Unlock();
911        }
912    
913    
914    
915      // internal gig file manager      // internal gig file manager
# Line 220  namespace LinuxSampler { namespace gig { Line 925  namespace LinuxSampler { namespace gig {
925    
926      void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {      void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
927          dmsg(1,("Freeing gig file from memory..."));          dmsg(1,("Freeing gig file from memory..."));
928          delete pResource;  
929          delete (::RIFF::File*) pArg;          // Delete as much as possible of the gig file. Some of the
930            // dimension regions and samples may still be in use - these
931            // will be deleted later by the HandBackDimReg function.
932            bool deleteFile = true;
933            ::gig::Instrument* nextInstrument;
934            for (::gig::Instrument* instrument = pResource->GetFirstInstrument() ;
935                 instrument ;
936                 instrument = nextInstrument) {
937                nextInstrument = pResource->GetNextInstrument();
938                bool deleteInstrument = true;
939                ::gig::Region* nextRegion;
940                for (::gig::Region *region = instrument->GetFirstRegion() ;
941                     region ;
942                     region = nextRegion) {
943                    nextRegion = instrument->GetNextRegion();
944                    bool deleteRegion = true;
945                    for (int i = 0 ; i < region->DimensionRegions ; i++)
946                    {
947                        ::gig::DimensionRegion *d = region->pDimensionRegions[i];
948                        std::map< ::gig::DimensionRegion*, region_info_t>::iterator iter = parent->RegionInfo.find(d);
949                        if (iter != parent->RegionInfo.end()) {
950                            region_info_t& dimRegInfo = (*iter).second;
951                            dimRegInfo.file = pResource;
952                            dimRegInfo.pArg = (::RIFF::File*)pArg;
953                            deleteFile = deleteInstrument = deleteRegion = false;
954                        }
955                    }
956                    if (deleteRegion) instrument->DeleteRegion(region);
957                }
958                if (deleteInstrument) pResource->DeleteInstrument(instrument);
959            }
960            if (deleteFile) {
961                delete pResource;
962                delete (::RIFF::File*) pArg;
963            } else {
964                dmsg(2,("keeping some samples that are in use..."));
965                ::gig::Sample* nextSample;
966                for (::gig::Sample* sample = pResource->GetFirstSample() ;
967                     sample ;
968                     sample = nextSample) {
969                    nextSample = pResource->GetNextSample();
970                    if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
971                        pResource->DeleteSample(sample);
972                    }
973                }
974            }
975          dmsg(1,("OK\n"));          dmsg(1,("OK\n"));
976      }      }
977    

Legend:
Removed from v.970  
changed lines
  Added in v.2012

  ViewVC Help
Powered by ViewVC