/[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 1212 by schoenebeck, Tue May 29 23:59:36 2007 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 - 2007 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 "../InstrumentEditorFactory.h"  #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 66  namespace LinuxSampler { namespace gig { Line 69  namespace LinuxSampler { namespace gig {
69    
70          // the instrument we borrowed on behalf of the editor          // the instrument we borrowed on behalf of the editor
71          ::gig::Instrument* pInstrument;          ::gig::Instrument* pInstrument;
72            // the instrument editor we work on behalf
73            InstrumentEditor* pEditor;
74      };      };
75    
76      /**      /**
# Line 89  namespace LinuxSampler { namespace gig { Line 94  namespace LinuxSampler { namespace gig {
94          return Entries();          return Entries();
95      }      }
96    
     InstrumentManager::mode_t InstrumentResourceManager::GetMode(const instrument_id_t& ID) {  
         return static_cast<InstrumentManager::mode_t>(AvailabilityMode(ID));  
     }  
   
     void InstrumentResourceManager::SetMode(const instrument_id_t& ID, InstrumentManager::mode_t Mode) {  
         dmsg(2,("gig::InstrumentResourceManager: setting mode for %s (Index=%d) to %d\n",ID.FileName.c_str(),ID.Index,Mode));  
         SetAvailabilityMode(ID, static_cast<ResourceManager<InstrumentManager::instrument_id_t, ::gig::Instrument>::mode_t>(Mode));  
     }  
   
97      String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {      String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
98          Lock();          Lock();
99          ::gig::Instrument* pInstrument = Resource(ID, false);          ::gig::Instrument* pInstrument = Resource(ID, false);
# Line 106  namespace LinuxSampler { namespace gig { Line 102  namespace LinuxSampler { namespace gig {
102          return res;          return res;
103      }      }
104    
105      String InstrumentResourceManager::GetInstrumentTypeName(instrument_id_t ID) {      String InstrumentResourceManager::GetInstrumentDataStructureName(instrument_id_t ID) {
106          return ::gig::libraryName();          return ::gig::libraryName();
107      }      }
108    
109      String InstrumentResourceManager::GetInstrumentTypeVersion(instrument_id_t ID) {      String InstrumentResourceManager::GetInstrumentDataStructureVersion(instrument_id_t ID) {
110          return ::gig::libraryVersion();          return ::gig::libraryVersion();
111      }      }
112    
113      void InstrumentResourceManager::LaunchInstrumentEditor(instrument_id_t ID) throw (InstrumentManagerException) {      std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::GetInstrumentFileContent(String File) throw (InstrumentManagerException) {
114          const String sDataType    = GetInstrumentTypeName(ID);          ::RIFF::File* riff = NULL;
115          const String sDataVersion = GetInstrumentTypeVersion(ID);          ::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();
143            ::gig::Instrument* pInstrument = Resource(ID, false);
144            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          // find instrument editors capable to handle given instrument
233          std::vector<String> vEditors =          std::vector<String> vEditors =
234              InstrumentEditorFactory::MatchingEditors(sDataType, sDataVersion);              InstrumentEditorFactory::MatchingEditors(sDataType, sDataVersion);
# Line 128  namespace LinuxSampler { namespace gig { Line 240  namespace LinuxSampler { namespace gig {
240          dmsg(1,("Found matching editor '%s' for instrument ('%s', %d) having data structure ('%s','%s')\n",          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()));              vEditors[0].c_str(), ID.FileName.c_str(), ID.Index, sDataType.c_str(), sDataVersion.c_str()));
242          InstrumentEditor* pEditor = InstrumentEditorFactory::Create(vEditors[0]);          InstrumentEditor* pEditor = InstrumentEditorFactory::Create(vEditors[0]);
243          // we want to know when you'll die X| (see OnInstrumentEditorQuit())          // register for receiving notifications from the instrument editor
244          pEditor->AddListener(this);          pEditor->AddListener(this);
245          // create a proxy that reacts on notification on behalf of the editor          // create a proxy that reacts on notification on behalf of the editor
246          InstrumentEditorProxy* pProxy = new InstrumentEditorProxy;          InstrumentEditorProxy* pProxy = new InstrumentEditorProxy;
# Line 136  namespace LinuxSampler { namespace gig { Line 248  namespace LinuxSampler { namespace gig {
248          ::gig::Instrument* pInstrument = Borrow(ID, pProxy);          ::gig::Instrument* pInstrument = Borrow(ID, pProxy);
249          // remember the proxy and instrument for this instrument editor          // remember the proxy and instrument for this instrument editor
250          pProxy->pInstrument = pInstrument;          pProxy->pInstrument = pInstrument;
251            pProxy->pEditor     = pEditor;
252          InstrumentEditorProxiesMutex.Lock();          InstrumentEditorProxiesMutex.Lock();
253          InstrumentEditorProxies[pEditor] = pProxy;          InstrumentEditorProxies.add(pProxy);
254          InstrumentEditorProxiesMutex.Unlock();          InstrumentEditorProxiesMutex.Unlock();
255          // launch the instrument editor for the given instrument          // launch the instrument editor for the given instrument
256          pEditor->Launch(pInstrument, sDataType, sDataVersion);          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();
273    
274            return pEditor;
275      }      }
276    
277      /**      /**
# Line 152  namespace LinuxSampler { namespace gig { Line 283  namespace LinuxSampler { namespace gig {
283       */       */
284      void InstrumentResourceManager::OnInstrumentEditorQuit(InstrumentEditor* pSender) {      void InstrumentResourceManager::OnInstrumentEditorQuit(InstrumentEditor* pSender) {
285          dmsg(1,("InstrumentResourceManager: instrument editor quit, doing cleanup\n"));          dmsg(1,("InstrumentResourceManager: instrument editor quit, doing cleanup\n"));
286          // hand back instrument and free proxy  
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();          InstrumentEditorProxiesMutex.Lock();
293          if (InstrumentEditorProxies.count(pSender)) {          for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
294              InstrumentEditorProxy* pProxy =              InstrumentEditorProxy* pCurProxy =
295                  dynamic_cast<InstrumentEditorProxy*>(                  dynamic_cast<InstrumentEditorProxy*>(
296                      InstrumentEditorProxies[pSender]                      InstrumentEditorProxies[i]
297                  );                  );
298              InstrumentEditorProxies.erase(pSender);              if (pCurProxy->pEditor == pSender) {
299              InstrumentEditorProxiesMutex.Unlock();                  pProxy      = pCurProxy;
300              HandBack(pProxy->pInstrument, pProxy);                  iProxyIndex = i;
301              if (pProxy) delete pProxy;                  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 {          } 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();              InstrumentEditorProxiesMutex.Unlock();
335              std::cerr << "Eeeek, could not find instrument editor proxy, this is a bug!\n" << std::flush;  
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          }          }
         // free the editor  
         InstrumentEditorFactory::Destroy(pSender);  
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 205  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 223  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 = dynamic_cast<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) ? 0 /* don't care for instrument editors */ :              (!pEngineChannel) ? 0 /* don't care for instrument editors */ :
616                  (pEngineChannel->GetEngine()) ?                  (pEngineChannel->GetEngine()) ?
617                      dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()                      dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
618                      : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;                      : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
619          pArg = pEntry;          pArg = pEntry;
620    
# Line 244  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 && 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       * Give back an instrument. This should be used instead of          // TODO: we could delete Region and Instrument here if they have become unused
      * HandBack if there are some dimension regions that are still in  
      * use. (When an instrument is changed, the voices currently  
      * playing is allowed to keep playing with the old instrument  
      * until note off arrives. New notes will use the new instrument.)  
      */  
     void InstrumentResourceManager::HandBackInstrument(::gig::Instrument* pResource, InstrumentConsumer* pConsumer,  
                                                        ::gig::DimensionRegion** dimRegionsInUse) {  
         DimRegInfoMutex.Lock();  
         for (int i = 0 ; dimRegionsInUse[i] ; i++) {  
             DimRegInfo[dimRegionsInUse[i]].refCount++;  
             SampleRefCount[dimRegionsInUse[i]->pSample]++;  
         }  
         HandBack(pResource, pConsumer, true);  
         DimRegInfoMutex.Unlock();  
644      }      }
645        void InstrumentResourceManager::DeleteSampleIfNotUsed(::gig::Sample* pSample, region_info_t* pRegInfo) {
646      /**          ::gig::File* gig = pRegInfo->file;
647       * Give back a dimension region that belongs to an instrument that          ::RIFF::File* riff = static_cast< ::RIFF::File*>(pRegInfo->pArg);
648       * was previously handed back.          if (gig) {
649       */              gig->DeleteSample(pSample);
650      void InstrumentResourceManager::HandBackDimReg(::gig::DimensionRegion* pDimReg) {              if (!gig->GetFirstSample()) {
651          DimRegInfoMutex.Lock();                  dmsg(2,("No more samples in use - freeing gig\n"));
652          dimreg_info_t& dimRegInfo = DimRegInfo[pDimReg];                  delete gig;
653          int dimRegRefCount = --dimRegInfo.refCount;                  delete riff;
         int sampleRefCount = --SampleRefCount[pDimReg->pSample];  
         if (dimRegRefCount == 0) {  
             ::gig::File* gig = dimRegInfo.file;  
             ::RIFF::File* riff = dimRegInfo.riff;  
             DimRegInfo.erase(pDimReg);  
             // TODO: we could delete Region and Instrument here if  
             // they have become unused  
   
             if (sampleRefCount == 0) {  
                 SampleRefCount.erase(pDimReg->pSample);  
   
                 if (gig) {  
                     gig->DeleteSample(pDimReg->pSample);  
                     if (!gig->GetFirstSample()) {  
                         dmsg(2,("No more samples in use - freeing gig\n"));  
                         delete gig;  
                         delete riff;  
                     }  
                 }  
654              }              }
655          }          }
656          DimRegInfoMutex.Unlock();      }
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      /**      /**
# Line 310  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 326  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 343  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 378  namespace LinuxSampler { namespace gig { Line 945  namespace LinuxSampler { namespace gig {
945                  for (int i = 0 ; i < region->DimensionRegions ; i++)                  for (int i = 0 ; i < region->DimensionRegions ; i++)
946                  {                  {
947                      ::gig::DimensionRegion *d = region->pDimensionRegions[i];                      ::gig::DimensionRegion *d = region->pDimensionRegions[i];
948                      std::map< ::gig::DimensionRegion*, dimreg_info_t>::iterator iter = parent->DimRegInfo.find(d);                      std::map< ::gig::DimensionRegion*, region_info_t>::iterator iter = parent->RegionInfo.find(d);
949                      if (iter != parent->DimRegInfo.end()) {                      if (iter != parent->RegionInfo.end()) {
950                          dimreg_info_t& dimRegInfo = (*iter).second;                          region_info_t& dimRegInfo = (*iter).second;
951                          dimRegInfo.file = pResource;                          dimRegInfo.file = pResource;
952                          dimRegInfo.riff = (::RIFF::File*)pArg;                          dimRegInfo.pArg = (::RIFF::File*)pArg;
953                          deleteFile = deleteInstrument = deleteRegion = false;                          deleteFile = deleteInstrument = deleteRegion = false;
954                      }                      }
955                  }                  }

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

  ViewVC Help
Powered by ViewVC