/[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 2275 - (show annotations) (download)
Mon Sep 19 21:48:45 2011 UTC (9 years, 10 months ago) by schoenebeck
File size: 46753 byte(s)
* Bugfix: When creating MIDI instrument map entries with "PERSISTENT"
  type, the instruments were uselessly precached with zero samples,
  however it still took the full preloading time and on 1st program
  change the respective instrument was completely reloaded again.
* Bumped version to 1.0.0.svn15

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

  ViewVC Help
Powered by ViewVC