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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2012 - (hide annotations) (download)
Fri Oct 23 17:53:17 2009 UTC (14 years, 7 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 iliev 2012 /***************************************************************************
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