/[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 2687 - (hide annotations) (download)
Sun Jan 4 17:16:05 2015 UTC (9 years, 3 months ago) by schoenebeck
File size: 45364 byte(s)
* Instrument editor interface: Changed instrument editor plugin interface,
providing additional informations like the EngineChannel for which the
instrument editor was spawned for. This allows the instrument editors to
interact more actively with the sampler.
* Bumped version (1.0.0.svn60).

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

  ViewVC Help
Powered by ViewVC