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

Contents of /linuxsampler/trunk/src/engines/sf2/InstrumentResourceManager.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2021 - (show annotations) (download)
Fri Oct 30 16:36:20 2009 UTC (14 years, 5 months ago) by iliev
File size: 13597 byte(s)
* sfz engine: loop support
* sf2 engine: 24bit support
* sf2 engine: loop support
* sf2 engine: instrument unloading

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2009 Christian Schoenebeck *
7 * Copyright (C) 2009 Grigor Iliev *
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 * This program is distributed in the hope that it will be useful, *
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17 * GNU General Public License for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License *
20 * along with this program; if not, write to the Free Software *
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
22 * MA 02111-1307 USA *
23 ***************************************************************************/
24
25 #include <sstream>
26
27 #include "InstrumentResourceManager.h"
28 #include "EngineChannel.h"
29 #include "Engine.h"
30
31 #include "../../common/global_private.h"
32 #include "../../common/Path.h"
33 #include "../../plugins/InstrumentEditorFactory.h"
34
35
36 namespace LinuxSampler { namespace sf2 {
37
38 // data stored as long as an instrument resource exists
39 struct instr_entry_t {
40 InstrumentManager::instrument_id_t ID;
41 ::sf2::File* pSf2;
42 uint MaxSamplesPerCycle; ///< if some engine requests an already allocated instrument with a higher value, we have to reallocate the instrument
43 };
44
45 // some data needed for the libgig callback function
46 struct progress_callback_arg_t {
47 InstrumentResourceManager* pManager;
48 InstrumentManager::instrument_id_t* pInstrumentKey;
49 };
50
51 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::Instruments() {
52 return Entries();
53 }
54
55 String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
56 Lock();
57 ::sf2::InstrumentBase* pInstrument = Resource(ID, false);
58 String res = (pInstrument) ? pInstrument->GetName() : "";
59 Unlock();
60 return res;
61 }
62
63 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::GetInstrumentFileContent(String File) throw (InstrumentManagerException) {
64 ::RIFF::File* riff = NULL;
65 ::sf2::File* sf2 = NULL;
66 try {
67 std::vector<instrument_id_t> result;
68 riff = new ::RIFF::File(File);
69 sf2 = new ::sf2::File(riff);
70 for (int i = 0; i < GetSfInstrumentCount(sf2); i++) {
71 instrument_id_t id;
72 id.FileName = File;
73 id.Index = i;
74 result.push_back(id);
75 }
76 if (sf2) delete sf2;
77 if (riff) delete riff;
78 return result;
79 } catch (::RIFF::Exception e) {
80 if (sf2) delete sf2;
81 if (riff) delete riff;
82 throw InstrumentManagerException(e.Message);
83 } catch (...) {
84 if (sf2) delete sf2;
85 if (riff) delete riff;
86 throw InstrumentManagerException("Unknown exception while trying to parse '" + File + "'");
87 }
88 }
89
90 InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) {
91 Lock();
92 ::sf2::InstrumentBase* pInstrument = Resource(ID, false);
93 bool loaded = (pInstrument != NULL);
94 if (!loaded) Unlock();
95
96 ::RIFF::File* riff = NULL;
97 ::sf2::File* sf2 = NULL;
98 try {
99 if(!loaded) {
100 riff = new ::RIFF::File(ID.FileName);
101 sf2 = new ::sf2::File(riff);
102 pInstrument = GetSfInstrument(sf2, ID.Index);
103 }
104
105 instrument_info_t info;
106 for (int i = 0; i < 128; i++) { info.KeyBindings[i] = info.KeySwitchBindings[i] = 0; }
107
108 ::sf2::File* pFile = pInstrument->GetFile();
109
110 info.FormatVersion = ToString(pFile->pInfo->pVer->Major);
111 info.Product = pFile->pInfo->Product;
112 info.Artists = pFile->pInfo->Engineers;
113
114 info.InstrumentName = pInstrument->Name;
115
116 for (int i = 0; i < pInstrument->GetRegionCount(); i++) {
117 int low = pInstrument->GetRegion(i)->loKey;
118 int high = pInstrument->GetRegion(i)->hiKey;
119 if (low < 0 || low > 127 || high < 0 || high > 127 || low > high) {
120 std::cerr << "Invalid key range: " << low << " - " << high << std::endl;
121 } else {
122 for (int i = low; i <= high; i++) info.KeyBindings[i] = 1;
123 }
124 }
125
126 if (loaded) Unlock();
127
128 if (sf2) delete sf2;
129 if (riff) delete riff;
130 return info;
131 } catch (::sf2::Exception e) {
132 if (loaded) Unlock();
133 if (sf2) delete sf2;
134 if (riff) delete riff;
135 throw InstrumentManagerException(e.Message);
136 } catch (...) {
137 if (loaded) Unlock();
138 if (sf2) delete sf2;
139 if (riff) delete riff;
140 throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'");
141 }
142 }
143
144 ::sf2::InstrumentBase* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
145 // get sfz file from internal sfz file manager
146 ::sf2::File* pSf2 = Sf2s.Borrow(Key.FileName, (Sf2Consumer*) Key.Index); // conversion kinda hackish :/
147
148 dmsg(1,("Loading sf2 instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
149 ::sf2::InstrumentBase* pInstrument = GetSfInstrument(pSf2, Key.Index);
150 dmsg(1,("OK\n"));
151
152 // cache initial samples points (for actually needed samples)
153 dmsg(1,("Caching initial samples..."));
154 float regTotal = 0, regCurrent = 0;
155 for(int i = 0 ; i < pInstrument->GetRegionCount(); i++) {
156 ::sf2::Instrument* sf2Instr = pInstrument->GetRegion(i)->pInstrument;
157 if(sf2Instr) regTotal += sf2Instr->GetRegionCount();
158 else regTotal++; // if sf2Instr is null, than pInstrument is not a preset
159 }
160 for(int i = 0 ; i < pInstrument->GetRegionCount(); i++) {
161 ::sf2::Instrument* sf2Instr = pInstrument->GetRegion(i)->pInstrument;
162 ::sf2::Sample* sf2Sample = pInstrument->GetRegion(i)->GetSample();
163 if(sf2Instr != NULL) {
164 // pInstrument is ::sf2::Preset
165 for(int j = 0; j < sf2Instr->GetRegionCount(); j++) {
166 const float localProgress = regCurrent++ / regTotal;
167 DispatchResourceProgressEvent(Key, localProgress);
168 CacheInitialSamples(sf2Instr->GetRegion(j)->GetSample(), dynamic_cast<AbstractEngineChannel*>(pConsumer));
169 }
170 } else if(sf2Sample != NULL) {
171 // pInstrument is ::sf2::Instrument
172 CacheInitialSamples(sf2Sample, dynamic_cast<AbstractEngineChannel*>(pConsumer));
173 }
174 }
175 dmsg(1,("OK\n"));
176 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
177
178 // we need the following for destruction later
179 instr_entry_t* pEntry = new instr_entry_t;
180 pEntry->ID.FileName = Key.FileName;
181 pEntry->ID.Index = Key.Index;
182 pEntry->pSf2 = pSf2;
183
184 EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(pConsumer);
185 // and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond'
186 pEntry->MaxSamplesPerCycle =
187 (!pEngineChannel) ? 0 /* don't care for instrument editors */ :
188 (pEngineChannel->GetEngine()) ?
189 dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
190 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
191 pArg = pEntry;
192
193 return pInstrument;
194 }
195
196 void InstrumentResourceManager::Destroy( ::sf2::InstrumentBase* pResource, void* pArg) {
197 instr_entry_t* pEntry = (instr_entry_t*) pArg;
198 // we don't need the .sf2 file here anymore
199 Sf2s.HandBack(pEntry->pSf2, (Sf2Consumer*) pEntry->ID.Index); // conversion kinda hackish :/
200 delete pEntry;
201 }
202
203 void InstrumentResourceManager::OnBorrow(::sf2::InstrumentBase* pResource, InstrumentConsumer* pConsumer, void*& pArg) {
204 instr_entry_t* pEntry = (instr_entry_t*) pArg;
205 EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(pConsumer);
206 uint maxSamplesPerCycle =
207 (pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle()
208 : GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE;
209 if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) {
210 Update(pResource, pConsumer);
211 }
212 }
213
214 void InstrumentResourceManager::DeleteRegionIfNotUsed(::sf2::Region* pRegion, region_info_t* pRegInfo) {
215 // TODO: we could delete Region and Instrument here if they have become unused
216 }
217
218 void InstrumentResourceManager::DeleteSampleIfNotUsed(::sf2::Sample* pSample, region_info_t* pRegInfo) {
219 ::sf2::File* sf2 = pRegInfo->file;
220 ::RIFF::File* riff = static_cast< ::RIFF::File*>(pRegInfo->pArg);
221 if (sf2) {
222 sf2->DeleteSample(pSample);
223 if (!sf2->HasSamples()) {
224 dmsg(2,("No more samples in use - freeing sf2\n"));
225 delete sf2;
226 delete riff;
227 }
228 }
229 }
230
231
232
233 // internal sfz file manager
234
235 ::sf2::File* InstrumentResourceManager::Sf2ResourceManager::Create(String Key, Sf2Consumer* pConsumer, void*& pArg) {
236 dmsg(1,("Loading sf2 file \'%s\'...", Key.c_str()));
237 ::RIFF::File* pRIFF = new ::RIFF::File(Key);
238 ::sf2::File* pSf2 = new ::sf2::File(pRIFF);
239 pArg = pRIFF;
240 dmsg(1,("OK\n"));
241 return pSf2;
242 }
243
244 void InstrumentResourceManager::Sf2ResourceManager::Destroy(::sf2::File* pResource, void* pArg) {
245 dmsg(1,("Freeing sf2 file from memory..."));
246
247 // Delete as much as possible of the sf2 file. Some of the
248 // regions and samples may still be in use - these
249 // will be deleted later by the HandBackRegion function.
250 bool deleteFile = true;
251
252 for (int i = pResource->GetInstrumentCount() - 1; i >= 0; i--) {
253 ::sf2::Instrument* pInstr = pResource->GetInstrument(i);
254 bool deleteInstrument = true;
255
256 for (int j = pInstr->GetRegionCount() - 1; j >= 0 ; j--) {
257 ::sf2::Region* pRegion = pInstr->GetRegion(j);
258 std::map< ::sf2::Region*, region_info_t>::iterator iter = parent->RegionInfo.find(pRegion);
259 if (iter != parent->RegionInfo.end()) {
260 region_info_t& regInfo = (*iter).second;
261 regInfo.file = pResource;
262 deleteFile = deleteInstrument = false;
263 } else {
264 pInstr->DeleteRegion(pRegion);
265 }
266 }
267
268 if (deleteInstrument) pResource->DeleteInstrument(pInstr);
269 }
270
271 if (deleteFile) {
272 delete pResource;
273 delete (::RIFF::File*) pArg;
274 } else {
275 dmsg(2,("keeping some samples that are in use..."));
276 for (int i = pResource->GetSampleCount() - 1; i >= 0; i--) {
277 ::sf2::Sample* sample = pResource->GetSample(i);
278 if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
279 pResource->DeleteSample(sample);
280 }
281 }
282 }
283
284 dmsg(1,("OK\n"));
285 }
286
287 int InstrumentResourceManager::GetSfInstrumentCount(::sf2::File* pFile) {
288 return pFile->GetInstrumentCount() + pFile->GetPresetCount();
289 }
290
291 ::sf2::InstrumentBase* InstrumentResourceManager::GetSfInstrument(::sf2::File* pFile, int idx) {
292 if (idx >= pFile->GetInstrumentCount() + pFile->GetPresetCount()) {
293 throw InstrumentManagerException("There is no instrument with index " + ToString(idx));
294 }
295
296 if (idx < pFile->GetInstrumentCount()) {
297 return pFile->GetInstrument(idx);
298 }
299
300 int presetIdx = idx - pFile->GetInstrumentCount();
301 return pFile->GetPreset(presetIdx);
302 }
303
304 }} // namespace LinuxSampler::sfz

  ViewVC Help
Powered by ViewVC