/[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 947 - (hide annotations) (download)
Mon Nov 27 21:34:55 2006 UTC (17 years, 4 months ago) by schoenebeck
File size: 12929 byte(s)
* implemented MIDI instrument mapping according to latest LSCP draft

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     float InstrumentResourceManager::GetVolume(const instrument_id_t& ID) {
81     void* pCustomData = CustomData(ID);
82     const float fVolume = (pCustomData) ? *((float*)pCustomData) /* stored value */ : 1.0f /* default value */;
83     return fVolume;
84     }
85    
86     void InstrumentResourceManager::SetVolume(const instrument_id_t& ID, float Volume) {
87     void* pCustomData = CustomData(ID);
88     if (Volume == 1.0f) { // if default volume ...
89     if (pCustomData) { // ... delete volume entry if necessary
90     delete (float*)pCustomData;
91     SetCustomData(ID, NULL);
92     }
93     } else { // if not default volume
94     if (!pCustomData) { // create volume entry if necessary
95     pCustomData = new float;
96     SetCustomData(ID, pCustomData);
97     }
98     *((float*)pCustomData) = Volume;
99     }
100     }
101    
102     String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
103     return ""; // TODO: ...
104     }
105    
106 schoenebeck 53 ::gig::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
107     // get gig file from inernal gig file manager
108 schoenebeck 947 ::gig::File* pGig = Gigs.Borrow(Key.FileName, (GigConsumer*) Key.Index); // conversion kinda hackish :/
109 schoenebeck 53
110 schoenebeck 517 // we pass this to the progress callback mechanism of libgig
111     progress_callback_arg_t callbackArg;
112     callbackArg.pManager = this;
113     callbackArg.pInstrumentKey = &Key;
114    
115     ::gig::progress_t progress;
116     progress.callback = OnInstrumentLoadingProgress;
117     progress.custom = &callbackArg;
118    
119 schoenebeck 947 dmsg(1,("Loading gig instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
120     ::gig::Instrument* pInstrument = pGig->GetInstrument(Key.Index, &progress);
121 schoenebeck 53 if (!pInstrument) {
122     std::stringstream msg;
123 schoenebeck 947 msg << "There's no instrument with index " << Key.Index << ".";
124 schoenebeck 53 throw InstrumentResourceManagerException(msg.str());
125     }
126     pGig->GetFirstSample(); // just to force complete instrument loading
127     dmsg(1,("OK\n"));
128    
129     // cache initial samples points (for actually needed samples)
130     dmsg(1,("Caching initial samples..."));
131 schoenebeck 517 uint iRegion = 0; // just for progress calculation
132 schoenebeck 53 ::gig::Region* pRgn = pInstrument->GetFirstRegion();
133     while (pRgn) {
134 schoenebeck 517 // we randomly schedule 90% for the .gig file loading and the remaining 10% now for sample caching
135     const float localProgress = 0.9f + 0.1f * (float) iRegion / (float) pInstrument->Regions;
136 schoenebeck 947 DispatchResourceProgressEvent(Key, localProgress);
137    
138 schoenebeck 354 if (pRgn->GetSample() && !pRgn->GetSample()->GetCache().Size) {
139 schoenebeck 53 dmsg(2,("C"));
140 schoenebeck 947 CacheInitialSamples(pRgn->GetSample(), (gig::EngineChannel*) pConsumer);
141 schoenebeck 53 }
142     for (uint i = 0; i < pRgn->DimensionRegions; i++) {
143 schoenebeck 947 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample, (gig::EngineChannel*) pConsumer);
144 schoenebeck 53 }
145    
146     pRgn = pInstrument->GetNextRegion();
147 schoenebeck 517 iRegion++;
148 schoenebeck 53 }
149     dmsg(1,("OK\n"));
150 schoenebeck 517 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
151 schoenebeck 53
152     // we need the following for destruction later
153     instr_entry_t* pEntry = new instr_entry_t;
154 schoenebeck 947 pEntry->ID.FileName = Key.FileName;
155     pEntry->ID.Index = Key.Index;
156 schoenebeck 53 pEntry->pGig = pGig;
157 persson 438
158 schoenebeck 947 gig::EngineChannel* pEngineChannel = (gig::EngineChannel*) pConsumer;
159 schoenebeck 411 // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'
160     pEntry->MaxSamplesPerCycle =
161 schoenebeck 947 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
162 schoenebeck 411 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
163 schoenebeck 53 pArg = pEntry;
164    
165     return pInstrument;
166     }
167    
168     void InstrumentResourceManager::Destroy( ::gig::Instrument* pResource, void* pArg) {
169     instr_entry_t* pEntry = (instr_entry_t*) pArg;
170 schoenebeck 947 // remove volume entry if necessary
171     void* pCustomData = CustomData(pEntry->ID);
172     if (pCustomData) {
173     delete (float*)pCustomData;
174     SetCustomData(pEntry->ID, NULL);
175     }
176     // we don't need the .gig file here anymore
177     Gigs.HandBack(pEntry->pGig, (GigConsumer*) pEntry->ID.Index); // conversion kinda hackish :/
178 schoenebeck 53 delete pEntry;
179     }
180    
181     void InstrumentResourceManager::OnBorrow(::gig::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) {
182     instr_entry_t* pEntry = (instr_entry_t*) pArg;
183 schoenebeck 411 gig::EngineChannel* pEngineChannel = dynamic_cast<gig::EngineChannel*>(pConsumer);
184     uint maxSamplesPerCycle =
185     (pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
186     : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
187     if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
188 schoenebeck 53 Update(pResource, pConsumer);
189     }
190     }
191    
192     /**
193     * Caches a certain size at the beginning of the given sample in RAM. If the
194     * sample is very short, the whole sample will be loaded into RAM and thus
195     * no disk streaming is needed for this sample. Caching an initial part of
196     * samples is needed to compensate disk reading latency.
197     *
198     * @param pSample - points to the sample to be cached
199 schoenebeck 411 * @param pEngineChannel - pointer to Gig Engine Channel which caused this call
200 schoenebeck 53 */
201 persson 438 void InstrumentResourceManager::CacheInitialSamples(::gig::Sample* pSample, gig::EngineChannel* pEngineChannel) {
202 schoenebeck 420 if (!pSample) {
203 persson 438 dmsg(4,("gig::InstrumentResourceManager: Skipping sample (pSample == NULL)\n"));
204 schoenebeck 420 return;
205     }
206     if (!pSample->SamplesTotal) return; // skip zero size samples
207 persson 438
208 schoenebeck 554 if (pSample->SamplesTotal <= CONFIG_PRELOAD_SAMPLES) {
209 schoenebeck 53 // Sample is too short for disk streaming, so we load the whole
210 schoenebeck 554 // sample into RAM and place 'pAudioIO->FragmentSize << CONFIG_MAX_PITCH'
211 schoenebeck 53 // number of '0' samples (silence samples) behind the official buffer
212     // border, to allow the interpolator do it's work even at the end of
213 persson 438 // the sample.
214 schoenebeck 411 const uint maxSamplesPerCycle =
215 schoenebeck 947 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<gig::Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
216 schoenebeck 411 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
217 schoenebeck 554 const uint neededSilenceSamples = (maxSamplesPerCycle << CONFIG_MAX_PITCH) + 3;
218 schoenebeck 420 const uint currentlyCachedSilenceSamples = pSample->GetCache().NullExtensionSize / pSample->FrameSize;
219     if (currentlyCachedSilenceSamples < neededSilenceSamples) {
220     dmsg(3,("Caching whole sample (sample name: \"%s\", sample size: %d)\n", pSample->pInfo->Name.c_str(), pSample->SamplesTotal));
221     ::gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(neededSilenceSamples);
222     dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
223     }
224 schoenebeck 53 }
225 schoenebeck 554 else { // we only cache CONFIG_PRELOAD_SAMPLES and stream the other sample points from disk
226     if (!pSample->GetCache().Size) pSample->LoadSampleData(CONFIG_PRELOAD_SAMPLES);
227 schoenebeck 53 }
228    
229     if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
230     }
231    
232    
233    
234     // internal gig file manager
235    
236     ::gig::File* InstrumentResourceManager::GigResourceManager::Create(String Key, GigConsumer* pConsumer, void*& pArg) {
237     dmsg(1,("Loading gig file \'%s\'...", Key.c_str()));
238     ::RIFF::File* pRIFF = new ::RIFF::File(Key);
239     ::gig::File* pGig = new ::gig::File(pRIFF);
240     pArg = pRIFF;
241     dmsg(1,("OK\n"));
242     return pGig;
243     }
244    
245     void InstrumentResourceManager::GigResourceManager::Destroy(::gig::File* pResource, void* pArg) {
246     dmsg(1,("Freeing gig file from memory..."));
247     delete pResource;
248     delete (::RIFF::File*) pArg;
249     dmsg(1,("OK\n"));
250     }
251    
252     }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC