/[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 2012 - (show annotations) (download)
Fri Oct 23 17:53:17 2009 UTC (14 years, 5 months ago) by iliev
File size: 13200 byte(s)
* Refactoring: moved the independent code from
  the Gigasampler format engine to base classes
* SFZ format engine: experimental code (not usable yet)
* SoundFont format engine: experimental code (not usable yet)
* Fixed crash which may occur when MIDI key + transpose is out of range

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

  ViewVC Help
Powered by ViewVC