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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1662 - (hide annotations) (download)
Sun Feb 3 16:21:38 2008 UTC (16 years, 2 months ago) by schoenebeck
File size: 45325 byte(s)
* the promised "cleanup": rewrote virtual MIDI device's notification a bit
  (using now a safe double buffer approach using "SynchronizedConfig"
  instead of the Trylock() approach previously to synchronize the list of
  virtual MIDI devices)
* bumped version to 0.5.1.2cvs

1 schoenebeck 53 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 schoenebeck 56 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 persson 1646 * Copyright (C) 2005 - 2008 Christian Schoenebeck *
7 schoenebeck 53 * *
8     * This program is free software; you can redistribute it and/or modify *
9     * it under the terms of the GNU General Public License as published by *
10     * the Free Software Foundation; either version 2 of the License, or *
11     * (at your option) any later version. *
12     * *
13     * This program is distributed in the hope that it will be useful, *
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16     * GNU General Public License for more details. *
17     * *
18     * You should have received a copy of the GNU General Public License *
19     * along with this program; if not, write to the Free Software *
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21     * MA 02111-1307 USA *
22     ***************************************************************************/
23    
24     #include <sstream>
25    
26     #include "InstrumentResourceManager.h"
27    
28 schoenebeck 1424 #include "../../common/global_private.h"
29 schoenebeck 1375 #include "../../plugins/InstrumentEditorFactory.h"
30 schoenebeck 1212
31 schoenebeck 411 // We need to know the maximum number of sample points which are going to
32     // be processed for each render cycle of the audio output driver, to know
33     // how much initial sample points we need to cache into RAM. If the given
34     // sampler channel does not have an audio output device assigned yet
35     // though, we simply use this default value.
36     #define GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE 128
37    
38 schoenebeck 53 namespace LinuxSampler { namespace gig {
39    
40 schoenebeck 947 // data stored as long as an instrument resource exists
41     struct instr_entry_t {
42     InstrumentManager::instrument_id_t ID;
43     ::gig::File* pGig;
44     uint MaxSamplesPerCycle; ///< if some engine requests an already allocated instrument with a higher value, we have to reallocate the instrument
45     };
46    
47 schoenebeck 517 // some data needed for the libgig callback function
48     struct progress_callback_arg_t {
49 schoenebeck 947 InstrumentResourceManager* pManager;
50     InstrumentManager::instrument_id_t* pInstrumentKey;
51 schoenebeck 517 };
52    
53 schoenebeck 1212 // we use this to react on events concerning an instrument on behalf of an instrument editor
54     class InstrumentEditorProxy : public InstrumentConsumer {
55     public:
56     virtual void ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
57     //TODO: inform the instrument editor about the pending update
58     }
59    
60     virtual void ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
61     //TODO:: inform the instrument editor about finished update
62     }
63    
64     virtual void OnResourceProgress(float fProgress) {
65     //TODO: inform the instrument editor about the progress of an update
66     }
67    
68     // the instrument we borrowed on behalf of the editor
69     ::gig::Instrument* pInstrument;
70 schoenebeck 1653 // the instrument editor we work on behalf
71     InstrumentEditor* pEditor;
72 schoenebeck 1212 };
73    
74 schoenebeck 517 /**
75     * Callback function which will be called by libgig during loading of
76     * instruments to inform about the current progress. Or to be more
77     * specific; it will be called during the GetInstrument() call.
78     *
79     * @param pProgress - contains current progress value, pointer to the
80     * InstrumentResourceManager instance and
81     * instrument ID
82     */
83     void InstrumentResourceManager::OnInstrumentLoadingProgress(::gig::progress_t* pProgress) {
84     dmsg(7,("gig::InstrumentResourceManager: progress %f%", pProgress->factor));
85     progress_callback_arg_t* pArg = static_cast<progress_callback_arg_t*>(pProgress->custom);
86     // we randomly schedule 90% for the .gig file loading and the remaining 10% later for sample caching
87     const float localProgress = 0.9f * pProgress->factor;
88     pArg->pManager->DispatchResourceProgressEvent(*pArg->pInstrumentKey, localProgress);
89     }
90    
91 schoenebeck 947 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::Instruments() {
92     return Entries();
93     }
94    
95     InstrumentManager::mode_t InstrumentResourceManager::GetMode(const instrument_id_t& ID) {
96     return static_cast<InstrumentManager::mode_t>(AvailabilityMode(ID));
97     }
98    
99     void InstrumentResourceManager::SetMode(const instrument_id_t& ID, InstrumentManager::mode_t Mode) {
100     dmsg(2,("gig::InstrumentResourceManager: setting mode for %s (Index=%d) to %d\n",ID.FileName.c_str(),ID.Index,Mode));
101     SetAvailabilityMode(ID, static_cast<ResourceManager<InstrumentManager::instrument_id_t, ::gig::Instrument>::mode_t>(Mode));
102     }
103    
104     String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
105 schoenebeck 970 Lock();
106     ::gig::Instrument* pInstrument = Resource(ID, false);
107     String res = (pInstrument) ? pInstrument->pInfo->Name : "";
108     Unlock();
109     return res;
110 schoenebeck 947 }
111    
112 schoenebeck 1321 String InstrumentResourceManager::GetInstrumentDataStructureName(instrument_id_t ID) {
113 schoenebeck 1212 return ::gig::libraryName();
114     }
115    
116 schoenebeck 1321 String InstrumentResourceManager::GetInstrumentDataStructureVersion(instrument_id_t ID) {
117 schoenebeck 1212 return ::gig::libraryVersion();
118     }
119    
120 schoenebeck 1525 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::GetInstrumentFileContent(String File) throw (InstrumentManagerException) {
121     ::RIFF::File* riff = NULL;
122     ::gig::File* gig = NULL;
123     try {
124     std::vector<instrument_id_t> result;
125     riff = new ::RIFF::File(File);
126     gig = new ::gig::File(riff);
127     gig->SetAutoLoad(false); // avoid time consuming samples scanning
128     for (int i = 0; gig->GetInstrument(i); i++) {
129     instrument_id_t id;
130     id.FileName = File;
131     id.Index = i;
132     result.push_back(id);
133     }
134     if (gig) delete gig;
135     if (riff) delete riff;
136     return result;
137     } catch (::RIFF::Exception e) {
138     if (gig) delete gig;
139     if (riff) delete riff;
140     throw InstrumentManagerException(e.Message);
141     } catch (...) {
142     if (gig) delete gig;
143     if (riff) delete riff;
144     throw InstrumentManagerException("Unknown exception while trying to parse '" + File + "'");
145     }
146     }
147    
148     InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) {
149     std::vector<instrument_id_t> result;
150     ::RIFF::File* riff = NULL;
151     ::gig::File* gig = NULL;
152     try {
153     riff = new ::RIFF::File(ID.FileName);
154     gig = new ::gig::File(riff);
155     gig->SetAutoLoad(false); // avoid time consuming samples scanning
156     ::gig::Instrument* pInstrument = gig->GetInstrument(ID.Index);
157     if (!pInstrument) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName);
158     instrument_info_t info;
159     if (gig->pVersion) {
160     info.FormatVersion = ToString(gig->pVersion->major);
161     info.Product = gig->pInfo->Product;
162     info.Artists = gig->pInfo->Artists;
163     }
164     info.InstrumentName = pInstrument->pInfo->Name;
165     if (gig) delete gig;
166     if (riff) delete riff;
167     return info;
168     } catch (::RIFF::Exception e) {
169     if (gig) delete gig;
170     if (riff) delete riff;
171     throw InstrumentManagerException(e.Message);
172     } catch (...) {
173     if (gig) delete gig;
174     if (riff) delete riff;
175     throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'");
176     }
177     }
178    
179 schoenebeck 1212 void InstrumentResourceManager::LaunchInstrumentEditor(instrument_id_t ID) throw (InstrumentManagerException) {
180 schoenebeck 1321 const String sDataType = GetInstrumentDataStructureName(ID);
181     const String sDataVersion = GetInstrumentDataStructureVersion(ID);
182 schoenebeck 1212 // find instrument editors capable to handle given instrument
183     std::vector<String> vEditors =
184     InstrumentEditorFactory::MatchingEditors(sDataType, sDataVersion);
185     if (!vEditors.size())
186     throw InstrumentManagerException(
187     "There is no instrument editor capable to handle this instrument"
188     );
189     // simply use the first editor in the result set
190     dmsg(1,("Found matching editor '%s' for instrument ('%s', %d) having data structure ('%s','%s')\n",
191     vEditors[0].c_str(), ID.FileName.c_str(), ID.Index, sDataType.c_str(), sDataVersion.c_str()));
192     InstrumentEditor* pEditor = InstrumentEditorFactory::Create(vEditors[0]);
193 schoenebeck 1321 // register for receiving notifications from the instrument editor
194 schoenebeck 1212 pEditor->AddListener(this);
195     // create a proxy that reacts on notification on behalf of the editor
196     InstrumentEditorProxy* pProxy = new InstrumentEditorProxy;
197     // borrow the instrument on behalf of the instrument editor
198     ::gig::Instrument* pInstrument = Borrow(ID, pProxy);
199     // remember the proxy and instrument for this instrument editor
200     pProxy->pInstrument = pInstrument;
201 schoenebeck 1653 pProxy->pEditor = pEditor;
202 schoenebeck 1212 InstrumentEditorProxiesMutex.Lock();
203 schoenebeck 1653 InstrumentEditorProxies.add(pProxy);
204 schoenebeck 1212 InstrumentEditorProxiesMutex.Unlock();
205     // launch the instrument editor for the given instrument
206     pEditor->Launch(pInstrument, sDataType, sDataVersion);
207 schoenebeck 1659
208     // register the instrument editor as virtual MIDI device as well ...
209     VirtualMidiDevice* pVirtualMidiDevice =
210     dynamic_cast<VirtualMidiDevice*>(pEditor);
211     if (!pVirtualMidiDevice) {
212     std::cerr << "Instrument editor not a virtual MIDI device\n" << std::flush;
213     return;
214     }
215     // 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 ? )
216     Lock();
217     std::set<gig::EngineChannel*> engineChannels =
218     GetEngineChannelsUsing(pInstrument, false/*don't lock again*/);
219     std::set<gig::EngineChannel*>::iterator iter = engineChannels.begin();
220     std::set<gig::EngineChannel*>::iterator end = engineChannels.end();
221     for (; iter != end; ++iter) (*iter)->Connect(pVirtualMidiDevice);
222     Unlock();
223 schoenebeck 1212 }
224    
225     /**
226     * Will be called by the respective instrument editor once it left its
227     * Main() loop. That way we can handle cleanup before its thread finally
228     * dies.
229     *
230     * @param pSender - instrument editor that stops execution
231     */
232     void InstrumentResourceManager::OnInstrumentEditorQuit(InstrumentEditor* pSender) {
233     dmsg(1,("InstrumentResourceManager: instrument editor quit, doing cleanup\n"));
234 schoenebeck 1659
235     ::gig::Instrument* pInstrument = NULL;
236     InstrumentEditorProxy* pProxy = NULL;
237     int iProxyIndex = -1;
238    
239     // first find the editor proxy entry for this editor
240 schoenebeck 1212 InstrumentEditorProxiesMutex.Lock();
241 schoenebeck 1653 for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
242 schoenebeck 1659 InstrumentEditorProxy* pCurProxy =
243 schoenebeck 1212 dynamic_cast<InstrumentEditorProxy*>(
244 schoenebeck 1653 InstrumentEditorProxies[i]
245 schoenebeck 1212 );
246 schoenebeck 1659 if (pCurProxy->pEditor == pSender) {
247     pProxy = pCurProxy;
248     iProxyIndex = i;
249     pInstrument = pCurProxy->pInstrument;
250 schoenebeck 1653 }
251 schoenebeck 1212 }
252 schoenebeck 1653 InstrumentEditorProxiesMutex.Unlock();
253 persson 1455
254 schoenebeck 1659 if (!pProxy) {
255     std::cerr << "Eeeek, could not find instrument editor proxy, "
256     "this is a bug!\n" << std::flush;
257     return;
258     }
259    
260     // now unregister editor as not being available as a virtual MIDI device anymore
261     VirtualMidiDevice* pVirtualMidiDevice =
262     dynamic_cast<VirtualMidiDevice*>(pSender);
263     if (pVirtualMidiDevice) {
264     Lock();
265     // NOTE: see note in LaunchInstrumentEditor()
266     std::set<gig::EngineChannel*> engineChannels =
267     GetEngineChannelsUsing(pInstrument, false/*don't lock again*/);
268     std::set<gig::EngineChannel*>::iterator iter = engineChannels.begin();
269     std::set<gig::EngineChannel*>::iterator end = engineChannels.end();
270     for (; iter != end; ++iter) (*iter)->Disconnect(pVirtualMidiDevice);
271     Unlock();
272     } else {
273     std::cerr << "Could not unregister editor as not longer acting as "
274     "virtual MIDI device. Wasn't it registered?\n"
275     << std::flush;
276     }
277    
278     // finally delete proxy entry and hand back instrument
279     if (pInstrument) {
280     InstrumentEditorProxiesMutex.Lock();
281     InstrumentEditorProxies.remove(iProxyIndex);
282     InstrumentEditorProxiesMutex.Unlock();
283    
284     HandBack(pInstrument, pProxy);
285     delete pProxy;
286     }
287    
288 persson 1455 // Note that we don't need to free the editor here. As it
289     // derives from Thread, it will delete itself when the thread
290     // dies.
291 schoenebeck 1212 }
292    
293 schoenebeck 1662 #if 0 // currently unused :
294 schoenebeck 1653 /**
295     * Try to inform the respective instrument editor(s), that a note on
296     * event just occured. This method is called by the MIDI thread. If any
297     * obstacles are in the way (e.g. if a wait for an unlock would be
298     * required) we give up immediately, since the RT safeness of the MIDI
299     * thread has absolute priority.
300     */
301     void InstrumentResourceManager::TrySendNoteOnToEditors(uint8_t Key, uint8_t Velocity, ::gig::Instrument* pInstrument) {
302     const bool bGotLock = InstrumentEditorProxiesMutex.Trylock(); // naively assumes RT safe implementation
303     if (!bGotLock) return; // hell, forget it, not worth the hassle
304     for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
305     InstrumentEditorProxy* pProxy =
306     dynamic_cast<InstrumentEditorProxy*>(
307     InstrumentEditorProxies[i]
308     );
309     if (pProxy->pInstrument == pInstrument)
310 schoenebeck 1659 pProxy->pEditor->SendNoteOnToDevice(Key, Velocity);
311 schoenebeck 1653 }
312     InstrumentEditorProxiesMutex.Unlock(); // naively assumes RT safe implementation
313     }
314    
315     /**
316     * Try to inform the respective instrument editor(s), that a note off
317     * event just occured. This method is called by the MIDI thread. If any
318     * obstacles are in the way (e.g. if a wait for an unlock would be
319     * required) we give up immediately, since the RT safeness of the MIDI
320     * thread has absolute priority.
321     */
322     void InstrumentResourceManager::TrySendNoteOffToEditors(uint8_t Key, uint8_t Velocity, ::gig::Instrument* pInstrument) {
323     const bool bGotLock = InstrumentEditorProxiesMutex.Trylock(); // naively assumes RT safe implementation
324     if (!bGotLock) return; // hell, forget it, not worth the hassle
325     for (int i = 0; i < InstrumentEditorProxies.size(); i++) {
326     InstrumentEditorProxy* pProxy =
327     dynamic_cast<InstrumentEditorProxy*>(
328     InstrumentEditorProxies[i]
329     );
330     if (pProxy->pInstrument == pInstrument)
331 schoenebeck 1659 pProxy->pEditor->SendNoteOffToDevice(Key, Velocity);
332 schoenebeck 1653 }
333     InstrumentEditorProxiesMutex.Unlock(); // naively assumes RT safe implementation
334     }
335 schoenebeck 1662 #endif // unused
336 schoenebeck 1653
337 schoenebeck 1321 void InstrumentResourceManager::OnSamplesToBeRemoved(std::set<void*> Samples, InstrumentEditor* pSender) {
338     if (Samples.empty()) {
339     std::cerr << "gig::InstrumentResourceManager: WARNING, "
340     "OnSamplesToBeRemoved() called with empty list, this "
341     "is a bug!\n" << std::flush;
342     return;
343     }
344     // TODO: ATM we assume here that all samples are from the same file
345     ::gig::Sample* pFirstSample = (::gig::Sample*) *Samples.begin();
346     ::gig::File* pCriticalFile = dynamic_cast< ::gig::File*>(pFirstSample->GetParent());
347     // completely suspend all engines that use that same file
348     SuspendEnginesUsing(pCriticalFile);
349     }
350    
351     void InstrumentResourceManager::OnSamplesRemoved(InstrumentEditor* pSender) {
352     // resume all previously, completely suspended engines
353     // (we don't have to un-cache the removed samples here, since that is
354     // automatically done by the gig::Sample destructor)
355     ResumeAllEngines();
356     }
357    
358     void InstrumentResourceManager::OnDataStructureToBeChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
359     //TODO: remove code duplication
360     if (sStructType == "gig::File") {
361     // completely suspend all engines that use that file
362     ::gig::File* pFile = (::gig::File*) pStruct;
363     SuspendEnginesUsing(pFile);
364     } else if (sStructType == "gig::Instrument") {
365     // completely suspend all engines that use that instrument
366     ::gig::Instrument* pInstrument = (::gig::Instrument*) pStruct;
367     SuspendEnginesUsing(pInstrument);
368     } else if (sStructType == "gig::Region") {
369     // only advice the engines to suspend the given region, so they'll
370     // only ignore that region (and probably already other suspended
371     // ones), but beside that continue normal playback
372     ::gig::Region* pRegion = (::gig::Region*) pStruct;
373     ::gig::Instrument* pInstrument =
374     (::gig::Instrument*) pRegion->GetParent();
375     Lock();
376     std::set<gig::Engine*> engines =
377     GetEnginesUsing(pInstrument, false/*don't lock again*/);
378     std::set<gig::Engine*>::iterator iter = engines.begin();
379     std::set<gig::Engine*>::iterator end = engines.end();
380     for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
381     Unlock();
382     } else if (sStructType == "gig::DimensionRegion") {
383     // only advice the engines to suspend the given DimensionRegions's
384     // parent region, so they'll only ignore that region (and probably
385     // already other suspended ones), but beside that continue normal
386     // playback
387     ::gig::DimensionRegion* pDimReg =
388     (::gig::DimensionRegion*) pStruct;
389     ::gig::Region* pRegion = pDimReg->GetParent();
390     ::gig::Instrument* pInstrument =
391     (::gig::Instrument*) pRegion->GetParent();
392     Lock();
393     std::set<gig::Engine*> engines =
394     GetEnginesUsing(pInstrument, false/*don't lock again*/);
395     std::set<gig::Engine*>::iterator iter = engines.begin();
396     std::set<gig::Engine*>::iterator end = engines.end();
397     for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
398     Unlock();
399     } else {
400     std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
401     "structure '" << sStructType << "' requested to be "
402     "suspended by instrument editor. This is a bug!\n"
403     << std::flush;
404     //TODO: we should inform the instrument editor that something seriously went wrong
405     }
406     }
407    
408     void InstrumentResourceManager::OnDataStructureChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
409     //TODO: remove code duplication
410     if (sStructType == "gig::File") {
411     // resume all previously suspended engines
412     ResumeAllEngines();
413     } else if (sStructType == "gig::Instrument") {
414     // resume all previously suspended engines
415     ResumeAllEngines();
416     } else if (sStructType == "gig::Region") {
417     // advice the engines to resume the given region, that is to
418     // using it for playback again
419     ::gig::Region* pRegion = (::gig::Region*) pStruct;
420     ::gig::Instrument* pInstrument =
421     (::gig::Instrument*) pRegion->GetParent();
422     Lock();
423     std::set<gig::Engine*> engines =
424     GetEnginesUsing(pInstrument, false/*don't lock again*/);
425     std::set<gig::Engine*>::iterator iter = engines.begin();
426     std::set<gig::Engine*>::iterator end = engines.end();
427     for (; iter != end; ++iter) (*iter)->Resume(pRegion);
428     Unlock();
429     } else if (sStructType == "gig::DimensionRegion") {
430     // advice the engines to resume the given DimensionRegion's parent
431     // region, that is to using it for playback again
432     ::gig::DimensionRegion* pDimReg =
433     (::gig::DimensionRegion*) pStruct;
434     ::gig::Region* pRegion = pDimReg->GetParent();
435     ::gig::Instrument* pInstrument =
436     (::gig::Instrument*) pRegion->GetParent();
437     Lock();
438     std::set<gig::Engine*> engines =
439     GetEnginesUsing(pInstrument, false/*don't lock again*/);
440     std::set<gig::Engine*>::iterator iter = engines.begin();
441     std::set<gig::Engine*>::iterator end = engines.end();
442     for (; iter != end; ++iter) (*iter)->Resume(pRegion);
443     Unlock();
444     } else {
445     std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
446     "structure '" << sStructType << "' requested to be "
447     "resumed by instrument editor. This is a bug!\n"
448     << std::flush;
449     //TODO: we should inform the instrument editor that something seriously went wrong
450     }
451     }
452    
453     void InstrumentResourceManager::OnSampleReferenceChanged(void* pOldSample, void* pNewSample, InstrumentEditor* pSender) {
454     // uncache old sample in case it's not used by anybody anymore
455     if (pOldSample) {
456     Lock();
457     ::gig::Sample* pSample = (::gig::Sample*) pOldSample;
458     ::gig::File* pFile = (::gig::File*) pSample->GetParent();
459     std::vector< ::gig::Instrument*> instruments =
460     GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
461     for (int i = 0; i < instruments.size(); i++)
462     if (!SampleReferencedByInstrument(pSample, instruments[i]))
463     UncacheInitialSamples(pSample);
464     Unlock();
465     }
466     // make sure new sample reference is cached
467     if (pNewSample) {
468     Lock();
469     ::gig::Sample* pSample = (::gig::Sample*) pNewSample;
470     ::gig::File* pFile = (::gig::File*) pSample->GetParent();
471     // get all engines that use that same gig::File
472     std::set<gig::Engine*> engines = GetEnginesUsing(pFile, false/*don't lock again*/);
473     std::set<gig::Engine*>::iterator iter = engines.begin();
474     std::set<gig::Engine*>::iterator end = engines.end();
475     for (; iter != end; ++iter)
476     CacheInitialSamples(pSample, *iter);
477     Unlock();
478     }
479     }
480    
481 schoenebeck 53 ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
482 schoenebeck 1321 // get gig file from internal gig file manager
483 schoenebeck 947 ::gig::File* pGig = Gigs.Borrow(Key.FileName, (GigConsumer*) Key.Index); // conversion kinda hackish :/
484 schoenebeck 53
485 schoenebeck 517 // we pass this to the progress callback mechanism of libgig
486     progress_callback_arg_t callbackArg;
487     callbackArg.pManager = this;
488     callbackArg.pInstrumentKey = &Key;
489    
490     ::gig::progress_t progress;
491     progress.callback = OnInstrumentLoadingProgress;
492     progress.custom = &callbackArg;
493    
494 schoenebeck 947 dmsg(1,("Loading gig instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
495     ::gig::Instrument* pInstrument = pGig->GetInstrument(Key.Index, &progress);
496 schoenebeck 53 if (!pInstrument) {
497     std::stringstream msg;
498 schoenebeck 947 msg << "There's no instrument with index " << Key.Index << ".";
499 schoenebeck 1212 throw InstrumentManagerException(msg.str());
500 schoenebeck 53 }
501     pGig->GetFirstSample(); // just to force complete instrument loading
502     dmsg(1,("OK\n"));
503    
504     // cache initial samples points (for actually needed samples)
505     dmsg(1,("Caching initial samples..."));
506 schoenebeck 517 uint iRegion = 0; // just for progress calculation
507 schoenebeck 53 ::gig::Region* pRgn = pInstrument->GetFirstRegion();
508     while (pRgn) {
509 schoenebeck 517 // we randomly schedule 90% for the .gig file loading and the remaining 10% now for sample caching
510     const float localProgress = 0.9f + 0.1f * (float) iRegion / (float) pInstrument->Regions;
511 schoenebeck 947 DispatchResourceProgressEvent(Key, localProgress);
512    
513 schoenebeck 354 if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {
514 schoenebeck 53 dmsg(2,("C"));
515 schoenebeck 947 CacheInitialSamples(pRgn->GetSample(), (gig::EngineChannel*) pConsumer);
516 schoenebeck 53 }
517     for (uint i = 0; i < pRgn->DimensionRegions; i++) {
518 schoenebeck 947 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, (gig::EngineChannel*) pConsumer);
519 schoenebeck 53 }
520    
521     pRgn = pInstrument->GetNextRegion();
522 schoenebeck 517 iRegion++;
523 schoenebeck 53 }
524     dmsg(1,("OK\n"));
525 schoenebeck 517 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
526 schoenebeck 53
527     // we need the following for destruction later
528     instr_entry_t* pEntry = new instr_entry_t;
529 schoenebeck 947 pEntry->ID.FileName = Key.FileName;
530     pEntry->ID.Index = Key.Index;
531 schoenebeck 53 pEntry->pGig = pGig;
532 persson 438
533 schoenebeck 1212 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
534 schoenebeck 411 // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'
535     pEntry->MaxSamplesPerCycle =
536 schoenebeck 1212 (!pEngineChannel) ? 0 /* don't care for instrument editors */ :
537     (pEngineChannel->GetEngine()) ?
538     dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
539     : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
540 schoenebeck 53 pArg = pEntry;
541    
542     return pInstrument;
543     }
544    
545     void InstrumentResourceManager::Destroy( ::gig::Instrument* pResource, void* pArg) {
546     instr_entry_t* pEntry = (instr_entry_t*) pArg;
547 schoenebeck 947 // we don't need the .gig file here anymore
548     Gigs.HandBack(pEntry->pGig, (GigConsumer*) pEntry->ID.Index); // conversion kinda hackish :/
549 schoenebeck 53 delete pEntry;
550     }
551    
552     void InstrumentResourceManager::OnBorrow(::gig::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) {
553     instr_entry_t* pEntry = (instr_entry_t*) pArg;
554 schoenebeck 411 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
555     uint maxSamplesPerCycle =
556 schoenebeck 1212 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
557 schoenebeck 411 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
558     if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
559 schoenebeck 53 Update(pResource, pConsumer);
560     }
561     }
562    
563     /**
564 persson 1038 * Give back an instrument. This should be used instead of
565     * HandBack if there are some dimension regions that are still in
566     * use. (When an instrument is changed, the voices currently
567 persson 1646 * playing are allowed to keep playing with the old instrument
568 persson 1038 * until note off arrives. New notes will use the new instrument.)
569     */
570     void InstrumentResourceManager::HandBackInstrument(::gig::Instrument* pResource, InstrumentConsumer* pConsumer,
571 persson 1646 RTList< ::gig::DimensionRegion*>* pDimRegionsInUse) {
572 persson 1038 DimRegInfoMutex.Lock();
573 persson 1646 for (RTList< ::gig::DimensionRegion*>::Iterator i = pDimRegionsInUse->first() ; i != pDimRegionsInUse->end() ; i++) {
574     DimRegInfo[*i].refCount++;
575     SampleRefCount[(*i)->pSample]++;
576 persson 1038 }
577     HandBack(pResource, pConsumer, true);
578     DimRegInfoMutex.Unlock();
579     }
580    
581     /**
582     * Give back a dimension region that belongs to an instrument that
583     * was previously handed back.
584     */
585     void InstrumentResourceManager::HandBackDimReg(::gig::DimensionRegion* pDimReg) {
586     DimRegInfoMutex.Lock();
587     dimreg_info_t& dimRegInfo = DimRegInfo[pDimReg];
588     int dimRegRefCount = --dimRegInfo.refCount;
589     int sampleRefCount = --SampleRefCount[pDimReg->pSample];
590     if (dimRegRefCount == 0) {
591     ::gig::File* gig = dimRegInfo.file;
592     ::RIFF::File* riff = dimRegInfo.riff;
593     DimRegInfo.erase(pDimReg);
594     // TODO: we could delete Region and Instrument here if
595     // they have become unused
596    
597     if (sampleRefCount == 0) {
598     SampleRefCount.erase(pDimReg->pSample);
599    
600     if (gig) {
601     gig->DeleteSample(pDimReg->pSample);
602     if (!gig->GetFirstSample()) {
603     dmsg(2,("No more samples in use - freeing gig\n"));
604     delete gig;
605     delete riff;
606     }
607     }
608     }
609     }
610     DimRegInfoMutex.Unlock();
611     }
612    
613     /**
614 schoenebeck 1321 * Just a wrapper around the other @c CacheInitialSamples() method.
615     *
616     * @param pSample - points to the sample to be cached
617     * @param pEngine - pointer to Gig Engine Channel which caused this call
618     * (may be NULL, in this case default amount of samples
619     * will be cached)
620     */
621     void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::EngineChannel* pEngineChannel) {
622     gig::Engine* pEngine =
623     (pEngineChannel && pEngineChannel->GetEngine()) ?
624     dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine()) : NULL;
625     CacheInitialSamples(pSample, pEngine);
626     }
627    
628     /**
629 schoenebeck 53 * Caches a certain size at the beginning of the given sample in RAM. If the
630     * sample is very short, the whole sample will be loaded into RAM and thus
631     * no disk streaming is needed for this sample. Caching an initial part of
632     * samples is needed to compensate disk reading latency.
633     *
634     * @param pSample - points to the sample to be cached
635 schoenebeck 1321 * @param pEngine - pointer to Gig Engine which caused this call
636     * (may be NULL, in this case default amount of samples
637     * will be cached)
638 schoenebeck 53 */
639 schoenebeck 1321 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::Engine* pEngine) {
640 schoenebeck 420 if (!pSample) {
641 persson 438 dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
642 schoenebeck 420 return;
643     }
644     if (!pSample->SamplesTotal) return; // skip zero size samples
645 persson 438
646 schoenebeck 554 if (pSample->SamplesTotal <= CONFIG_PRELOAD_SAMPLES) {
647 schoenebeck 53 // Sample is too short for disk streaming, so we load the whole
648 schoenebeck 554 // sample into RAM and place 'pAudioIO->FragmentSize << CONFIG_MAX_PITCH'
649 schoenebeck 53 // number of '0' samples (silence samples) behind the official buffer
650     // border, to allow the interpolator do it's work even at the end of
651 persson 438 // the sample.
652 schoenebeck 411 const uint maxSamplesPerCycle =
653 schoenebeck 1321 (pEngine) ? pEngine->pAudioOutputDevice->MaxSamplesPerCycle()
654     : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
655 schoenebeck 554 const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;
656 schoenebeck 420 const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;
657     if (currentlyCachedSilenceSamples < neededSilenceSamples) {
658     dmsg(3,("Caching whole sample (sample name: \"%s\", sample size: %d)\n", pSample->pInfo->Name.c_str(), pSample->SamplesTotal));
659     ::gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(neededSilenceSamples);
660     dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
661     }
662 schoenebeck 53 }
663 schoenebeck 554 else { // we only cache CONFIG_PRELOAD_SAMPLES and stream the other sample points from disk
664     if (!pSample->GetCache().Size) pSample->LoadSampleData(CONFIG_PRELOAD_SAMPLES);
665 schoenebeck 53 }
666    
667     if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
668     }
669    
670 schoenebeck 1321 void InstrumentResourceManager::UncacheInitialSamples(::gig::Sample* pSample) {
671     dmsg(1,("Uncaching sample %x\n",pSample));
672     if (pSample->GetCache().Size) pSample->ReleaseSampleData();
673     }
674 schoenebeck 53
675 schoenebeck 1321 /**
676     * Returns a list with all instruments currently in use, that are part of
677     * the given file.
678     *
679     * @param pFile - search criteria
680     * @param bLock - whether we should lock (mutex) the instrument manager
681     * during this call and unlock at the end of this call
682     */
683     std::vector< ::gig::Instrument*> InstrumentResourceManager::GetInstrumentsCurrentlyUsedOf(::gig::File* pFile, bool bLock) {
684     if (bLock) Lock();
685     std::vector< ::gig::Instrument*> result;
686     std::vector< ::gig::Instrument*> allInstruments = Resources(false/*don't lock again*/);
687     for (int i = 0; i < allInstruments.size(); i++)
688     if (
689     (::gig::File*) allInstruments[i]->GetParent()
690     == pFile
691     ) result.push_back(allInstruments[i]);
692     if (bLock) Unlock();
693     return result;
694     }
695 schoenebeck 53
696 schoenebeck 1321 /**
697 schoenebeck 1659 * Returns a list with all gig engine channels that are currently using
698     * the given instrument.
699     *
700     * @param pInstrument - search criteria
701     * @param bLock - whether we should lock (mutex) the instrument manager
702     * during this call and unlock at the end of this call
703     */
704     std::set<gig::EngineChannel*> InstrumentResourceManager::GetEngineChannelsUsing(::gig::Instrument* pInstrument, bool bLock) {
705     if (bLock) Lock();
706     std::set<gig::EngineChannel*> result;
707     std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(pInstrument);
708     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
709     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
710     for (; iter != end; ++iter) {
711     gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(*iter);
712     if (!pEngineChannel) continue;
713     result.insert(pEngineChannel);
714     }
715     if (bLock) Unlock();
716     return result;
717     }
718    
719     /**
720 schoenebeck 1321 * Returns a list with all gig Engines that are currently using the given
721     * instrument.
722     *
723     * @param pInstrument - search criteria
724     * @param bLock - whether we should lock (mutex) the instrument manager
725     * during this call and unlock at the end of this call
726     */
727     std::set<gig::Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::Instrument* pInstrument, bool bLock) {
728     if (bLock) Lock();
729     std::set<gig::Engine*> result;
730     std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(pInstrument);
731     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
732     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
733     for (; iter != end; ++iter) {
734     gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(*iter);
735     if (!pEngineChannel) continue;
736     gig::Engine* pEngine = dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine());
737     if (!pEngine) continue;
738     result.insert(pEngine);
739     }
740     if (bLock) Unlock();
741     return result;
742     }
743    
744     /**
745     * Returns a list with all gig Engines that are currently using an
746     * instrument that is part of the given instrument file.
747     *
748     * @param pFile - search criteria
749     * @param bLock - whether we should lock (mutex) the instrument manager
750     * during this call and unlock at the end of this call
751     */
752     std::set<gig::Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::File* pFile, bool bLock) {
753     if (bLock) Lock();
754     // get all instruments (currently in usage) that use that same gig::File
755     std::vector< ::gig::Instrument*> instrumentsOfInterest =
756     GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
757    
758     // get all engines that use that same gig::File
759     std::set<gig::Engine*> result;
760     {
761     for (int i = 0; i < instrumentsOfInterest.size(); i++) {
762     std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(instrumentsOfInterest[i]);
763     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
764     std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
765     for (; iter != end; ++iter) {
766     gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(*iter);
767     if (!pEngineChannel) continue;
768     gig::Engine* pEngine = dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine());
769     if (!pEngine) continue;
770     // the unique, sorted container std::set makes
771     // sure we won't have duplicates
772     result.insert(pEngine);
773     }
774     }
775     }
776     if (bLock) Unlock();
777     return result;
778     }
779    
780     /**
781     * Returns @c true in case the given sample is referenced somewhere by the
782     * given instrument, @c false otherwise.
783     *
784     * @param pSample - sample reference
785     * @param pInstrument - instrument that might use that sample
786     */
787     bool InstrumentResourceManager::SampleReferencedByInstrument(::gig::Sample* pSample, ::gig::Instrument* pInstrument) {
788     for (
789     ::gig::Region* pRegion = pInstrument->GetFirstRegion();
790     pRegion; pRegion = pInstrument->GetNextRegion()
791     ) {
792     for (
793     int i = 0; i < pRegion->DimensionRegions &&
794     pRegion->pDimensionRegions[i]; i++
795     ) {
796     if (pRegion->pDimensionRegions[i]->pSample == pSample)
797     return true;
798     }
799     }
800     return false;
801     }
802    
803     /**
804     * Suspend all gig engines that use the given instrument. This means
805     * completely stopping playback on those engines and killing all their
806     * voices and disk streams. This method will block until all voices AND
807     * their disk streams are finally deleted and the engine turned into a
808     * complete idle loop.
809     *
810     * All @c SuspendEnginesUsing() methods only serve one thread by one and
811     * block all other threads until the current active thread called
812     * @c ResumeAllEngines() .
813     *
814     * @param pInstrument - search criteria
815     */
816     void InstrumentResourceManager::SuspendEnginesUsing(::gig::Instrument* pInstrument) {
817     // make sure no other thread suspends whole engines at the same time
818     suspendedEnginesMutex.Lock();
819     // get all engines that use that same gig::Instrument
820     suspendedEngines = GetEnginesUsing(pInstrument, true/*lock*/);
821     // finally, completely suspend all engines that use that same gig::Instrument
822     std::set<gig::Engine*>::iterator iter = suspendedEngines.begin();
823     std::set<gig::Engine*>::iterator end = suspendedEngines.end();
824     for (; iter != end; ++iter) (*iter)->SuspendAll();
825     }
826    
827     /**
828     * Suspend all gig engines that use the given instrument file. This means
829     * completely stopping playback on those engines and killing all their
830     * voices and disk streams. This method will block until all voices AND
831     * their disk streams are finally deleted and the engine turned into a
832     * complete idle loop.
833     *
834     * All @c SuspendEnginesUsing() methods only serve one thread by one and
835     * block all other threads until the current active thread called
836     * @c ResumeAllEngines() .
837     *
838     * @param pFile - search criteria
839     */
840     void InstrumentResourceManager::SuspendEnginesUsing(::gig::File* pFile) {
841     // make sure no other thread suspends whole engines at the same time
842     suspendedEnginesMutex.Lock();
843     // get all engines that use that same gig::File
844     suspendedEngines = GetEnginesUsing(pFile, true/*lock*/);
845     // finally, completely suspend all engines that use that same gig::File
846     std::set<gig::Engine*>::iterator iter = suspendedEngines.begin();
847     std::set<gig::Engine*>::iterator end = suspendedEngines.end();
848     for (; iter != end; ++iter) (*iter)->SuspendAll();
849     }
850    
851     /**
852     * MUST be called after one called one of the @c SuspendEnginesUsing()
853     * methods, to resume normal playback on all previously suspended engines.
854     * As it's only possible for one thread to suspend whole engines at the
855     * same time, this method doesn't take any arguments.
856     */
857     void InstrumentResourceManager::ResumeAllEngines() {
858     // resume all previously completely suspended engines
859     std::set<Engine*>::iterator iter = suspendedEngines.begin();
860     std::set<Engine*>::iterator end = suspendedEngines.end();
861     for (; iter != end; ++iter) (*iter)->ResumeAll();
862     // no more suspended engines ...
863     suspendedEngines.clear();
864     // allow another thread to suspend whole engines
865     suspendedEnginesMutex.Unlock();
866     }
867    
868    
869    
870 schoenebeck 53 // internal gig file manager
871    
872     ::gig::File* InstrumentResourceManager::GigResourceManager::Create(String Key, GigConsumer* pConsumer, void*& pArg) {
873     dmsg(1,("Loading gig file \'%s\'...", Key.c_str()));
874     ::RIFF::File* pRIFF = new ::RIFF::File(Key);
875     ::gig::File* pGig = new ::gig::File(pRIFF);
876     pArg = pRIFF;
877     dmsg(1,("OK\n"));
878     return pGig;
879     }
880    
881     void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
882     dmsg(1,("Freeing gig file from memory..."));
883 persson 1038
884     // Delete as much as possible of the gig file. Some of the
885     // dimension regions and samples may still be in use - these
886     // will be deleted later by the HandBackDimReg function.
887     bool deleteFile = true;
888     ::gig::Instrument* nextInstrument;
889     for (::gig::Instrument* instrument = pResource->GetFirstInstrument() ;
890     instrument ;
891     instrument = nextInstrument) {
892     nextInstrument = pResource->GetNextInstrument();
893     bool deleteInstrument = true;
894     ::gig::Region* nextRegion;
895     for (::gig::Region *region = instrument->GetFirstRegion() ;
896     region ;
897     region = nextRegion) {
898     nextRegion = instrument->GetNextRegion();
899     bool deleteRegion = true;
900     for (int i = 0 ; i < region->DimensionRegions ; i++)
901     {
902     ::gig::DimensionRegion *d = region->pDimensionRegions[i];
903     std::map< ::gig::DimensionRegion*, dimreg_info_t>::iterator iter = parent->DimRegInfo.find(d);
904     if (iter != parent->DimRegInfo.end()) {
905     dimreg_info_t& dimRegInfo = (*iter).second;
906     dimRegInfo.file = pResource;
907     dimRegInfo.riff = (::RIFF::File*)pArg;
908     deleteFile = deleteInstrument = deleteRegion = false;
909     }
910     }
911     if (deleteRegion) instrument->DeleteRegion(region);
912     }
913     if (deleteInstrument) pResource->DeleteInstrument(instrument);
914     }
915     if (deleteFile) {
916     delete pResource;
917     delete (::RIFF::File*) pArg;
918     } else {
919     dmsg(2,("keeping some samples that are in use..."));
920     ::gig::Sample* nextSample;
921     for (::gig::Sample* sample = pResource->GetFirstSample() ;
922     sample ;
923     sample = nextSample) {
924     nextSample = pResource->GetNextSample();
925     if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
926     pResource->DeleteSample(sample);
927     }
928     }
929     }
930 schoenebeck 53 dmsg(1,("OK\n"));
931     }
932    
933     }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC