/[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 1771 - (show annotations) (download)
Wed Sep 10 15:02:24 2008 UTC (15 years, 7 months ago) by iliev
File size: 46764 byte(s)
* added new LSCP command: SEND CHANNEL MIDI_DATA which can be used by
  frontends to send MIDI messages to specific sampler channel
* added two additional fields to GET FILE INSTRUMENT INFO command -
  KEY_BINDINGS and KEYSWITCH_BINDINGS
* InstrumentResourceManager::GetInstrumentInfo now scans the specified
  instrument file only if it is not loaded in the instrument manager

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

  ViewVC Help
Powered by ViewVC