/[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 1774 - (show annotations) (download)
Thu Sep 11 18:11:06 2008 UTC (15 years, 7 months ago) by iliev
File size: 46897 byte(s)
* don't retrieve key bindings and keyswitch bindings if
  the instrument is not loaded in the instrument manager
* notification events are now sent when sending MIDI
  messages using SEND CHANNEL MIDI_DATA lscp command
* fixed a bug in the keyswitch bindings retrieval

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

  ViewVC Help
Powered by ViewVC