/[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 1852 - (show annotations) (download)
Sun Mar 1 22:22:03 2009 UTC (11 years, 7 months ago) by schoenebeck
File size: 49025 byte(s)
* bugfix: on sample reference changes (instrument editor), only un-cache
  the respective sample if it's really not used by any sampler engine
  anymore
* re-cache samples in case they were changed by an instrument editor, e.g.
  when a sample was added while playing (#82)
* bumped version to 0.5.1.10cvs

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

  ViewVC Help
Powered by ViewVC