/[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 2326 - (show annotations) (download)
Thu Mar 8 19:40:14 2012 UTC (12 years ago) by persson
File size: 11527 byte(s)
* bugfix: instrument loading crashed for sfz and sf2 in Ardour (#176)
* more thread safety fixes for the instrument loading thread

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2008 Christian Schoenebeck *
7 * Copyright (C) 2009 - 2012 Christian Schoenebeck and 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 String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) {
39 Lock();
40 ::sf2::Preset* pInstrument = Resource(ID, false);
41 String res = (pInstrument) ? pInstrument->GetName() : "";
42 Unlock();
43 return res;
44 }
45
46 std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::GetInstrumentFileContent(String File) throw (InstrumentManagerException) {
47 ::RIFF::File* riff = NULL;
48 ::sf2::File* sf2 = NULL;
49 try {
50 std::vector<instrument_id_t> result;
51 riff = new ::RIFF::File(File);
52 sf2 = new ::sf2::File(riff);
53 for (int i = 0; i < GetSfInstrumentCount(sf2); i++) {
54 instrument_id_t id;
55 id.FileName = File;
56 id.Index = i;
57 result.push_back(id);
58 }
59 if (sf2) delete sf2;
60 if (riff) delete riff;
61 return result;
62 } catch (::RIFF::Exception e) {
63 if (sf2) delete sf2;
64 if (riff) delete riff;
65 throw InstrumentManagerException(e.Message);
66 } catch (...) {
67 if (sf2) delete sf2;
68 if (riff) delete riff;
69 throw InstrumentManagerException("Unknown exception while trying to parse '" + File + "'");
70 }
71 }
72
73 InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) {
74 Lock();
75 ::sf2::Preset* pInstrument = Resource(ID, false);
76 bool loaded = (pInstrument != NULL);
77 if (!loaded) Unlock();
78
79 ::RIFF::File* riff = NULL;
80 ::sf2::File* sf2 = NULL;
81 try {
82 if (!loaded) {
83 riff = new ::RIFF::File(ID.FileName);
84 sf2 = new ::sf2::File(riff);
85 pInstrument = GetSfInstrument(sf2, ID.Index);
86 }
87
88 instrument_info_t info;
89 for (int i = 0; i < 128; i++) { info.KeyBindings[i] = info.KeySwitchBindings[i] = 0; }
90
91 ::sf2::File* pFile = pInstrument->GetFile();
92
93 info.FormatVersion = ToString(pFile->pInfo->pVer->Major);
94 info.Product = pFile->pInfo->Product;
95 info.Artists = pFile->pInfo->Engineers;
96
97 info.InstrumentName = pInstrument->Name;
98
99 for (int i = 0; i < pInstrument->GetRegionCount(); i++) {
100 int low = pInstrument->GetRegion(i)->loKey;
101 int high = pInstrument->GetRegion(i)->hiKey;
102 if (low == ::sf2::NONE || high == ::sf2::NONE) {
103 ::sf2::Instrument* layer = pInstrument->GetRegion(i)->pInstrument;
104 for (int j = 0; j < layer->GetRegionCount(); j++) {
105 int lo = layer->GetRegion(j)->loKey;
106 int hi = layer->GetRegion(j)->hiKey;
107 SetKeyBindings(info.KeyBindings, lo, hi, ::sf2::NONE);
108 }
109 } else {
110 SetKeyBindings(info.KeyBindings, low, high, ::sf2::NONE);
111 }
112 }
113
114 if (loaded) Unlock();
115
116 if (sf2) delete sf2;
117 if (riff) delete riff;
118 return info;
119 } catch (::sf2::Exception e) {
120 if (loaded) Unlock();
121 if (sf2) delete sf2;
122 if (riff) delete riff;
123 throw InstrumentManagerException(e.Message);
124 } catch (...) {
125 if (loaded) Unlock();
126 if (sf2) delete sf2;
127 if (riff) delete riff;
128 throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'");
129 }
130 }
131
132 ::sf2::Preset* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) {
133 // get sfz file from internal sfz file manager
134 ::sf2::File* pSf2 = Sf2s.Borrow(Key.FileName, reinterpret_cast<Sf2Consumer*>(Key.Index)); // conversion kinda hackish :/
135
136 dmsg(1,("Loading sf2 instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index));
137 ::sf2::Preset* pInstrument = GetSfInstrument(pSf2, Key.Index);
138 dmsg(1,("OK\n"));
139
140 // cache initial samples points (for actually needed samples)
141 dmsg(1,("Caching initial samples..."));
142 float regTotal = 0, regCurrent = 0;
143 for (int i = 0 ; i < pInstrument->GetRegionCount() ; i++) {
144 ::sf2::Instrument* sf2Instr = pInstrument->GetRegion(i)->pInstrument;
145 if (sf2Instr) regTotal += sf2Instr->GetRegionCount();
146 }
147 uint maxSamplesPerCycle = GetMaxSamplesPerCycle(pConsumer);
148 for (int i = 0 ; i < pInstrument->GetRegionCount() ; i++) {
149 ::sf2::Instrument* sf2Instr = pInstrument->GetRegion(i)->pInstrument;
150 if (sf2Instr) {
151 // pInstrument is ::sf2::Preset
152 for (int j = 0 ; j < sf2Instr->GetRegionCount() ; j++) {
153 float localProgress = regCurrent++ / regTotal;
154 DispatchResourceProgressEvent(Key, localProgress);
155 CacheInitialSamples(sf2Instr->GetRegion(j)->GetSample(), maxSamplesPerCycle);
156 }
157 }
158 }
159 dmsg(1,("OK\n"));
160 DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100%
161
162 // we need the following for destruction later
163 instr_entry_t* pEntry = new instr_entry_t;
164 pEntry->ID.FileName = Key.FileName;
165 pEntry->ID.Index = Key.Index;
166 pEntry->pFile = pSf2;
167
168 // and we save this to check if we need to reallocate for an engine with higher value of 'MaxSamplesPerSecond'
169 pEntry->MaxSamplesPerCycle = maxSamplesPerCycle;
170
171 pArg = pEntry;
172
173 return pInstrument;
174 }
175
176 void InstrumentResourceManager::Destroy(::sf2::Preset* pResource, void* pArg) {
177 instr_entry_t* pEntry = (instr_entry_t*) pArg;
178 // we don't need the .sf2 file here anymore
179 Sf2s.HandBack(pEntry->pFile, reinterpret_cast<Sf2Consumer*>(pEntry->ID.Index)); // conversion kinda hackish :/
180 delete pEntry;
181 }
182
183 void InstrumentResourceManager::DeleteRegionIfNotUsed(::sf2::Region* pRegion, region_info_t* pRegInfo) {
184 // TODO: we could delete Region and Instrument here if they have become unused
185 }
186
187 void InstrumentResourceManager::DeleteSampleIfNotUsed(::sf2::Sample* pSample, region_info_t* pRegInfo) {
188 ::sf2::File* sf2 = pRegInfo->file;
189 ::RIFF::File* riff = static_cast< ::RIFF::File*>(pRegInfo->pArg);
190 if (sf2) {
191 sf2->DeleteSample(pSample);
192 if (!sf2->HasSamples()) {
193 dmsg(2,("No more samples in use - freeing sf2\n"));
194 delete sf2;
195 delete riff;
196 }
197 }
198 }
199
200
201
202 // internal sfz file manager
203
204 ::sf2::File* InstrumentResourceManager::Sf2ResourceManager::Create(String Key, Sf2Consumer* pConsumer, void*& pArg) {
205 dmsg(1,("Loading sf2 file \'%s\'...", Key.c_str()));
206 ::RIFF::File* pRIFF = new ::RIFF::File(Key);
207 ::sf2::File* pSf2 = new ::sf2::File(pRIFF);
208 pArg = pRIFF;
209 dmsg(1,("OK\n"));
210 return pSf2;
211 }
212
213 void InstrumentResourceManager::Sf2ResourceManager::Destroy(::sf2::File* pResource, void* pArg) {
214 dmsg(1,("Freeing sf2 file from memory..."));
215
216 // Delete as much as possible of the sf2 file. Some of the
217 // regions and samples may still be in use - these
218 // will be deleted later by the HandBackRegion function.
219 bool deleteFile = true;
220
221 for (int i = pResource->GetInstrumentCount() - 1; i >= 0; i--) {
222 ::sf2::Instrument* pInstr = pResource->GetInstrument(i);
223 bool deleteInstrument = true;
224
225 for (int j = pInstr->GetRegionCount() - 1; j >= 0 ; j--) {
226 ::sf2::Region* pRegion = pInstr->GetRegion(j);
227 std::map< ::sf2::Region*, region_info_t>::iterator iter = parent->RegionInfo.find(pRegion);
228 if (iter != parent->RegionInfo.end()) {
229 region_info_t& regInfo = (*iter).second;
230 regInfo.file = pResource;
231 deleteFile = deleteInstrument = false;
232 } else {
233 pInstr->DeleteRegion(pRegion);
234 }
235 }
236
237 if (deleteInstrument) pResource->DeleteInstrument(pInstr);
238 }
239
240 if (deleteFile) {
241 delete pResource;
242 delete (::RIFF::File*) pArg;
243 } else {
244 dmsg(2,("keeping some samples that are in use..."));
245 for (int i = pResource->GetSampleCount() - 1; i >= 0; i--) {
246 ::sf2::Sample* sample = pResource->GetSample(i);
247 if (parent->SampleRefCount.find(sample) == parent->SampleRefCount.end()) {
248 pResource->DeleteSample(sample);
249 }
250 }
251 }
252
253 dmsg(1,("OK\n"));
254 }
255
256 int InstrumentResourceManager::GetSfInstrumentCount(::sf2::File* pFile) {
257 return pFile->GetPresetCount();
258 }
259
260 ::sf2::Preset* InstrumentResourceManager::GetSfInstrument(::sf2::File* pFile, int idx) {
261 if (idx >= pFile->GetPresetCount()) {
262 throw InstrumentManagerException("There is no instrument with index " + ToString(idx));
263 }
264 return pFile->GetPreset(idx);
265 }
266
267 }} // namespace LinuxSampler::sfz

  ViewVC Help
Powered by ViewVC