1 |
/*************************************************************************** |
2 |
* * |
3 |
* LinuxSampler - modular, streaming capable sampler * |
4 |
* * |
5 |
* Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005 - 2022 Christian Schoenebeck * |
7 |
* Copyright (C) 2009 - 2012 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 |
String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) { |
39 |
Lock(); |
40 |
::sfz::Instrument* 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 |
std::vector<instrument_id_t> result; |
48 |
instrument_id_t id; |
49 |
id.FileName = File; |
50 |
id.Index = 0; |
51 |
result.push_back(id); |
52 |
return result; |
53 |
} |
54 |
|
55 |
InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) { |
56 |
if (ID.Index) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName); |
57 |
Lock(); |
58 |
::sfz::Instrument* pInstrument = Resource(ID, false); |
59 |
bool loaded = (pInstrument != NULL); |
60 |
if (!loaded) Unlock(); |
61 |
|
62 |
::sfz::File* sfz = NULL; |
63 |
try { |
64 |
if (!loaded) { |
65 |
sfz = new ::sfz::File(ID.FileName); |
66 |
pInstrument = sfz->GetInstrument(); |
67 |
} |
68 |
|
69 |
if (!pInstrument) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName); |
70 |
|
71 |
instrument_info_t info; |
72 |
info.InstrumentName = Path::getBaseName(ID.FileName); |
73 |
|
74 |
for (int i = 0; i < 128; i++) { |
75 |
info.KeyBindings[i] = pInstrument->HasKeyBinding(i); |
76 |
info.KeySwitchBindings[i] = pInstrument->HasKeySwitchBinding(i); |
77 |
} |
78 |
|
79 |
if (loaded) Unlock(); |
80 |
|
81 |
if (sfz) delete sfz; |
82 |
return info; |
83 |
} catch (::sfz::Exception e) { |
84 |
if (loaded) Unlock(); |
85 |
if (sfz) delete sfz; |
86 |
throw InstrumentManagerException(e.Message()); |
87 |
} catch (...) { |
88 |
if (loaded) Unlock(); |
89 |
if (sfz) delete sfz; |
90 |
throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'"); |
91 |
} |
92 |
} |
93 |
|
94 |
::sfz::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) { |
95 |
// get sfz file from internal sfz file manager |
96 |
::sfz::File* pSfz = Sfzs.Borrow(Key.FileName, &Sfzs); |
97 |
|
98 |
dmsg(1,("Loading sfz instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index)); |
99 |
if (Key.Index) { |
100 |
std::stringstream msg; |
101 |
msg << "There's no instrument with index " << Key.Index << "."; |
102 |
throw InstrumentManagerException(msg.str()); |
103 |
} |
104 |
::sfz::Instrument* pInstrument = pSfz->GetInstrument(); |
105 |
if (!pInstrument) { |
106 |
std::stringstream msg; |
107 |
msg << "There's no instrument with index " << Key.Index << "."; |
108 |
throw InstrumentManagerException(msg.str()); |
109 |
} |
110 |
dmsg(1,("OK\n")); |
111 |
|
112 |
// cache initial samples points (for actually needed samples) |
113 |
dmsg(1,("Caching initial samples...")); |
114 |
int regionCount = (int) pInstrument->regions.size(); |
115 |
uint maxSamplesPerCycle = GetMaxSamplesPerCycle(pConsumer); |
116 |
for (int i = 0 ; i < regionCount ; i++) { |
117 |
float localProgress = (float) i / (float) regionCount; |
118 |
DispatchResourceProgressEvent(Key, localProgress); |
119 |
CacheInitialSamples(pInstrument->regions[i]->GetSample(), maxSamplesPerCycle); |
120 |
//pInstrument->regions[i]->GetSample()->Close(); |
121 |
} |
122 |
dmsg(1,("OK\n")); |
123 |
DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100% |
124 |
|
125 |
// we need the following for destruction later |
126 |
instr_entry_t* pEntry = new instr_entry_t; |
127 |
pEntry->ID.FileName = Key.FileName; |
128 |
pEntry->ID.Index = Key.Index; |
129 |
pEntry->pFile = pSfz; |
130 |
|
131 |
// and we save this to check if we need to reallocate for an engine with higher value of 'MaxSamplesPerSecond' |
132 |
pEntry->MaxSamplesPerCycle = maxSamplesPerCycle; |
133 |
|
134 |
pArg = pEntry; |
135 |
|
136 |
return pInstrument; |
137 |
} |
138 |
|
139 |
void InstrumentResourceManager::Destroy(::sfz::Instrument* pResource, void* pArg) { |
140 |
instr_entry_t* pEntry = (instr_entry_t*) pArg; |
141 |
// we don't need the .sfz file here anymore |
142 |
Sfzs.HandBack(pEntry->pFile, reinterpret_cast<SfzConsumer*>(pEntry->ID.Index)); // conversion kinda hackish :/ |
143 |
delete pEntry; |
144 |
} |
145 |
|
146 |
void InstrumentResourceManager::DeleteRegionIfNotUsed(::sfz::Region* pRegion, region_info_t* pRegInfo) { |
147 |
::sfz::File* file = pRegInfo->file; |
148 |
if (file == NULL) return; |
149 |
|
150 |
file->GetInstrument()->DestroyRegion(pRegion); |
151 |
if (file->GetInstrument()->regions.empty()) { |
152 |
dmsg(2,("No more regions in use - freeing sfz\n")); |
153 |
delete file; |
154 |
} |
155 |
} |
156 |
|
157 |
void InstrumentResourceManager::DeleteSampleIfNotUsed(Sample* pSample, region_info_t* pRegInfo) { |
158 |
|
159 |
} |
160 |
|
161 |
|
162 |
|
163 |
// internal sfz file manager |
164 |
|
165 |
::sfz::File* InstrumentResourceManager::SfzResourceManager::Create(String Key, SfzConsumer* pConsumer, void*& pArg) { |
166 |
dmsg(1,("Loading sfz file \'%s\'...", Key.c_str())); |
167 |
::sfz::File* pSfz = new ::sfz::File(Key, &sampleManager); |
168 |
dmsg(1,("OK\n")); |
169 |
InstrumentManagerThread* thread = parent->GetInstrumentManagerThread(); |
170 |
// let this code be executed periodically by instrument manager thread |
171 |
// in order to automatically reload this .sfz file whenever it was |
172 |
// modified, e.g. by some external text editor application |
173 |
thread->AddPeriodicJob("autoreload_sfz:" + Key, [this,Key,pSfz]{ |
174 |
if (pSfz->checkFileModified()) { |
175 |
dmsg(1,("Sfz file was modified: auto reloading '%s'\n", Key.c_str())); |
176 |
// passing NULL as consumer here instead of 'this' to ensure |
177 |
// that the ResourceToBeUpdated() and ResourceUpdated() method |
178 |
// pair below is executed |
179 |
Update(pSfz, NULL); |
180 |
} |
181 |
}); |
182 |
return pSfz; |
183 |
} |
184 |
|
185 |
void InstrumentResourceManager::SfzResourceManager::Destroy(::sfz::File* pResource, void* pArg) { |
186 |
dmsg(1,("Freeing sfz file from memory...")); |
187 |
|
188 |
// stop periodic job previously scheduled above before freeing sfz file |
189 |
const String key = pResource->filename(); |
190 |
InstrumentManagerThread* thread = parent->GetInstrumentManagerThread(); |
191 |
thread->RemovePeriodicJob("autoreload_sfz:" + key); |
192 |
|
193 |
// Delete as much as possible of the sfz file. Some of the |
194 |
// regions and samples may still be in use - these |
195 |
// will be deleted later by the HandBackRegion function. |
196 |
bool deleteInstrument = true; |
197 |
::sfz::Instrument* pInstr = pResource->GetInstrument(); |
198 |
|
199 |
for (int i = (int)pInstr->regions.size() - 1; i >= 0 ; i--) { |
200 |
::sfz::Region* pRegion = pInstr->regions[i]; |
201 |
std::map< ::sfz::Region*, region_info_t>::iterator iter = parent->RegionInfo.find(pRegion); |
202 |
if (iter != parent->RegionInfo.end()) { |
203 |
region_info_t& regInfo = (*iter).second; |
204 |
regInfo.file = pResource; |
205 |
deleteInstrument = false; |
206 |
} else { |
207 |
pInstr->DestroyRegion(pRegion); |
208 |
} |
209 |
} |
210 |
|
211 |
// free sfz file |
212 |
if (deleteInstrument) delete pResource; |
213 |
else dmsg(2,("keeping some samples that are in use...")); |
214 |
|
215 |
dmsg(1,("OK\n")); |
216 |
} |
217 |
|
218 |
void InstrumentResourceManager::SfzResourceManager::ResourceToBeUpdated(::sfz::File* pResource, void*& pUpdateArg) { |
219 |
// Since the respective sfz::Instrument objects will already be |
220 |
// invalidated when ResourceUpdated() will be called subsequently, we |
221 |
// must assemble a list of the current sfz::Instrument objects being |
222 |
// subject of the .sfz file changed already here. |
223 |
std::set<::sfz::Instrument*>* pInstruments = new std::set<::sfz::Instrument*>(); |
224 |
{ |
225 |
std::vector<::sfz::Instrument*> instrumentsInUse = parent->Resources(false); |
226 |
for (size_t i = 0; i < instrumentsInUse.size(); i++) { |
227 |
::sfz::File* f = instrumentsInUse[i]->GetFile(); |
228 |
if (f == pResource) pInstruments->insert(instrumentsInUse[i]); |
229 |
} |
230 |
} |
231 |
pUpdateArg = pInstruments; |
232 |
} |
233 |
|
234 |
void InstrumentResourceManager::SfzResourceManager::ResourceUpdated(::sfz::File* pOldResource, ::sfz::File* pNewResource, void* pUpdateArg) { |
235 |
// force reload sfz::Instrument objects on engine channels that used that sfz file |
236 |
std::set<::sfz::Instrument*>* pInstruments = (std::set<::sfz::Instrument*>*)pUpdateArg; |
237 |
std::set<::sfz::Instrument*>::const_iterator it = pInstruments->begin(); |
238 |
std::set<::sfz::Instrument*>::const_iterator end = pInstruments->end(); |
239 |
for (; it != end; ++it) { |
240 |
::sfz::Instrument* instrument = *it; |
241 |
parent->Update(instrument, reinterpret_cast<SfzInstrConsumer*>(this)); |
242 |
} |
243 |
delete pInstruments; |
244 |
} |
245 |
|
246 |
}} // namespace LinuxSampler::sfz |