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 |
|
|
|
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 sfz { |
37 |
|
|
|
38 |
|
|
// data stored as long as an instrument resource exists |
39 |
|
|
struct instr_entry_t { |
40 |
|
|
InstrumentManager::instrument_id_t ID; |
41 |
|
|
::sfz::File* pSfz; |
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 |
|
|
::sfz::Instrument* 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 |
|
|
std::vector<instrument_id_t> result; |
65 |
|
|
instrument_id_t id; |
66 |
|
|
id.FileName = File; |
67 |
|
|
id.Index = 0; |
68 |
|
|
result.push_back(id); |
69 |
|
|
return result; |
70 |
|
|
} |
71 |
|
|
|
72 |
|
|
InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) { |
73 |
|
|
if (ID.Index) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName); |
74 |
|
|
Lock(); |
75 |
|
|
::sfz::Instrument* pInstrument = Resource(ID, false); |
76 |
|
|
bool loaded = (pInstrument != NULL); |
77 |
|
|
if (!loaded) Unlock(); |
78 |
|
|
|
79 |
|
|
::sfz::File* sfz = NULL; |
80 |
|
|
try { |
81 |
|
|
if(!loaded) { |
82 |
|
|
sfz = new ::sfz::File(ID.FileName); |
83 |
|
|
pInstrument = sfz->GetInstrument(); |
84 |
|
|
} |
85 |
|
|
|
86 |
|
|
if (!pInstrument) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName); |
87 |
|
|
|
88 |
|
|
instrument_info_t info; |
89 |
|
|
info.InstrumentName = Path::getBaseName(ID.FileName); |
90 |
|
|
|
91 |
|
|
for (int i = 0; i < 128; i++) { |
92 |
|
|
info.KeyBindings[i] = pInstrument->HasKeyBinding(i); |
93 |
|
|
info.KeySwitchBindings[i] = pInstrument->HasKeySwitchBinding(i); |
94 |
|
|
} |
95 |
|
|
|
96 |
|
|
if (loaded) Unlock(); |
97 |
|
|
|
98 |
|
|
if (sfz) delete sfz; |
99 |
|
|
return info; |
100 |
|
|
} catch (::sfz::Exception e) { |
101 |
|
|
if (loaded) Unlock(); |
102 |
|
|
if (sfz) delete sfz; |
103 |
|
|
throw InstrumentManagerException(e.Message()); |
104 |
|
|
} catch (...) { |
105 |
|
|
if (loaded) Unlock(); |
106 |
|
|
if (sfz) delete sfz; |
107 |
|
|
throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'"); |
108 |
|
|
} |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
::sfz::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) { |
112 |
|
|
// get sfz file from internal sfz file manager |
113 |
|
|
::sfz::File* pSfz = Sfzs.Borrow(Key.FileName, (SfzConsumer*) Key.Index); // conversion kinda hackish :/ |
114 |
|
|
|
115 |
|
|
dmsg(1,("Loading sfz instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index)); |
116 |
|
|
if (Key.Index) { |
117 |
|
|
std::stringstream msg; |
118 |
|
|
msg << "There's no instrument with index " << Key.Index << "."; |
119 |
|
|
throw InstrumentManagerException(msg.str()); |
120 |
|
|
} |
121 |
|
|
::sfz::Instrument* pInstrument = pSfz->GetInstrument(); |
122 |
|
|
if (!pInstrument) { |
123 |
|
|
std::stringstream msg; |
124 |
|
|
msg << "There's no instrument with index " << Key.Index << "."; |
125 |
|
|
throw InstrumentManagerException(msg.str()); |
126 |
|
|
} |
127 |
|
|
dmsg(1,("OK\n")); |
128 |
|
|
|
129 |
|
|
// cache initial samples points (for actually needed samples) |
130 |
|
|
dmsg(1,("Caching initial samples...")); |
131 |
|
|
int regionCount = pInstrument->regions.size(); |
132 |
|
|
for(int i = 0 ; i < regionCount; i++) { |
133 |
|
|
const float localProgress = (float) i / (float) regionCount; |
134 |
|
|
DispatchResourceProgressEvent(Key, localProgress); |
135 |
|
|
CacheInitialSamples(pInstrument->regions[i]->GetSample(), dynamic_cast<AbstractEngineChannel*>(pConsumer)); |
136 |
|
|
//pInstrument->regions[i]->GetSample()->Close(); |
137 |
|
|
} |
138 |
|
|
dmsg(1,("OK\n")); |
139 |
|
|
DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100% |
140 |
|
|
|
141 |
|
|
// we need the following for destruction later |
142 |
|
|
instr_entry_t* pEntry = new instr_entry_t; |
143 |
|
|
pEntry->ID.FileName = Key.FileName; |
144 |
|
|
pEntry->ID.Index = Key.Index; |
145 |
|
|
pEntry->pSfz = pSfz; |
146 |
|
|
|
147 |
|
|
EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(pConsumer); |
148 |
|
|
// and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond' |
149 |
|
|
pEntry->MaxSamplesPerCycle = |
150 |
|
|
(!pEngineChannel) ? 0 /* don't care for instrument editors */ : |
151 |
|
|
(pEngineChannel->GetEngine()) ? |
152 |
|
|
dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle() |
153 |
|
|
: GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE; |
154 |
|
|
pArg = pEntry; |
155 |
|
|
|
156 |
|
|
return pInstrument; |
157 |
|
|
} |
158 |
|
|
|
159 |
|
|
void InstrumentResourceManager::Destroy( ::sfz::Instrument* pResource, void* pArg) { |
160 |
|
|
instr_entry_t* pEntry = (instr_entry_t*) pArg; |
161 |
|
|
// we don't need the .sfz file here anymore |
162 |
|
|
Sfzs.HandBack(pEntry->pSfz, (SfzConsumer*) pEntry->ID.Index); // conversion kinda hackish :/ |
163 |
|
|
delete pEntry; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
void InstrumentResourceManager::OnBorrow(::sfz::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) { |
167 |
|
|
instr_entry_t* pEntry = (instr_entry_t*) pArg; |
168 |
|
|
EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(pConsumer); |
169 |
|
|
uint maxSamplesPerCycle = |
170 |
|
|
(pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle() |
171 |
|
|
: GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE; |
172 |
|
|
if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) { |
173 |
|
|
Update(pResource, pConsumer); |
174 |
|
|
} |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
void InstrumentResourceManager::DeleteRegionIfNotUsed(::sfz::Region* pRegion, region_info_t* pRegInfo) { |
178 |
|
|
::sfz::File* file = pRegInfo->file; |
179 |
|
|
if (file == NULL) return; |
180 |
|
|
|
181 |
|
|
file->GetInstrument()->DestroyRegion(pRegion); |
182 |
|
|
if(file->GetInstrument()->regions.empty()) { |
183 |
|
|
dmsg(2,("No more regions in use - freeing sfz\n")); |
184 |
|
|
delete file; |
185 |
|
|
} |
186 |
|
|
} |
187 |
|
|
|
188 |
|
|
void InstrumentResourceManager::DeleteSampleIfNotUsed(Sample* pSample, region_info_t* pRegInfo) { |
189 |
|
|
|
190 |
|
|
} |
191 |
|
|
|
192 |
|
|
|
193 |
|
|
|
194 |
|
|
// internal sfz file manager |
195 |
|
|
|
196 |
|
|
::sfz::File* InstrumentResourceManager::SfzResourceManager::Create(String Key, SfzConsumer* pConsumer, void*& pArg) { |
197 |
|
|
dmsg(1,("Loading sfz file \'%s\'...", Key.c_str())); |
198 |
|
|
::sfz::File* pSfz = new ::sfz::File(Key, &sampleManager); |
199 |
|
|
dmsg(1,("OK\n")); |
200 |
|
|
return pSfz; |
201 |
|
|
} |
202 |
|
|
|
203 |
|
|
void InstrumentResourceManager::SfzResourceManager::Destroy(::sfz::File* pResource, void* pArg) { |
204 |
|
|
dmsg(1,("Freeing sfz file from memory...")); |
205 |
|
|
|
206 |
|
|
// Delete as much as possible of the sfz file. Some of the |
207 |
|
|
// regions and samples may still be in use - these |
208 |
|
|
// will be deleted later by the HandBackRegion function. |
209 |
|
|
bool deleteInstrument = true; |
210 |
|
|
::sfz::Instrument* pInstr = pResource->GetInstrument(); |
211 |
|
|
|
212 |
|
|
for (int i = pInstr->regions.size() - 1; i >= 0 ; i--) { |
213 |
|
|
::sfz::Region* pRegion = pInstr->regions[i]; |
214 |
|
|
std::map< ::sfz::Region*, region_info_t>::iterator iter = parent->RegionInfo.find(pRegion); |
215 |
|
|
if (iter != parent->RegionInfo.end()) { |
216 |
|
|
region_info_t& regInfo = (*iter).second; |
217 |
|
|
regInfo.file = pResource; |
218 |
|
|
deleteInstrument = false; |
219 |
|
|
} else { |
220 |
|
|
SampleFile* sf = pRegion->GetSample(false); |
221 |
|
|
if (sf != NULL) pInstr->GetSampleManager()->RemoveSampleConsumer(sf, pRegion); |
222 |
|
|
if (sf == NULL || !pInstr->GetSampleManager()->HasSampleConsumers(sf)) pInstr->DestroyRegion(pRegion); |
223 |
|
|
} |
224 |
|
|
} |
225 |
|
|
|
226 |
|
|
if(deleteInstrument) delete pResource; |
227 |
|
|
else dmsg(2,("keeping some samples that are in use...")); |
228 |
|
|
|
229 |
|
|
dmsg(1,("OK\n")); |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
}} // namespace LinuxSampler::sfz |