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

Contents of /linuxsampler/trunk/src/engines/sfz/InstrumentResourceManager.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4019 - (show annotations) (download)
Mon Jan 3 17:45:26 2022 UTC (2 years, 3 months ago) by schoenebeck
File size: 11237 byte(s)
* SFZ engine: Automatically reload .sfz files on file changes
  (e.g. by some external text editor).

* Bumped version (2.2.0.svn14).

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

  ViewVC Help
Powered by ViewVC