/[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 4010 - (show annotations) (download)
Thu Dec 23 15:26:02 2021 UTC (2 years, 4 months ago) by schoenebeck
File size: 50119 byte(s)
* Additional more verbose error output in case an appropriate
  instrument editor could not be found (if there is not any
  editor at all then say so, if there is one but not capable
  to handle the instrument, then make this case clear to the
  user; in both cases print the expected location for instrument
  editor plugins).

* Bumped version (2.2.0.svn9).

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

  ViewVC Help
Powered by ViewVC