/[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 2687 - (show annotations) (download)
Sun Jan 4 17:16:05 2015 UTC (9 years, 3 months ago) by schoenebeck
File size: 45364 byte(s)
* Instrument editor interface: Changed instrument editor plugin interface,
providing additional informations like the EngineChannel for which the
instrument editor was spawned for. This allows the instrument editors to
interact more actively with the sampler.
* Bumped version (1.0.0.svn60).

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

  ViewVC Help
Powered by ViewVC