/[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 1321 - (show annotations) (download)
Tue Sep 4 01:12:49 2007 UTC (16 years, 7 months ago) by schoenebeck
File size: 36654 byte(s)
* added highly experimental code for synchronizing instrument editors
  hosted in the sampler's process to safely edit instruments while playing
  without a crash (hopefully) by either suspending single regions wherever
  possible or - if unavoidable - whole engine(s)
* disk thread: queue sizes are now proportional to CONFIG_MAX_STREAMS
  instead of fix values
* removed legacy Makefiles in meanwhile deleted src/lib directory and its
  subdirectories
* bumped version to 0.4.0.7cvs

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2007 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 "../InstrumentEditorFactory.h"
29
30 // We need to know the maximum number of sample points which are going to
31 // be processed for each render cycle of the audio output driver, to know
32 // how much initial sample points we need to cache into RAM. If the given
33 // sampler channel does not have an audio output device assigned yet
34 // though, we simply use this default value.
35 #define GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE 128
36
37 namespace LinuxSampler { namespace gig {
38
39 // data stored as long as an instrument resource exists
40 struct instr_entry_t {
41 InstrumentManager::instrument_id_t ID;
42 ::gig::File* pGig;
43 uint MaxSamplesPerCycle; ///< if some engine requests an already allocated instrument with a higher value, we have to reallocate the instrument
44 };
45
46 // some data needed for the libgig callback function
47 struct progress_callback_arg_t {
48 InstrumentResourceManager* pManager;
49 InstrumentManager::instrument_id_t* pInstrumentKey;
50 };
51
52 // we use this to react on events concerning an instrument on behalf of an instrument editor
53 class InstrumentEditorProxy : public InstrumentConsumer {
54 public:
55 virtual void ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
56 //TODO: inform the instrument editor about the pending update
57 }
58
59 virtual void ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
60 //TODO:: inform the instrument editor about finished update
61 }
62
63 virtual void OnResourceProgress(float fProgress) {
64 //TODO: inform the instrument editor about the progress of an update
65 }
66
67 // the instrument we borrowed on behalf of the editor
68 ::gig::Instrument* pInstrument;
69 };
70
71 /**
72 * Callback function which will be called by libgig during loading of
73 * instruments to inform about the current progress. Or to be more
74 * specific; it will be called during the GetInstrument() call.
75 *
76 * @param pProgress - contains current progress value, pointer to the
77 * InstrumentResourceManager instance and
78 * instrument ID
79 */
80 void InstrumentResourceManager::OnInstrumentLoadingProgress(::gig::progress_t* pProgress) {
81 dmsg(7,("gig::InstrumentResourceManager: progress %f%", pProgress->factor));
82 progress_callback_arg_t* pArg = static_cast<progress_callback_arg_t*>(pProgress->custom);
83 // we randomly schedule 90% for the .gig file loading and the remaining 10% later for sample caching
84 const float localProgress = 0.9f * pProgress->factor;
85 pArg->pManager->DispatchResourceProgressEvent(*pArg->pInstrumentKey, localProgress);
86 }
87
88 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::Instruments() {
89 return Entries();
90 }
91
92 InstrumentManager::mode_t InstrumentResourceManager::GetMode(const instrument_id_t& ID) {
93 return static_cast<InstrumentManager::mode_t>(AvailabilityMode(ID));
94 }
95
96 void InstrumentResourceManager::SetMode(const instrument_id_t& ID, InstrumentManager::mode_t Mode) {
97 dmsg(2,("gig::InstrumentResourceManager: setting mode for %s (Index=%d) to %d\n",ID.FileName.c_str(),ID.Index,Mode));
98 SetAvailabilityMode(ID, static_cast<ResourceManager<InstrumentManager::instrument_id_t, ::gig::Instrument>::mode_t>(Mode));
99 }
100
101 String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
102 Lock();
103 ::gig::Instrument* pInstrument = Resource(ID, false);
104 String res = (pInstrument) ? pInstrument->pInfo->Name : "";
105 Unlock();
106 return res;
107 }
108
109 String InstrumentResourceManager::GetInstrumentDataStructureName(instrument_id_t ID) {
110 return ::gig::libraryName();
111 }
112
113 String InstrumentResourceManager::GetInstrumentDataStructureVersion(instrument_id_t ID) {
114 return ::gig::libraryVersion();
115 }
116
117 void InstrumentResourceManager::LaunchInstrumentEditor(instrument_id_t ID) throw (InstrumentManagerException) {
118 const String sDataType = GetInstrumentDataStructureName(ID);
119 const String sDataVersion = GetInstrumentDataStructureVersion(ID);
120 // find instrument editors capable to handle given instrument
121 std::vector<String> vEditors =
122 InstrumentEditorFactory::MatchingEditors(sDataType, sDataVersion);
123 if (!vEditors.size())
124 throw InstrumentManagerException(
125 "There is no instrument editor capable to handle this instrument"
126 );
127 // simply use the first editor in the result set
128 dmsg(1,("Found matching editor '%s' for instrument ('%s', %d) having data structure ('%s','%s')\n",
129 vEditors[0].c_str(), ID.FileName.c_str(), ID.Index, sDataType.c_str(), sDataVersion.c_str()));
130 InstrumentEditor* pEditor = InstrumentEditorFactory::Create(vEditors[0]);
131 // register for receiving notifications from the instrument editor
132 pEditor->AddListener(this);
133 // create a proxy that reacts on notification on behalf of the editor
134 InstrumentEditorProxy* pProxy = new InstrumentEditorProxy;
135 // borrow the instrument on behalf of the instrument editor
136 ::gig::Instrument* pInstrument = Borrow(ID, pProxy);
137 // remember the proxy and instrument for this instrument editor
138 pProxy->pInstrument = pInstrument;
139 InstrumentEditorProxiesMutex.Lock();
140 InstrumentEditorProxies[pEditor] = pProxy;
141 InstrumentEditorProxiesMutex.Unlock();
142 // launch the instrument editor for the given instrument
143 pEditor->Launch(pInstrument, sDataType, sDataVersion);
144 }
145
146 /**
147 * Will be called by the respective instrument editor once it left its
148 * Main() loop. That way we can handle cleanup before its thread finally
149 * dies.
150 *
151 * @param pSender - instrument editor that stops execution
152 */
153 void InstrumentResourceManager::OnInstrumentEditorQuit(InstrumentEditor* pSender) {
154 dmsg(1,("InstrumentResourceManager: instrument editor quit, doing cleanup\n"));
155 // hand back instrument and free proxy
156 InstrumentEditorProxiesMutex.Lock();
157 if (InstrumentEditorProxies.count(pSender)) {
158 InstrumentEditorProxy* pProxy =
159 dynamic_cast<InstrumentEditorProxy*>(
160 InstrumentEditorProxies[pSender]
161 );
162 InstrumentEditorProxies.erase(pSender);
163 InstrumentEditorProxiesMutex.Unlock();
164 HandBack(pProxy->pInstrument, pProxy);
165 if (pProxy) delete pProxy;
166 } else {
167 InstrumentEditorProxiesMutex.Unlock();
168 std::cerr << "Eeeek, could not find instrument editor proxy, this is a bug!\n" << std::flush;
169 }
170 // free the editor
171 InstrumentEditorFactory::Destroy(pSender);
172 }
173
174 void InstrumentResourceManager::OnSamplesToBeRemoved(std::set<void*> Samples, InstrumentEditor* pSender) {
175 if (Samples.empty()) {
176 std::cerr << "gig::InstrumentResourceManager: WARNING, "
177 "OnSamplesToBeRemoved() called with empty list, this "
178 "is a bug!\n" << std::flush;
179 return;
180 }
181 // TODO: ATM we assume here that all samples are from the same file
182 ::gig::Sample* pFirstSample = (::gig::Sample*) *Samples.begin();
183 ::gig::File* pCriticalFile = dynamic_cast< ::gig::File*>(pFirstSample->GetParent());
184 // completely suspend all engines that use that same file
185 SuspendEnginesUsing(pCriticalFile);
186 }
187
188 void InstrumentResourceManager::OnSamplesRemoved(InstrumentEditor* pSender) {
189 // resume all previously, completely suspended engines
190 // (we don't have to un-cache the removed samples here, since that is
191 // automatically done by the gig::Sample destructor)
192 ResumeAllEngines();
193 }
194
195 void InstrumentResourceManager::OnDataStructureToBeChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
196 //TODO: remove code duplication
197 if (sStructType == "gig::File") {
198 // completely suspend all engines that use that file
199 ::gig::File* pFile = (::gig::File*) pStruct;
200 SuspendEnginesUsing(pFile);
201 } else if (sStructType == "gig::Instrument") {
202 // completely suspend all engines that use that instrument
203 ::gig::Instrument* pInstrument = (::gig::Instrument*) pStruct;
204 SuspendEnginesUsing(pInstrument);
205 } else if (sStructType == "gig::Region") {
206 // only advice the engines to suspend the given region, so they'll
207 // only ignore that region (and probably already other suspended
208 // ones), but beside that continue normal playback
209 ::gig::Region* pRegion = (::gig::Region*) pStruct;
210 ::gig::Instrument* pInstrument =
211 (::gig::Instrument*) pRegion->GetParent();
212 Lock();
213 std::set<gig::Engine*> engines =
214 GetEnginesUsing(pInstrument, false/*don't lock again*/);
215 std::set<gig::Engine*>::iterator iter = engines.begin();
216 std::set<gig::Engine*>::iterator end = engines.end();
217 for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
218 Unlock();
219 } else if (sStructType == "gig::DimensionRegion") {
220 // only advice the engines to suspend the given DimensionRegions's
221 // parent region, so they'll only ignore that region (and probably
222 // already other suspended ones), but beside that continue normal
223 // playback
224 ::gig::DimensionRegion* pDimReg =
225 (::gig::DimensionRegion*) pStruct;
226 ::gig::Region* pRegion = pDimReg->GetParent();
227 ::gig::Instrument* pInstrument =
228 (::gig::Instrument*) pRegion->GetParent();
229 Lock();
230 std::set<gig::Engine*> engines =
231 GetEnginesUsing(pInstrument, false/*don't lock again*/);
232 std::set<gig::Engine*>::iterator iter = engines.begin();
233 std::set<gig::Engine*>::iterator end = engines.end();
234 for (; iter != end; ++iter) (*iter)->Suspend(pRegion);
235 Unlock();
236 } else {
237 std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
238 "structure '" << sStructType << "' requested to be "
239 "suspended by instrument editor. This is a bug!\n"
240 << std::flush;
241 //TODO: we should inform the instrument editor that something seriously went wrong
242 }
243 }
244
245 void InstrumentResourceManager::OnDataStructureChanged(void* pStruct, String sStructType, InstrumentEditor* pSender) {
246 //TODO: remove code duplication
247 if (sStructType == "gig::File") {
248 // resume all previously suspended engines
249 ResumeAllEngines();
250 } else if (sStructType == "gig::Instrument") {
251 // resume all previously suspended engines
252 ResumeAllEngines();
253 } else if (sStructType == "gig::Region") {
254 // advice the engines to resume the given region, that is to
255 // using it for playback again
256 ::gig::Region* pRegion = (::gig::Region*) pStruct;
257 ::gig::Instrument* pInstrument =
258 (::gig::Instrument*) pRegion->GetParent();
259 Lock();
260 std::set<gig::Engine*> engines =
261 GetEnginesUsing(pInstrument, false/*don't lock again*/);
262 std::set<gig::Engine*>::iterator iter = engines.begin();
263 std::set<gig::Engine*>::iterator end = engines.end();
264 for (; iter != end; ++iter) (*iter)->Resume(pRegion);
265 Unlock();
266 } else if (sStructType == "gig::DimensionRegion") {
267 // advice the engines to resume the given DimensionRegion's parent
268 // region, that is to using it for playback again
269 ::gig::DimensionRegion* pDimReg =
270 (::gig::DimensionRegion*) pStruct;
271 ::gig::Region* pRegion = pDimReg->GetParent();
272 ::gig::Instrument* pInstrument =
273 (::gig::Instrument*) pRegion->GetParent();
274 Lock();
275 std::set<gig::Engine*> engines =
276 GetEnginesUsing(pInstrument, false/*don't lock again*/);
277 std::set<gig::Engine*>::iterator iter = engines.begin();
278 std::set<gig::Engine*>::iterator end = engines.end();
279 for (; iter != end; ++iter) (*iter)->Resume(pRegion);
280 Unlock();
281 } else {
282 std::cerr << "gig::InstrumentResourceManager: ERROR, unknown data "
283 "structure '" << sStructType << "' requested to be "
284 "resumed by instrument editor. This is a bug!\n"
285 << std::flush;
286 //TODO: we should inform the instrument editor that something seriously went wrong
287 }
288 }
289
290 void InstrumentResourceManager::OnSampleReferenceChanged(void* pOldSample, void* pNewSample, InstrumentEditor* pSender) {
291 // uncache old sample in case it's not used by anybody anymore
292 if (pOldSample) {
293 Lock();
294 ::gig::Sample* pSample = (::gig::Sample*) pOldSample;
295 ::gig::File* pFile = (::gig::File*) pSample->GetParent();
296 std::vector< ::gig::Instrument*> instruments =
297 GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
298 for (int i = 0; i < instruments.size(); i++)
299 if (!SampleReferencedByInstrument(pSample, instruments[i]))
300 UncacheInitialSamples(pSample);
301 Unlock();
302 }
303 // make sure new sample reference is cached
304 if (pNewSample) {
305 Lock();
306 ::gig::Sample* pSample = (::gig::Sample*) pNewSample;
307 ::gig::File* pFile = (::gig::File*) pSample->GetParent();
308 // get all engines that use that same gig::File
309 std::set<gig::Engine*> engines = GetEnginesUsing(pFile, false/*don't lock again*/);
310 std::set<gig::Engine*>::iterator iter = engines.begin();
311 std::set<gig::Engine*>::iterator end = engines.end();
312 for (; iter != end; ++iter)
313 CacheInitialSamples(pSample, *iter);
314 Unlock();
315 }
316 }
317
318 ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
319 // get gig file from internal gig file manager
320 ::gig::File* pGig = Gigs.Borrow(Key.FileName, (GigConsumer*) Key.Index); // conversion kinda hackish :/
321
322 // we pass this to the progress callback mechanism of libgig
323 progress_callback_arg_t callbackArg;
324 callbackArg.pManager = this;
325 callbackArg.pInstrumentKey = &Key;
326
327 ::gig::progress_t progress;
328 progress.callback = OnInstrumentLoadingProgress;
329 progress.custom = &callbackArg;
330
331 dmsg(1,("Loading gig instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
332 ::gig::Instrument* pInstrument = pGig->GetInstrument(Key.Index, &progress);
333 if (!pInstrument) {
334 std::stringstream msg;
335 msg << "There's no instrument with index " << Key.Index << ".";
336 throw InstrumentManagerException(msg.str());
337 }
338 pGig->GetFirstSample(); // just to force complete instrument loading
339 dmsg(1,("OK\n"));
340
341 // cache initial samples points (for actually needed samples)
342 dmsg(1,("Caching initial samples..."));
343 uint iRegion = 0; // just for progress calculation
344 ::gig::Region* pRgn = pInstrument->GetFirstRegion();
345 while (pRgn) {
346 // we randomly schedule 90% for the .gig file loading and the remaining 10% now for sample caching
347 const float localProgress = 0.9f + 0.1f * (float) iRegion / (float) pInstrument->Regions;
348 DispatchResourceProgressEvent(Key, localProgress);
349
350 if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {
351 dmsg(2,("C"));
352 CacheInitialSamples(pRgn->GetSample(), (gig::EngineChannel*) pConsumer);
353 }
354 for (uint i = 0; i < pRgn->DimensionRegions; i++) {
355 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, (gig::EngineChannel*) pConsumer);
356 }
357
358 pRgn = pInstrument->GetNextRegion();
359 iRegion++;
360 }
361 dmsg(1,("OK\n"));
362 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
363
364 // we need the following for destruction later
365 instr_entry_t* pEntry = new instr_entry_t;
366 pEntry->ID.FileName = Key.FileName;
367 pEntry->ID.Index = Key.Index;
368 pEntry->pGig = pGig;
369
370 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
371 // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'
372 pEntry->MaxSamplesPerCycle =
373 (!pEngineChannel) ? 0 /* don't care for instrument editors */ :
374 (pEngineChannel->GetEngine()) ?
375 dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
376 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
377 pArg = pEntry;
378
379 return pInstrument;
380 }
381
382 void InstrumentResourceManager::Destroy( ::gig::Instrument* pResource, void* pArg) {
383 instr_entry_t* pEntry = (instr_entry_t*) pArg;
384 // we don't need the .gig file here anymore
385 Gigs.HandBack(pEntry->pGig, (GigConsumer*) pEntry->ID.Index); // conversion kinda hackish :/
386 delete pEntry;
387 }
388
389 void InstrumentResourceManager::OnBorrow(::gig::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) {
390 instr_entry_t* pEntry = (instr_entry_t*) pArg;
391 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
392 uint maxSamplesPerCycle =
393 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
394 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
395 if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
396 Update(pResource, pConsumer);
397 }
398 }
399
400 /**
401 * Give back an instrument. This should be used instead of
402 * HandBack if there are some dimension regions that are still in
403 * use. (When an instrument is changed, the voices currently
404 * playing is allowed to keep playing with the old instrument
405 * until note off arrives. New notes will use the new instrument.)
406 */
407 void InstrumentResourceManager::HandBackInstrument(::gig::Instrument* pResource, InstrumentConsumer* pConsumer,
408 ::gig::DimensionRegion** dimRegionsInUse) {
409 DimRegInfoMutex.Lock();
410 for (int i = 0 ; dimRegionsInUse[i] ; i++) {
411 DimRegInfo[dimRegionsInUse[i]].refCount++;
412 SampleRefCount[dimRegionsInUse[i]->pSample]++;
413 }
414 HandBack(pResource, pConsumer, true);
415 DimRegInfoMutex.Unlock();
416 }
417
418 /**
419 * Give back a dimension region that belongs to an instrument that
420 * was previously handed back.
421 */
422 void InstrumentResourceManager::HandBackDimReg(::gig::DimensionRegion* pDimReg) {
423 DimRegInfoMutex.Lock();
424 dimreg_info_t& dimRegInfo = DimRegInfo[pDimReg];
425 int dimRegRefCount = --dimRegInfo.refCount;
426 int sampleRefCount = --SampleRefCount[pDimReg->pSample];
427 if (dimRegRefCount == 0) {
428 ::gig::File* gig = dimRegInfo.file;
429 ::RIFF::File* riff = dimRegInfo.riff;
430 DimRegInfo.erase(pDimReg);
431 // TODO: we could delete Region and Instrument here if
432 // they have become unused
433
434 if (sampleRefCount == 0) {
435 SampleRefCount.erase(pDimReg->pSample);
436
437 if (gig) {
438 gig->DeleteSample(pDimReg->pSample);
439 if (!gig->GetFirstSample()) {
440 dmsg(2,("No more samples in use - freeing gig\n"));
441 delete gig;
442 delete riff;
443 }
444 }
445 }
446 }
447 DimRegInfoMutex.Unlock();
448 }
449
450 /**
451 * Just a wrapper around the other @c CacheInitialSamples() method.
452 *
453 * @param pSample - points to the sample to be cached
454 * @param pEngine - pointer to Gig Engine Channel which caused this call
455 * (may be NULL, in this case default amount of samples
456 * will be cached)
457 */
458 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::EngineChannel* pEngineChannel) {
459 gig::Engine* pEngine =
460 (pEngineChannel && pEngineChannel->GetEngine()) ?
461 dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine()) : NULL;
462 CacheInitialSamples(pSample, pEngine);
463 }
464
465 /**
466 * Caches a certain size at the beginning of the given sample in RAM. If the
467 * sample is very short, the whole sample will be loaded into RAM and thus
468 * no disk streaming is needed for this sample. Caching an initial part of
469 * samples is needed to compensate disk reading latency.
470 *
471 * @param pSample - points to the sample to be cached
472 * @param pEngine - pointer to Gig Engine which caused this call
473 * (may be NULL, in this case default amount of samples
474 * will be cached)
475 */
476 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::Engine* pEngine) {
477 if (!pSample) {
478 dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
479 return;
480 }
481 if (!pSample->SamplesTotal) return; // skip zero size samples
482
483 if (pSample->SamplesTotal <= CONFIG_PRELOAD_SAMPLES) {
484 // Sample is too short for disk streaming, so we load the whole
485 // sample into RAM and place 'pAudioIO->FragmentSize << CONFIG_MAX_PITCH'
486 // number of '0' samples (silence samples) behind the official buffer
487 // border, to allow the interpolator do it's work even at the end of
488 // the sample.
489 const uint maxSamplesPerCycle =
490 (pEngine) ? pEngine->pAudioOutputDevice->MaxSamplesPerCycle()
491 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
492 const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;
493 const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;
494 if (currentlyCachedSilenceSamples < neededSilenceSamples) {
495 dmsg(3,("Caching whole sample (sample name: \"%s\", sample size: %d)\n", pSample->pInfo->Name.c_str(), pSample->SamplesTotal));
496 ::gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(neededSilenceSamples);
497 dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
498 }
499 }
500 else { // we only cache CONFIG_PRELOAD_SAMPLES and stream the other sample points from disk
501 if (!pSample->GetCache().Size) pSample->LoadSampleData(CONFIG_PRELOAD_SAMPLES);
502 }
503
504 if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
505 }
506
507 void InstrumentResourceManager::UncacheInitialSamples(::gig::Sample* pSample) {
508 dmsg(1,("Uncaching sample %x\n",pSample));
509 if (pSample->GetCache().Size) pSample->ReleaseSampleData();
510 }
511
512 /**
513 * Returns a list with all instruments currently in use, that are part of
514 * the given file.
515 *
516 * @param pFile - search criteria
517 * @param bLock - whether we should lock (mutex) the instrument manager
518 * during this call and unlock at the end of this call
519 */
520 std::vector< ::gig::Instrument*> InstrumentResourceManager::GetInstrumentsCurrentlyUsedOf(::gig::File* pFile, bool bLock) {
521 if (bLock) Lock();
522 std::vector< ::gig::Instrument*> result;
523 std::vector< ::gig::Instrument*> allInstruments = Resources(false/*don't lock again*/);
524 for (int i = 0; i < allInstruments.size(); i++)
525 if (
526 (::gig::File*) allInstruments[i]->GetParent()
527 == pFile
528 ) result.push_back(allInstruments[i]);
529 if (bLock) Unlock();
530 return result;
531 }
532
533 /**
534 * Returns a list with all gig Engines that are currently using the given
535 * instrument.
536 *
537 * @param pInstrument - search criteria
538 * @param bLock - whether we should lock (mutex) the instrument manager
539 * during this call and unlock at the end of this call
540 */
541 std::set<gig::Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::Instrument* pInstrument, bool bLock) {
542 if (bLock) Lock();
543 std::set<gig::Engine*> result;
544 std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(pInstrument);
545 std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
546 std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
547 for (; iter != end; ++iter) {
548 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(*iter);
549 if (!pEngineChannel) continue;
550 gig::Engine* pEngine = dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine());
551 if (!pEngine) continue;
552 result.insert(pEngine);
553 }
554 if (bLock) Unlock();
555 return result;
556 }
557
558 /**
559 * Returns a list with all gig Engines that are currently using an
560 * instrument that is part of the given instrument file.
561 *
562 * @param pFile - search criteria
563 * @param bLock - whether we should lock (mutex) the instrument manager
564 * during this call and unlock at the end of this call
565 */
566 std::set<gig::Engine*> InstrumentResourceManager::GetEnginesUsing(::gig::File* pFile, bool bLock) {
567 if (bLock) Lock();
568 // get all instruments (currently in usage) that use that same gig::File
569 std::vector< ::gig::Instrument*> instrumentsOfInterest =
570 GetInstrumentsCurrentlyUsedOf(pFile, false/*don't lock again*/);
571
572 // get all engines that use that same gig::File
573 std::set<gig::Engine*> result;
574 {
575 for (int i = 0; i < instrumentsOfInterest.size(); i++) {
576 std::set<ResourceConsumer< ::gig::Instrument>*> consumers = ConsumersOf(instrumentsOfInterest[i]);
577 std::set<ResourceConsumer< ::gig::Instrument>*>::iterator iter = consumers.begin();
578 std::set<ResourceConsumer< ::gig::Instrument>*>::iterator end = consumers.end();
579 for (; iter != end; ++iter) {
580 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(*iter);
581 if (!pEngineChannel) continue;
582 gig::Engine* pEngine = dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine());
583 if (!pEngine) continue;
584 // the unique, sorted container std::set makes
585 // sure we won't have duplicates
586 result.insert(pEngine);
587 }
588 }
589 }
590 if (bLock) Unlock();
591 return result;
592 }
593
594 /**
595 * Returns @c true in case the given sample is referenced somewhere by the
596 * given instrument, @c false otherwise.
597 *
598 * @param pSample - sample reference
599 * @param pInstrument - instrument that might use that sample
600 */
601 bool InstrumentResourceManager::SampleReferencedByInstrument(::gig::Sample* pSample, ::gig::Instrument* pInstrument) {
602 for (
603 ::gig::Region* pRegion = pInstrument->GetFirstRegion();
604 pRegion; pRegion = pInstrument->GetNextRegion()
605 ) {
606 for (
607 int i = 0; i < pRegion->DimensionRegions &&
608 pRegion->pDimensionRegions[i]; i++
609 ) {
610 if (pRegion->pDimensionRegions[i]->pSample == pSample)
611 return true;
612 }
613 }
614 return false;
615 }
616
617 /**
618 * Suspend all gig engines that use the given instrument. This means
619 * completely stopping playback on those engines and killing all their
620 * voices and disk streams. This method will block until all voices AND
621 * their disk streams are finally deleted and the engine turned into a
622 * complete idle loop.
623 *
624 * All @c SuspendEnginesUsing() methods only serve one thread by one and
625 * block all other threads until the current active thread called
626 * @c ResumeAllEngines() .
627 *
628 * @param pInstrument - search criteria
629 */
630 void InstrumentResourceManager::SuspendEnginesUsing(::gig::Instrument* pInstrument) {
631 // make sure no other thread suspends whole engines at the same time
632 suspendedEnginesMutex.Lock();
633 // get all engines that use that same gig::Instrument
634 suspendedEngines = GetEnginesUsing(pInstrument, true/*lock*/);
635 // finally, completely suspend all engines that use that same gig::Instrument
636 std::set<gig::Engine*>::iterator iter = suspendedEngines.begin();
637 std::set<gig::Engine*>::iterator end = suspendedEngines.end();
638 for (; iter != end; ++iter) (*iter)->SuspendAll();
639 }
640
641 /**
642 * Suspend all gig engines that use the given instrument file. This means
643 * completely stopping playback on those engines and killing all their
644 * voices and disk streams. This method will block until all voices AND
645 * their disk streams are finally deleted and the engine turned into a
646 * complete idle loop.
647 *
648 * All @c SuspendEnginesUsing() methods only serve one thread by one and
649 * block all other threads until the current active thread called
650 * @c ResumeAllEngines() .
651 *
652 * @param pFile - search criteria
653 */
654 void InstrumentResourceManager::SuspendEnginesUsing(::gig::File* pFile) {
655 // make sure no other thread suspends whole engines at the same time
656 suspendedEnginesMutex.Lock();
657 // get all engines that use that same gig::File
658 suspendedEngines = GetEnginesUsing(pFile, true/*lock*/);
659 // finally, completely suspend all engines that use that same gig::File
660 std::set<gig::Engine*>::iterator iter = suspendedEngines.begin();
661 std::set<gig::Engine*>::iterator end = suspendedEngines.end();
662 for (; iter != end; ++iter) (*iter)->SuspendAll();
663 }
664
665 /**
666 * MUST be called after one called one of the @c SuspendEnginesUsing()
667 * methods, to resume normal playback on all previously suspended engines.
668 * As it's only possible for one thread to suspend whole engines at the
669 * same time, this method doesn't take any arguments.
670 */
671 void InstrumentResourceManager::ResumeAllEngines() {
672 // resume all previously completely suspended engines
673 std::set<Engine*>::iterator iter = suspendedEngines.begin();
674 std::set<Engine*>::iterator end = suspendedEngines.end();
675 for (; iter != end; ++iter) (*iter)->ResumeAll();
676 // no more suspended engines ...
677 suspendedEngines.clear();
678 // allow another thread to suspend whole engines
679 suspendedEnginesMutex.Unlock();
680 }
681
682
683
684 // internal gig file manager
685
686 ::gig::File* InstrumentResourceManager::GigResourceManager::Create(String Key, GigConsumer* pConsumer, void*& pArg) {
687 dmsg(1,("Loading gig file \'%s\'...", Key.c_str()));
688 ::RIFF::File* pRIFF = new ::RIFF::File(Key);
689 ::gig::File* pGig = new ::gig::File(pRIFF);
690 pArg = pRIFF;
691 dmsg(1,("OK\n"));
692 return pGig;
693 }
694
695 void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
696 dmsg(1,("Freeing gig file from memory..."));
697
698 // Delete as much as possible of the gig file. Some of the
699 // dimension regions and samples may still be in use - these
700 // will be deleted later by the HandBackDimReg function.
701 bool deleteFile = true;
702 ::gig::Instrument* nextInstrument;
703 for (::gig::Instrument* instrument = pResource->GetFirstInstrument() ;
704 instrument ;
705 instrument = nextInstrument) {
706 nextInstrument = pResource->GetNextInstrument();
707 bool deleteInstrument = true;
708 ::gig::Region* nextRegion;
709 for (::gig::Region *region = instrument->GetFirstRegion() ;
710 region ;
711 region = nextRegion) {
712 nextRegion = instrument->GetNextRegion();
713 bool deleteRegion = true;
714 for (int i = 0 ; i < region->DimensionRegions ; i++)
715 {
716 ::gig::DimensionRegion *d = region->pDimensionRegions[i];
717 std::map< ::gig::DimensionRegion*, dimreg_info_t>::iterator iter = parent->DimRegInfo.find(d);
718 if (iter != parent->DimRegInfo.end()) {
719 dimreg_info_t& dimRegInfo = (*iter).second;
720 dimRegInfo.file = pResource;
721 dimRegInfo.riff = (::RIFF::File*)pArg;
722 deleteFile = deleteInstrument = deleteRegion = false;
723 }
724 }
725 if (deleteRegion) instrument->DeleteRegion(region);
726 }
727 if (deleteInstrument) pResource->DeleteInstrument(instrument);
728 }
729 if (deleteFile) {
730 delete pResource;
731 delete (::RIFF::File*) pArg;
732 } else {
733 dmsg(2,("keeping some samples that are in use..."));
734 ::gig::Sample* nextSample;
735 for (::gig::Sample* sample = pResource->GetFirstSample() ;
736 sample ;
737 sample = nextSample) {
738 nextSample = pResource->GetNextSample();
739 if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
740 pResource->DeleteSample(sample);
741 }
742 }
743 }
744 dmsg(1,("OK\n"));
745 }
746
747 }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC