/[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 1662 - (show annotations) (download)
Sun Feb 3 16:21:38 2008 UTC (16 years, 2 months ago) by schoenebeck
File size: 45325 byte(s)
* the promised "cleanup": rewrote virtual MIDI device's notification a bit
  (using now a safe double buffer approach using "SynchronizedConfig"
  instead of the Trylock() approach previously to synchronize the list of
  virtual MIDI devices)
* bumped version to 0.5.1.2cvs

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

  ViewVC Help
Powered by ViewVC