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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2185 - (show annotations) (download)
Sun Jun 19 09:09:38 2011 UTC (12 years, 10 months ago) by persson
File size: 46998 byte(s)
* fixed compilation with gcc 4.6.1
* another "make dist" fix, for LV2 plugin
* made --enable-pthread-testcancel default on Mac OS X
* Mac OS X: fixed hanging threads

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

  ViewVC Help
Powered by ViewVC