/[svn]/linuxsampler/trunk/src/engines/gig/InstrumentResourceManager.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/gig/InstrumentResourceManager.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1212 - (hide annotations) (download)
Tue May 29 23:59:36 2007 UTC (16 years, 10 months ago) by schoenebeck
File size: 20294 byte(s)
* added highly experimental support for on-the-fly instrument editing
  within the sampler's process (by using instrument editor plugins),
  you'll notice the new "Registered instrument editors:" message on
  startup, the plugin path can be overridden at compile time with
  ./configure --enable-plugin-dir=/some/dir
* added a new LSCP command "EDIT INSTRUMENT <sampler-channel>" to spawn
  a matching instrument editor for the instrument on the given sampler
  channel (LSCP command syntax might be subject to change soon)
* config.h is not going to be installed along with liblinuxsampler's
  API header files anymore (not necessary anymore)
* take care of $(DESTDIR) when creating the instruments DB on 'make
  install' rule (needed for packaging and cross compilation)
* bumped version to 0.4.0.5cvs

1 schoenebeck 53 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 schoenebeck 56 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 1212 * Copyright (C) 2005 - 2007 Christian Schoenebeck *
7 schoenebeck 53 * *
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 schoenebeck 1212 #include "../InstrumentEditorFactory.h"
29    
30 schoenebeck 411 // We need to know the maximum number of sample points which are going to
31     // be processed for each render cycle of the audio output driver, to know
32     // how much initial sample points we need to cache into RAM. If the given
33     // sampler channel does not have an audio output device assigned yet
34     // though, we simply use this default value.
35     #define GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE 128
36    
37 schoenebeck 53 namespace LinuxSampler { namespace gig {
38    
39 schoenebeck 947 // data stored as long as an instrument resource exists
40     struct instr_entry_t {
41     InstrumentManager::instrument_id_t ID;
42     ::gig::File* pGig;
43     uint MaxSamplesPerCycle; ///< if some engine requests an already allocated instrument with a higher value, we have to reallocate the instrument
44     };
45    
46 schoenebeck 517 // some data needed for the libgig callback function
47     struct progress_callback_arg_t {
48 schoenebeck 947 InstrumentResourceManager* pManager;
49     InstrumentManager::instrument_id_t* pInstrumentKey;
50 schoenebeck 517 };
51    
52 schoenebeck 1212 // we use this to react on events concerning an instrument on behalf of an instrument editor
53     class InstrumentEditorProxy : public InstrumentConsumer {
54     public:
55     virtual void ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
56     //TODO: inform the instrument editor about the pending update
57     }
58    
59     virtual void ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
60     //TODO:: inform the instrument editor about finished update
61     }
62    
63     virtual void OnResourceProgress(float fProgress) {
64     //TODO: inform the instrument editor about the progress of an update
65     }
66    
67     // the instrument we borrowed on behalf of the editor
68     ::gig::Instrument* pInstrument;
69     };
70    
71 schoenebeck 517 /**
72     * Callback function which will be called by libgig during loading of
73     * instruments to inform about the current progress. Or to be more
74     * specific; it will be called during the GetInstrument() call.
75     *
76     * @param pProgress - contains current progress value, pointer to the
77     * InstrumentResourceManager instance and
78     * instrument ID
79     */
80     void InstrumentResourceManager::OnInstrumentLoadingProgress(::gig::progress_t* pProgress) {
81     dmsg(7,("gig::InstrumentResourceManager: progress %f%", pProgress->factor));
82     progress_callback_arg_t* pArg = static_cast<progress_callback_arg_t*>(pProgress->custom);
83     // we randomly schedule 90% for the .gig file loading and the remaining 10% later for sample caching
84     const float localProgress = 0.9f * pProgress->factor;
85     pArg->pManager->DispatchResourceProgressEvent(*pArg->pInstrumentKey, localProgress);
86     }
87    
88 schoenebeck 947 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::Instruments() {
89     return Entries();
90     }
91    
92     InstrumentManager::mode_t InstrumentResourceManager::GetMode(const instrument_id_t& ID) {
93     return static_cast<InstrumentManager::mode_t>(AvailabilityMode(ID));
94     }
95    
96     void InstrumentResourceManager::SetMode(const instrument_id_t& ID, InstrumentManager::mode_t Mode) {
97     dmsg(2,("gig::InstrumentResourceManager: setting mode for %s (Index=%d) to %d\n",ID.FileName.c_str(),ID.Index,Mode));
98     SetAvailabilityMode(ID, static_cast<ResourceManager<InstrumentManager::instrument_id_t, ::gig::Instrument>::mode_t>(Mode));
99     }
100    
101     String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
102 schoenebeck 970 Lock();
103     ::gig::Instrument* pInstrument = Resource(ID, false);
104     String res = (pInstrument) ? pInstrument->pInfo->Name : "";
105     Unlock();
106     return res;
107 schoenebeck 947 }
108    
109 schoenebeck 1212 String InstrumentResourceManager::GetInstrumentTypeName(instrument_id_t ID) {
110     return ::gig::libraryName();
111     }
112    
113     String InstrumentResourceManager::GetInstrumentTypeVersion(instrument_id_t ID) {
114     return ::gig::libraryVersion();
115     }
116    
117     void InstrumentResourceManager::LaunchInstrumentEditor(instrument_id_t ID) throw (InstrumentManagerException) {
118     const String sDataType = GetInstrumentTypeName(ID);
119     const String sDataVersion = GetInstrumentTypeVersion(ID);
120     // find instrument editors capable to handle given instrument
121     std::vector<String> vEditors =
122     InstrumentEditorFactory::MatchingEditors(sDataType, sDataVersion);
123     if (!vEditors.size())
124     throw InstrumentManagerException(
125     "There is no instrument editor capable to handle this instrument"
126     );
127     // simply use the first editor in the result set
128     dmsg(1,("Found matching editor '%s' for instrument ('%s', %d) having data structure ('%s','%s')\n",
129     vEditors[0].c_str(), ID.FileName.c_str(), ID.Index, sDataType.c_str(), sDataVersion.c_str()));
130     InstrumentEditor* pEditor = InstrumentEditorFactory::Create(vEditors[0]);
131     // we want to know when you'll die X| (see OnInstrumentEditorQuit())
132     pEditor->AddListener(this);
133     // create a proxy that reacts on notification on behalf of the editor
134     InstrumentEditorProxy* pProxy = new InstrumentEditorProxy;
135     // borrow the instrument on behalf of the instrument editor
136     ::gig::Instrument* pInstrument = Borrow(ID, pProxy);
137     // remember the proxy and instrument for this instrument editor
138     pProxy->pInstrument = pInstrument;
139     InstrumentEditorProxiesMutex.Lock();
140     InstrumentEditorProxies[pEditor] = pProxy;
141     InstrumentEditorProxiesMutex.Unlock();
142     // launch the instrument editor for the given instrument
143     pEditor->Launch(pInstrument, sDataType, sDataVersion);
144     }
145    
146     /**
147     * Will be called by the respective instrument editor once it left its
148     * Main() loop. That way we can handle cleanup before its thread finally
149     * dies.
150     *
151     * @param pSender - instrument editor that stops execution
152     */
153     void InstrumentResourceManager::OnInstrumentEditorQuit(InstrumentEditor* pSender) {
154     dmsg(1,("InstrumentResourceManager: instrument editor quit, doing cleanup\n"));
155     // hand back instrument and free proxy
156     InstrumentEditorProxiesMutex.Lock();
157     if (InstrumentEditorProxies.count(pSender)) {
158     InstrumentEditorProxy* pProxy =
159     dynamic_cast<InstrumentEditorProxy*>(
160     InstrumentEditorProxies[pSender]
161     );
162     InstrumentEditorProxies.erase(pSender);
163     InstrumentEditorProxiesMutex.Unlock();
164     HandBack(pProxy->pInstrument, pProxy);
165     if (pProxy) delete pProxy;
166     } else {
167     InstrumentEditorProxiesMutex.Unlock();
168     std::cerr << "Eeeek, could not find instrument editor proxy, this is a bug!\n" << std::flush;
169     }
170     // free the editor
171     InstrumentEditorFactory::Destroy(pSender);
172     }
173    
174 schoenebeck 53 ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
175     // get gig file from inernal gig file manager
176 schoenebeck 947 ::gig::File* pGig = Gigs.Borrow(Key.FileName, (GigConsumer*) Key.Index); // conversion kinda hackish :/
177 schoenebeck 53
178 schoenebeck 517 // we pass this to the progress callback mechanism of libgig
179     progress_callback_arg_t callbackArg;
180     callbackArg.pManager = this;
181     callbackArg.pInstrumentKey = &Key;
182    
183     ::gig::progress_t progress;
184     progress.callback = OnInstrumentLoadingProgress;
185     progress.custom = &callbackArg;
186    
187 schoenebeck 947 dmsg(1,("Loading gig instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
188     ::gig::Instrument* pInstrument = pGig->GetInstrument(Key.Index, &progress);
189 schoenebeck 53 if (!pInstrument) {
190     std::stringstream msg;
191 schoenebeck 947 msg << "There's no instrument with index " << Key.Index << ".";
192 schoenebeck 1212 throw InstrumentManagerException(msg.str());
193 schoenebeck 53 }
194     pGig->GetFirstSample(); // just to force complete instrument loading
195     dmsg(1,("OK\n"));
196    
197     // cache initial samples points (for actually needed samples)
198     dmsg(1,("Caching initial samples..."));
199 schoenebeck 517 uint iRegion = 0; // just for progress calculation
200 schoenebeck 53 ::gig::Region* pRgn = pInstrument->GetFirstRegion();
201     while (pRgn) {
202 schoenebeck 517 // we randomly schedule 90% for the .gig file loading and the remaining 10% now for sample caching
203     const float localProgress = 0.9f + 0.1f * (float) iRegion / (float) pInstrument->Regions;
204 schoenebeck 947 DispatchResourceProgressEvent(Key, localProgress);
205    
206 schoenebeck 354 if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {
207 schoenebeck 53 dmsg(2,("C"));
208 schoenebeck 947 CacheInitialSamples(pRgn->GetSample(), (gig::EngineChannel*) pConsumer);
209 schoenebeck 53 }
210     for (uint i = 0; i < pRgn->DimensionRegions; i++) {
211 schoenebeck 947 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, (gig::EngineChannel*) pConsumer);
212 schoenebeck 53 }
213    
214     pRgn = pInstrument->GetNextRegion();
215 schoenebeck 517 iRegion++;
216 schoenebeck 53 }
217     dmsg(1,("OK\n"));
218 schoenebeck 517 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
219 schoenebeck 53
220     // we need the following for destruction later
221     instr_entry_t* pEntry = new instr_entry_t;
222 schoenebeck 947 pEntry->ID.FileName = Key.FileName;
223     pEntry->ID.Index = Key.Index;
224 schoenebeck 53 pEntry->pGig = pGig;
225 persson 438
226 schoenebeck 1212 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
227 schoenebeck 411 // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'
228     pEntry->MaxSamplesPerCycle =
229 schoenebeck 1212 (!pEngineChannel) ? 0 /* don't care for instrument editors */ :
230     (pEngineChannel->GetEngine()) ?
231     dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
232     : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
233 schoenebeck 53 pArg = pEntry;
234    
235     return pInstrument;
236     }
237    
238     void InstrumentResourceManager::Destroy( ::gig::Instrument* pResource, void* pArg) {
239     instr_entry_t* pEntry = (instr_entry_t*) pArg;
240 schoenebeck 947 // we don't need the .gig file here anymore
241     Gigs.HandBack(pEntry->pGig, (GigConsumer*) pEntry->ID.Index); // conversion kinda hackish :/
242 schoenebeck 53 delete pEntry;
243     }
244    
245     void InstrumentResourceManager::OnBorrow(::gig::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) {
246     instr_entry_t* pEntry = (instr_entry_t*) pArg;
247 schoenebeck 411 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
248     uint maxSamplesPerCycle =
249 schoenebeck 1212 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
250 schoenebeck 411 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
251     if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
252 schoenebeck 53 Update(pResource, pConsumer);
253     }
254     }
255    
256     /**
257 persson 1038 * Give back an instrument. This should be used instead of
258     * HandBack if there are some dimension regions that are still in
259     * use. (When an instrument is changed, the voices currently
260     * playing is allowed to keep playing with the old instrument
261     * until note off arrives. New notes will use the new instrument.)
262     */
263     void InstrumentResourceManager::HandBackInstrument(::gig::Instrument* pResource, InstrumentConsumer* pConsumer,
264     ::gig::DimensionRegion** dimRegionsInUse) {
265     DimRegInfoMutex.Lock();
266     for (int i = 0 ; dimRegionsInUse[i] ; i++) {
267     DimRegInfo[dimRegionsInUse[i]].refCount++;
268     SampleRefCount[dimRegionsInUse[i]->pSample]++;
269     }
270     HandBack(pResource, pConsumer, true);
271     DimRegInfoMutex.Unlock();
272     }
273    
274     /**
275     * Give back a dimension region that belongs to an instrument that
276     * was previously handed back.
277     */
278     void InstrumentResourceManager::HandBackDimReg(::gig::DimensionRegion* pDimReg) {
279     DimRegInfoMutex.Lock();
280     dimreg_info_t& dimRegInfo = DimRegInfo[pDimReg];
281     int dimRegRefCount = --dimRegInfo.refCount;
282     int sampleRefCount = --SampleRefCount[pDimReg->pSample];
283     if (dimRegRefCount == 0) {
284     ::gig::File* gig = dimRegInfo.file;
285     ::RIFF::File* riff = dimRegInfo.riff;
286     DimRegInfo.erase(pDimReg);
287     // TODO: we could delete Region and Instrument here if
288     // they have become unused
289    
290     if (sampleRefCount == 0) {
291     SampleRefCount.erase(pDimReg->pSample);
292    
293     if (gig) {
294     gig->DeleteSample(pDimReg->pSample);
295     if (!gig->GetFirstSample()) {
296     dmsg(2,("No more samples in use - freeing gig\n"));
297     delete gig;
298     delete riff;
299     }
300     }
301     }
302     }
303     DimRegInfoMutex.Unlock();
304     }
305    
306     /**
307 schoenebeck 53 * Caches a certain size at the beginning of the given sample in RAM. If the
308     * sample is very short, the whole sample will be loaded into RAM and thus
309     * no disk streaming is needed for this sample. Caching an initial part of
310     * samples is needed to compensate disk reading latency.
311     *
312     * @param pSample - points to the sample to be cached
313 schoenebeck 411 * @param pEngineChannel - pointer to Gig Engine Channel which caused this call
314 schoenebeck 53 */
315 persson 438 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::EngineChannel* pEngineChannel) {
316 schoenebeck 420 if (!pSample) {
317 persson 438 dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
318 schoenebeck 420 return;
319     }
320     if (!pSample->SamplesTotal) return; // skip zero size samples
321 persson 438
322 schoenebeck 554 if (pSample->SamplesTotal <= CONFIG_PRELOAD_SAMPLES) {
323 schoenebeck 53 // Sample is too short for disk streaming, so we load the whole
324 schoenebeck 554 // sample into RAM and place 'pAudioIO->FragmentSize << CONFIG_MAX_PITCH'
325 schoenebeck 53 // number of '0' samples (silence samples) behind the official buffer
326     // border, to allow the interpolator do it's work even at the end of
327 persson 438 // the sample.
328 schoenebeck 411 const uint maxSamplesPerCycle =
329 schoenebeck 947 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
330 schoenebeck 411 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
331 schoenebeck 554 const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;
332 schoenebeck 420 const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;
333     if (currentlyCachedSilenceSamples < neededSilenceSamples) {
334     dmsg(3,("Caching whole sample (sample name: \"%s\", sample size: %d)\n", pSample->pInfo->Name.c_str(), pSample->SamplesTotal));
335     ::gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(neededSilenceSamples);
336     dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
337     }
338 schoenebeck 53 }
339 schoenebeck 554 else { // we only cache CONFIG_PRELOAD_SAMPLES and stream the other sample points from disk
340     if (!pSample->GetCache().Size) pSample->LoadSampleData(CONFIG_PRELOAD_SAMPLES);
341 schoenebeck 53 }
342    
343     if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
344     }
345    
346    
347    
348     // internal gig file manager
349    
350     ::gig::File* InstrumentResourceManager::GigResourceManager::Create(String Key, GigConsumer* pConsumer, void*& pArg) {
351     dmsg(1,("Loading gig file \'%s\'...", Key.c_str()));
352     ::RIFF::File* pRIFF = new ::RIFF::File(Key);
353     ::gig::File* pGig = new ::gig::File(pRIFF);
354     pArg = pRIFF;
355     dmsg(1,("OK\n"));
356     return pGig;
357     }
358    
359     void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
360     dmsg(1,("Freeing gig file from memory..."));
361 persson 1038
362     // Delete as much as possible of the gig file. Some of the
363     // dimension regions and samples may still be in use - these
364     // will be deleted later by the HandBackDimReg function.
365     bool deleteFile = true;
366     ::gig::Instrument* nextInstrument;
367     for (::gig::Instrument* instrument = pResource->GetFirstInstrument() ;
368     instrument ;
369     instrument = nextInstrument) {
370     nextInstrument = pResource->GetNextInstrument();
371     bool deleteInstrument = true;
372     ::gig::Region* nextRegion;
373     for (::gig::Region *region = instrument->GetFirstRegion() ;
374     region ;
375     region = nextRegion) {
376     nextRegion = instrument->GetNextRegion();
377     bool deleteRegion = true;
378     for (int i = 0 ; i < region->DimensionRegions ; i++)
379     {
380     ::gig::DimensionRegion *d = region->pDimensionRegions[i];
381     std::map< ::gig::DimensionRegion*, dimreg_info_t>::iterator iter = parent->DimRegInfo.find(d);
382     if (iter != parent->DimRegInfo.end()) {
383     dimreg_info_t& dimRegInfo = (*iter).second;
384     dimRegInfo.file = pResource;
385     dimRegInfo.riff = (::RIFF::File*)pArg;
386     deleteFile = deleteInstrument = deleteRegion = false;
387     }
388     }
389     if (deleteRegion) instrument->DeleteRegion(region);
390     }
391     if (deleteInstrument) pResource->DeleteInstrument(instrument);
392     }
393     if (deleteFile) {
394     delete pResource;
395     delete (::RIFF::File*) pArg;
396     } else {
397     dmsg(2,("keeping some samples that are in use..."));
398     ::gig::Sample* nextSample;
399     for (::gig::Sample* sample = pResource->GetFirstSample() ;
400     sample ;
401     sample = nextSample) {
402     nextSample = pResource->GetNextSample();
403     if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
404     pResource->DeleteSample(sample);
405     }
406     }
407     }
408 schoenebeck 53 dmsg(1,("OK\n"));
409     }
410    
411     }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC