/[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 1038 - (hide annotations) (download)
Sat Feb 3 15:33:00 2007 UTC (17 years, 2 months ago) by persson
File size: 16152 byte(s)
* playback is no longer disabled during instrument loading
* all notes playing on a channel that changes its instrument keep
  playing with the old instrument until they get a note off command
* new thread safety fix for lscp "load engine" and "set channel audio
  output device"

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 947 * Copyright (C) 2005, 2006 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 411 // We need to know the maximum number of sample points which are going to
29     // be processed for each render cycle of the audio output driver, to know
30     // how much initial sample points we need to cache into RAM. If the given
31     // sampler channel does not have an audio output device assigned yet
32     // though, we simply use this default value.
33     #define GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE 128
34    
35 schoenebeck 53 namespace LinuxSampler { namespace gig {
36    
37 schoenebeck 947 // data stored as long as an instrument resource exists
38     struct instr_entry_t {
39     InstrumentManager::instrument_id_t ID;
40     ::gig::File* pGig;
41     uint MaxSamplesPerCycle; ///< if some engine requests an already allocated instrument with a higher value, we have to reallocate the instrument
42     };
43    
44 schoenebeck 517 // some data needed for the libgig callback function
45     struct progress_callback_arg_t {
46 schoenebeck 947 InstrumentResourceManager* pManager;
47     InstrumentManager::instrument_id_t* pInstrumentKey;
48 schoenebeck 517 };
49    
50     /**
51     * Callback function which will be called by libgig during loading of
52     * instruments to inform about the current progress. Or to be more
53     * specific; it will be called during the GetInstrument() call.
54     *
55     * @param pProgress - contains current progress value, pointer to the
56     * InstrumentResourceManager instance and
57     * instrument ID
58     */
59     void InstrumentResourceManager::OnInstrumentLoadingProgress(::gig::progress_t* pProgress) {
60     dmsg(7,("gig::InstrumentResourceManager: progress %f%", pProgress->factor));
61     progress_callback_arg_t* pArg = static_cast<progress_callback_arg_t*>(pProgress->custom);
62     // we randomly schedule 90% for the .gig file loading and the remaining 10% later for sample caching
63     const float localProgress = 0.9f * pProgress->factor;
64     pArg->pManager->DispatchResourceProgressEvent(*pArg->pInstrumentKey, localProgress);
65     }
66    
67 schoenebeck 947 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::Instruments() {
68     return Entries();
69     }
70    
71     InstrumentManager::mode_t InstrumentResourceManager::GetMode(const instrument_id_t& ID) {
72     return static_cast<InstrumentManager::mode_t>(AvailabilityMode(ID));
73     }
74    
75     void InstrumentResourceManager::SetMode(const instrument_id_t& ID, InstrumentManager::mode_t Mode) {
76     dmsg(2,("gig::InstrumentResourceManager: setting mode for %s (Index=%d) to %d\n",ID.FileName.c_str(),ID.Index,Mode));
77     SetAvailabilityMode(ID, static_cast<ResourceManager<InstrumentManager::instrument_id_t, ::gig::Instrument>::mode_t>(Mode));
78     }
79    
80     String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
81 schoenebeck 970 Lock();
82     ::gig::Instrument* pInstrument = Resource(ID, false);
83     String res = (pInstrument) ? pInstrument->pInfo->Name : "";
84     Unlock();
85     return res;
86 schoenebeck 947 }
87    
88 schoenebeck 53 ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
89     // get gig file from inernal gig file manager
90 schoenebeck 947 ::gig::File* pGig = Gigs.Borrow(Key.FileName, (GigConsumer*) Key.Index); // conversion kinda hackish :/
91 schoenebeck 53
92 schoenebeck 517 // we pass this to the progress callback mechanism of libgig
93     progress_callback_arg_t callbackArg;
94     callbackArg.pManager = this;
95     callbackArg.pInstrumentKey = &Key;
96    
97     ::gig::progress_t progress;
98     progress.callback = OnInstrumentLoadingProgress;
99     progress.custom = &callbackArg;
100    
101 schoenebeck 947 dmsg(1,("Loading gig instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
102     ::gig::Instrument* pInstrument = pGig->GetInstrument(Key.Index, &progress);
103 schoenebeck 53 if (!pInstrument) {
104     std::stringstream msg;
105 schoenebeck 947 msg << "There's no instrument with index " << Key.Index << ".";
106 schoenebeck 53 throw InstrumentResourceManagerException(msg.str());
107     }
108     pGig->GetFirstSample(); // just to force complete instrument loading
109     dmsg(1,("OK\n"));
110    
111     // cache initial samples points (for actually needed samples)
112     dmsg(1,("Caching initial samples..."));
113 schoenebeck 517 uint iRegion = 0; // just for progress calculation
114 schoenebeck 53 ::gig::Region* pRgn = pInstrument->GetFirstRegion();
115     while (pRgn) {
116 schoenebeck 517 // we randomly schedule 90% for the .gig file loading and the remaining 10% now for sample caching
117     const float localProgress = 0.9f + 0.1f * (float) iRegion / (float) pInstrument->Regions;
118 schoenebeck 947 DispatchResourceProgressEvent(Key, localProgress);
119    
120 schoenebeck 354 if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {
121 schoenebeck 53 dmsg(2,("C"));
122 schoenebeck 947 CacheInitialSamples(pRgn->GetSample(), (gig::EngineChannel*) pConsumer);
123 schoenebeck 53 }
124     for (uint i = 0; i < pRgn->DimensionRegions; i++) {
125 schoenebeck 947 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, (gig::EngineChannel*) pConsumer);
126 schoenebeck 53 }
127    
128     pRgn = pInstrument->GetNextRegion();
129 schoenebeck 517 iRegion++;
130 schoenebeck 53 }
131     dmsg(1,("OK\n"));
132 schoenebeck 517 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
133 schoenebeck 53
134     // we need the following for destruction later
135     instr_entry_t* pEntry = new instr_entry_t;
136 schoenebeck 947 pEntry->ID.FileName = Key.FileName;
137     pEntry->ID.Index = Key.Index;
138 schoenebeck 53 pEntry->pGig = pGig;
139 persson 438
140 schoenebeck 947 gig::EngineChannel* pEngineChannel = (gig::EngineChannel*) pConsumer;
141 schoenebeck 411 // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'
142     pEntry->MaxSamplesPerCycle =
143 schoenebeck 947 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
144 schoenebeck 411 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
145 schoenebeck 53 pArg = pEntry;
146    
147     return pInstrument;
148     }
149    
150     void InstrumentResourceManager::Destroy( ::gig::Instrument* pResource, void* pArg) {
151     instr_entry_t* pEntry = (instr_entry_t*) pArg;
152 schoenebeck 947 // we don't need the .gig file here anymore
153     Gigs.HandBack(pEntry->pGig, (GigConsumer*) pEntry->ID.Index); // conversion kinda hackish :/
154 schoenebeck 53 delete pEntry;
155     }
156    
157     void InstrumentResourceManager::OnBorrow(::gig::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) {
158     instr_entry_t* pEntry = (instr_entry_t*) pArg;
159 schoenebeck 411 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
160     uint maxSamplesPerCycle =
161     (pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
162     : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
163     if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
164 schoenebeck 53 Update(pResource, pConsumer);
165     }
166     }
167    
168     /**
169 persson 1038 * Give back an instrument. This should be used instead of
170     * HandBack if there are some dimension regions that are still in
171     * use. (When an instrument is changed, the voices currently
172     * playing is allowed to keep playing with the old instrument
173     * until note off arrives. New notes will use the new instrument.)
174     */
175     void InstrumentResourceManager::HandBackInstrument(::gig::Instrument* pResource, InstrumentConsumer* pConsumer,
176     ::gig::DimensionRegion** dimRegionsInUse) {
177     DimRegInfoMutex.Lock();
178     for (int i = 0 ; dimRegionsInUse[i] ; i++) {
179     DimRegInfo[dimRegionsInUse[i]].refCount++;
180     SampleRefCount[dimRegionsInUse[i]->pSample]++;
181     }
182     HandBack(pResource, pConsumer, true);
183     DimRegInfoMutex.Unlock();
184     }
185    
186     /**
187     * Give back a dimension region that belongs to an instrument that
188     * was previously handed back.
189     */
190     void InstrumentResourceManager::HandBackDimReg(::gig::DimensionRegion* pDimReg) {
191     DimRegInfoMutex.Lock();
192     dimreg_info_t& dimRegInfo = DimRegInfo[pDimReg];
193     int dimRegRefCount = --dimRegInfo.refCount;
194     int sampleRefCount = --SampleRefCount[pDimReg->pSample];
195     if (dimRegRefCount == 0) {
196     ::gig::File* gig = dimRegInfo.file;
197     ::RIFF::File* riff = dimRegInfo.riff;
198     DimRegInfo.erase(pDimReg);
199     // TODO: we could delete Region and Instrument here if
200     // they have become unused
201    
202     if (sampleRefCount == 0) {
203     SampleRefCount.erase(pDimReg->pSample);
204    
205     if (gig) {
206     gig->DeleteSample(pDimReg->pSample);
207     if (!gig->GetFirstSample()) {
208     dmsg(2,("No more samples in use - freeing gig\n"));
209     delete gig;
210     delete riff;
211     }
212     }
213     }
214     }
215     DimRegInfoMutex.Unlock();
216     }
217    
218     /**
219 schoenebeck 53 * Caches a certain size at the beginning of the given sample in RAM. If the
220     * sample is very short, the whole sample will be loaded into RAM and thus
221     * no disk streaming is needed for this sample. Caching an initial part of
222     * samples is needed to compensate disk reading latency.
223     *
224     * @param pSample - points to the sample to be cached
225 schoenebeck 411 * @param pEngineChannel - pointer to Gig Engine Channel which caused this call
226 schoenebeck 53 */
227 persson 438 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::EngineChannel* pEngineChannel) {
228 schoenebeck 420 if (!pSample) {
229 persson 438 dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
230 schoenebeck 420 return;
231     }
232     if (!pSample->SamplesTotal) return; // skip zero size samples
233 persson 438
234 schoenebeck 554 if (pSample->SamplesTotal <= CONFIG_PRELOAD_SAMPLES) {
235 schoenebeck 53 // Sample is too short for disk streaming, so we load the whole
236 schoenebeck 554 // sample into RAM and place 'pAudioIO->FragmentSize << CONFIG_MAX_PITCH'
237 schoenebeck 53 // number of '0' samples (silence samples) behind the official buffer
238     // border, to allow the interpolator do it's work even at the end of
239 persson 438 // the sample.
240 schoenebeck 411 const uint maxSamplesPerCycle =
241 schoenebeck 947 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
242 schoenebeck 411 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
243 schoenebeck 554 const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;
244 schoenebeck 420 const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;
245     if (currentlyCachedSilenceSamples < neededSilenceSamples) {
246     dmsg(3,("Caching whole sample (sample name: \"%s\", sample size: %d)\n", pSample->pInfo->Name.c_str(), pSample->SamplesTotal));
247     ::gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(neededSilenceSamples);
248     dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
249     }
250 schoenebeck 53 }
251 schoenebeck 554 else { // we only cache CONFIG_PRELOAD_SAMPLES and stream the other sample points from disk
252     if (!pSample->GetCache().Size) pSample->LoadSampleData(CONFIG_PRELOAD_SAMPLES);
253 schoenebeck 53 }
254    
255     if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
256     }
257    
258    
259    
260     // internal gig file manager
261    
262     ::gig::File* InstrumentResourceManager::GigResourceManager::Create(String Key, GigConsumer* pConsumer, void*& pArg) {
263     dmsg(1,("Loading gig file \'%s\'...", Key.c_str()));
264     ::RIFF::File* pRIFF = new ::RIFF::File(Key);
265     ::gig::File* pGig = new ::gig::File(pRIFF);
266     pArg = pRIFF;
267     dmsg(1,("OK\n"));
268     return pGig;
269     }
270    
271     void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
272     dmsg(1,("Freeing gig file from memory..."));
273 persson 1038
274     // Delete as much as possible of the gig file. Some of the
275     // dimension regions and samples may still be in use - these
276     // will be deleted later by the HandBackDimReg function.
277     bool deleteFile = true;
278     ::gig::Instrument* nextInstrument;
279     for (::gig::Instrument* instrument = pResource->GetFirstInstrument() ;
280     instrument ;
281     instrument = nextInstrument) {
282     nextInstrument = pResource->GetNextInstrument();
283     bool deleteInstrument = true;
284     ::gig::Region* nextRegion;
285     for (::gig::Region *region = instrument->GetFirstRegion() ;
286     region ;
287     region = nextRegion) {
288     nextRegion = instrument->GetNextRegion();
289     bool deleteRegion = true;
290     for (int i = 0 ; i < region->DimensionRegions ; i++)
291     {
292     ::gig::DimensionRegion *d = region->pDimensionRegions[i];
293     std::map< ::gig::DimensionRegion*, dimreg_info_t>::iterator iter = parent->DimRegInfo.find(d);
294     if (iter != parent->DimRegInfo.end()) {
295     dimreg_info_t& dimRegInfo = (*iter).second;
296     dimRegInfo.file = pResource;
297     dimRegInfo.riff = (::RIFF::File*)pArg;
298     deleteFile = deleteInstrument = deleteRegion = false;
299     }
300     }
301     if (deleteRegion) instrument->DeleteRegion(region);
302     }
303     if (deleteInstrument) pResource->DeleteInstrument(instrument);
304     }
305     if (deleteFile) {
306     delete pResource;
307     delete (::RIFF::File*) pArg;
308     } else {
309     dmsg(2,("keeping some samples that are in use..."));
310     ::gig::Sample* nextSample;
311     for (::gig::Sample* sample = pResource->GetFirstSample() ;
312     sample ;
313     sample = nextSample) {
314     nextSample = pResource->GetNextSample();
315     if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
316     pResource->DeleteSample(sample);
317     }
318     }
319     }
320 schoenebeck 53 dmsg(1,("OK\n"));
321     }
322    
323     }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC